1 | unit BGRAReadLzp;
|
---|
2 |
|
---|
3 | {$mode objfpc}{$H+}
|
---|
4 |
|
---|
5 | interface
|
---|
6 |
|
---|
7 | uses
|
---|
8 | Classes, SysUtils, FPimage, BGRALzpCommon, BGRABitmapTypes, BGRABitmap;
|
---|
9 |
|
---|
10 | type
|
---|
11 |
|
---|
12 | { TBGRAReaderLazPaint }
|
---|
13 |
|
---|
14 | TBGRAReaderLazPaint = class(TFPCustomImageReader)
|
---|
15 | private
|
---|
16 | FHeight: integer;
|
---|
17 | FNbLayers: integer;
|
---|
18 | FWidth: integer;
|
---|
19 | FCaption: string;
|
---|
20 | FDimensionsAlreadyFetched: boolean;
|
---|
21 | protected
|
---|
22 | procedure InternalRead(Str: TStream; Img: TFPCustomImage); override;
|
---|
23 | procedure InternalReadLayers({%H-}str: TStream;{%H-}Img: TFPCustomImage); virtual;
|
---|
24 | procedure InternalReadCompressableBitmap(str: TStream; Img: TFPCustomImage); virtual;
|
---|
25 | function InternalCheck(Str: TStream): boolean; override;
|
---|
26 | public
|
---|
27 | WantThumbnail: boolean;
|
---|
28 | class procedure LoadRLEImage(Str: TStream; Img: TFPCustomImage; out ACaption: string);
|
---|
29 | property Width: integer read FWidth;
|
---|
30 | property Height: integer read FHeight;
|
---|
31 | property NbLayers: integer read FNbLayers;
|
---|
32 | property Caption: string read FCaption;
|
---|
33 | end;
|
---|
34 |
|
---|
35 | implementation
|
---|
36 |
|
---|
37 | uses BGRACompressableBitmap, BGRAReadPng;
|
---|
38 |
|
---|
39 | { TBGRAReaderLazPaint }
|
---|
40 |
|
---|
41 | procedure TBGRAReaderLazPaint.InternalRead(Str: TStream; Img: TFPCustomImage);
|
---|
42 | var
|
---|
43 | {%H-}header: TLazPaintImageHeader;
|
---|
44 | oldPos: int64;
|
---|
45 | png: TBGRAReaderPNG;
|
---|
46 |
|
---|
47 | begin
|
---|
48 | FCaption := '';
|
---|
49 | FWidth:= 0;
|
---|
50 | FHeight:= 0;
|
---|
51 | FNbLayers:= 0;
|
---|
52 | FDimensionsAlreadyFetched:= false;
|
---|
53 | oldPos := str.Position;
|
---|
54 | str.ReadBuffer({%H-}header.magic,sizeof(header.magic));
|
---|
55 | if header.magic = LAZPAINT_MAGIC_HEADER then
|
---|
56 | begin
|
---|
57 | str.ReadBuffer(header.zero1, sizeof(header)-sizeof(header.magic));
|
---|
58 | LazPaintImageHeader_SwapEndianIfNeeded(header);
|
---|
59 | if (header.zero1 <> 0) or (header.zero2 <> 0) or
|
---|
60 | (header.headerSize < $30) then raise exception.Create('Invalid file format');
|
---|
61 | FWidth:= header.width;
|
---|
62 | FHeight:= header.height;
|
---|
63 | FNbLayers:= header.nbLayers;
|
---|
64 | FDimensionsAlreadyFetched:= true;
|
---|
65 |
|
---|
66 | if WantThumbnail and ((header.compressionMode and LAZPAINT_THUMBNAIL_PNG) <> 0) then
|
---|
67 | begin
|
---|
68 | str.Position:= oldPos+header.headerSize;
|
---|
69 | png := TBGRAReaderPNG.create;
|
---|
70 | try
|
---|
71 | png.ImageRead(Str,Img);
|
---|
72 | except
|
---|
73 | png.Free;
|
---|
74 | raise exception.Create('Invalid file format');
|
---|
75 | end;
|
---|
76 | png.free;
|
---|
77 | exit;
|
---|
78 | end;
|
---|
79 |
|
---|
80 | if ((header.compressionMode and LAZPAINT_COMPRESSION_MASK) <> LAZPAINT_COMPRESSION_MODE_ZSTREAM) and
|
---|
81 | ((header.compressionMode and LAZPAINT_COMPRESSION_MASK) <> LAZPAINT_COMPRESSION_MODE_RLE) then raise exception.Create('Compression mode not supported');
|
---|
82 |
|
---|
83 | str.Position:= oldPos+header.previewOffset;
|
---|
84 | if (header.compressionMode and LAZPAINT_COMPRESSION_MASK) = LAZPAINT_COMPRESSION_MODE_RLE then
|
---|
85 | LoadRLEImage(Str, Img, FCaption)
|
---|
86 | else
|
---|
87 | InternalReadCompressableBitmap(str,Img);
|
---|
88 |
|
---|
89 | if header.layersOffset > 0 then
|
---|
90 | begin
|
---|
91 | Str.Position:= oldPos+header.layersOffset;
|
---|
92 | InternalReadLayers(Str,Img);
|
---|
93 | end;
|
---|
94 | end else
|
---|
95 | begin
|
---|
96 | str.Position:= oldPos;
|
---|
97 | InternalReadCompressableBitmap(str,Img);
|
---|
98 | if (Str.Position < Str.Size) and (FCaption = 'Preview') then InternalReadLayers(Str,Img);
|
---|
99 | end;
|
---|
100 | end;
|
---|
101 |
|
---|
102 | procedure TBGRAReaderLazPaint.InternalReadLayers(str: TStream;
|
---|
103 | Img: TFPCustomImage);
|
---|
104 | begin
|
---|
105 | //not implemented here
|
---|
106 | end;
|
---|
107 |
|
---|
108 | procedure TBGRAReaderLazPaint.InternalReadCompressableBitmap(str: TStream;
|
---|
109 | Img: TFPCustomImage);
|
---|
110 | var
|
---|
111 | compressed: TBGRACompressableBitmap;
|
---|
112 | bmp: TBGRABitmap;
|
---|
113 | begin
|
---|
114 | compressed := TBGRACompressableBitmap.Create;
|
---|
115 | try
|
---|
116 | compressed.ReadFromStream(Str);
|
---|
117 | bmp := compressed.GetBitmap;
|
---|
118 | try
|
---|
119 | FCaption := compressed.Caption;
|
---|
120 | if (Img is TBGRACustomBitmap) then
|
---|
121 | TBGRACustomBitmap(Img).Assign(bmp)
|
---|
122 | else
|
---|
123 | Img.Assign(bmp);
|
---|
124 | if not FDimensionsAlreadyFetched then
|
---|
125 | begin
|
---|
126 | FDimensionsAlreadyFetched := true;
|
---|
127 | FWidth:= bmp.width;
|
---|
128 | FHeight:= bmp.height;
|
---|
129 | FNbLayers:= 1;
|
---|
130 | end;
|
---|
131 | finally
|
---|
132 | bmp.Free;
|
---|
133 | end;
|
---|
134 | finally
|
---|
135 | compressed.Free;
|
---|
136 | end;
|
---|
137 | end;
|
---|
138 |
|
---|
139 | function TBGRAReaderLazPaint.InternalCheck(Str: TStream): boolean;
|
---|
140 | var {%H-}magic: packed array[0..7] of byte;
|
---|
141 | magicAsText: string;
|
---|
142 | oldPos: int64;
|
---|
143 | begin
|
---|
144 | oldPos := str.Position;
|
---|
145 | result := (str.Read({%H-}magic,sizeof(magic)) = sizeof(magic));
|
---|
146 | str.Position:= oldPos;
|
---|
147 | setlength(magicAsText, sizeof(magic));
|
---|
148 | move(magic[0], magicAsText[1], sizeof(magic));
|
---|
149 | result := (copy(magicAsText,1,8) = 'LazPaint') or
|
---|
150 | (((magic[0] <> 0) or (magic[1] <> 0)) and (magic[2] = 0) and (magic[3] = 0) and
|
---|
151 | ((magic[4] <> 0) or (magic[5] <> 0)) and (magic[6] = 0) and (magic[7] = 0));
|
---|
152 | end;
|
---|
153 |
|
---|
154 | class procedure TBGRAReaderLazPaint.LoadRLEImage(Str: TStream; Img: TFPCustomImage; out ACaption: string);
|
---|
155 | var channelFlags: byte;
|
---|
156 | w,h,NbPixels,nameLen,channelStreamSize: DWord;
|
---|
157 | nextPosition: int64;
|
---|
158 | PIndexed,PRed,PGreen,PBlue,PAlpha,
|
---|
159 | PCurRed, PCurGreen, PCurBlue, PCurAlpha: PByte;
|
---|
160 | PDest: PBGRAPixel;
|
---|
161 | x,y: DWord;
|
---|
162 | c: TFPColor;
|
---|
163 | n,NbNonTransp: DWord;
|
---|
164 | a,index: NativeInt;
|
---|
165 | ColorTab: packed array[0..256*3-1] of byte;
|
---|
166 | begin
|
---|
167 | w := LEtoN(str.ReadDWord);
|
---|
168 | h := LEtoN(str.ReadDWord);
|
---|
169 | nameLen := LEtoN(str.ReadDWord);
|
---|
170 | setlength(ACaption, nameLen);
|
---|
171 | {$PUSH}{$RANGECHECKS OFF}
|
---|
172 | str.ReadBuffer(ACaption[1], nameLen);
|
---|
173 | {$POP}
|
---|
174 | channelFlags := str.ReadByte;
|
---|
175 | NbPixels := w*h;
|
---|
176 |
|
---|
177 | PRed := nil;
|
---|
178 | PGreen := nil;
|
---|
179 | PBlue := nil;
|
---|
180 | PAlpha := nil;
|
---|
181 |
|
---|
182 | try
|
---|
183 | if (channelFlags and LazpaintChannelNoAlpha) = 0 then
|
---|
184 | begin
|
---|
185 | Getmem(PAlpha, NbPixels);
|
---|
186 | channelStreamSize := LEtoN(str.ReadDWord);
|
---|
187 | nextPosition:= str.Position+channelStreamSize;
|
---|
188 | if (channelStreamSize > 0) and (NbPixels > 0) then DecodeLazRLE(Str, PAlpha^, NbPixels);
|
---|
189 | Str.Position:= nextPosition;
|
---|
190 |
|
---|
191 | NbNonTransp := 0;
|
---|
192 | PCurAlpha := PAlpha;
|
---|
193 | for n := NbPixels-1 downto 0 do
|
---|
194 | begin
|
---|
195 | if PCurAlpha^ <> 0 then inc(NbNonTransp);
|
---|
196 | inc(PCurAlpha);
|
---|
197 | end;
|
---|
198 | end else
|
---|
199 | NbNonTransp:= NbPixels;
|
---|
200 |
|
---|
201 | if NbNonTransp > 0 then
|
---|
202 | begin
|
---|
203 | if (channelFlags and LazpaintPalettedRGB) <> 0 then
|
---|
204 | begin
|
---|
205 | Getmem(PIndexed, NbNonTransp);
|
---|
206 | try
|
---|
207 | Getmem(PRed, NbNonTransp);
|
---|
208 | Getmem(PGreen, NbNonTransp);
|
---|
209 | Getmem(PBlue, NbNonTransp);
|
---|
210 | fillchar({%H-}ColorTab,sizeof(ColorTab),0);
|
---|
211 |
|
---|
212 | channelStreamSize := LEtoN(str.ReadDWord);
|
---|
213 | nextPosition:= str.Position+channelStreamSize;
|
---|
214 | DecodeLazRLE(Str, colorTab[0], 256);
|
---|
215 | Str.Position:= nextPosition;
|
---|
216 |
|
---|
217 | if (channelFlags and LazPaintChannelGreenFromRed) <> 0 then
|
---|
218 | move(ColorTab[0],colorTab[256], 256)
|
---|
219 | else
|
---|
220 | begin
|
---|
221 | channelStreamSize := LEtoN(str.ReadDWord);
|
---|
222 | nextPosition:= str.Position+channelStreamSize;
|
---|
223 | DecodeLazRLE(Str, colorTab[256], 256);
|
---|
224 | Str.Position:= nextPosition;
|
---|
225 | end;
|
---|
226 | if (channelFlags and LazPaintChannelBlueFromRed) <> 0 then
|
---|
227 | move(ColorTab[0],colorTab[512], 256)
|
---|
228 | else if (channelFlags and LazpaintChannelBlueFromGreen) <> 0 then
|
---|
229 | move(ColorTab[256],colorTab[512], 256)
|
---|
230 | else
|
---|
231 | begin
|
---|
232 | channelStreamSize := LEtoN(str.ReadDWord);
|
---|
233 | nextPosition:= str.Position+channelStreamSize;
|
---|
234 | DecodeLazRLE(Str, colorTab[512], 256);
|
---|
235 | Str.Position:= nextPosition;
|
---|
236 | end;
|
---|
237 |
|
---|
238 | channelStreamSize := LEtoN(str.ReadDWord);
|
---|
239 | nextPosition:= str.Position+channelStreamSize;
|
---|
240 | DecodeLazRLE(Str, PIndexed^, NbNonTransp);
|
---|
241 | Str.Position:= nextPosition;
|
---|
242 |
|
---|
243 | for n := 0 to NbNonTransp-1 do
|
---|
244 | begin
|
---|
245 | index := (PIndexed+n)^;
|
---|
246 | (PRed+n)^ := colorTab[index];
|
---|
247 | (PGreen+n)^ := colorTab[index+256];
|
---|
248 | (PBlue+n)^ := colorTab[index+512];
|
---|
249 | end;
|
---|
250 | finally
|
---|
251 | FreeMem(PIndexed);
|
---|
252 | end;
|
---|
253 | end else
|
---|
254 | begin
|
---|
255 | Getmem(PRed, NbNonTransp);
|
---|
256 | channelStreamSize := LEtoN(str.ReadDWord);
|
---|
257 | nextPosition:= str.Position+channelStreamSize;
|
---|
258 | DecodeLazRLE(Str, PRed^, NbNonTransp);
|
---|
259 | Str.Position:= nextPosition;
|
---|
260 |
|
---|
261 | if (channelFlags and LazPaintChannelGreenFromRed) <> 0 then PGreen := PRed else
|
---|
262 | begin
|
---|
263 | Getmem(PGreen, NbNonTransp);
|
---|
264 | channelStreamSize := LEtoN(str.ReadDWord);
|
---|
265 | nextPosition:= str.Position+channelStreamSize;
|
---|
266 | DecodeLazRLE(Str, PGreen^, NbNonTransp);
|
---|
267 | Str.Position:= nextPosition;
|
---|
268 | end;
|
---|
269 |
|
---|
270 | if (channelFlags and LazPaintChannelBlueFromRed) <> 0 then PBlue := PRed else
|
---|
271 | if (channelFlags and LazPaintChannelBlueFromGreen) <> 0 then PBlue := PGreen else
|
---|
272 | begin
|
---|
273 | Getmem(PBlue, NbNonTransp);
|
---|
274 | channelStreamSize := LEtoN(str.ReadDWord);
|
---|
275 | nextPosition:= str.Position+channelStreamSize;
|
---|
276 | DecodeLazRLE(Str, PBlue^, NbNonTransp);
|
---|
277 | Str.Position:= nextPosition;
|
---|
278 | end;
|
---|
279 | end;
|
---|
280 | end;
|
---|
281 |
|
---|
282 | Img.SetSize(w,h);
|
---|
283 |
|
---|
284 | if NbNonTransp > 0 then
|
---|
285 | begin
|
---|
286 | PCurRed := PRed;
|
---|
287 | PCurGreen := PGreen;
|
---|
288 | PCurBlue := PBlue;
|
---|
289 | PCurAlpha := PAlpha;
|
---|
290 |
|
---|
291 | if Img is TBGRACustomBitmap then
|
---|
292 | begin
|
---|
293 | If PCurAlpha = nil then
|
---|
294 | begin
|
---|
295 | for y := 0 to h-1 do
|
---|
296 | begin
|
---|
297 | PDest := TBGRACustomBitmap(Img).ScanLine[y];
|
---|
298 | for x := w-1 downto 0 do
|
---|
299 | begin
|
---|
300 | PDest^ := BGRA(PCurRed^,PCurGreen^,PCurBlue^);
|
---|
301 | inc(PCurBlue);
|
---|
302 | inc(PCurGreen);
|
---|
303 | inc(PCurRed);
|
---|
304 | inc(PDest);
|
---|
305 | end;
|
---|
306 | end;
|
---|
307 | end else
|
---|
308 | for y := 0 to h-1 do
|
---|
309 | begin
|
---|
310 | PDest := TBGRACustomBitmap(Img).ScanLine[y];
|
---|
311 | for x := w-1 downto 0 do
|
---|
312 | begin
|
---|
313 | if PCurAlpha^ = 0 then
|
---|
314 | PDest^ := BGRAPixelTransparent
|
---|
315 | else
|
---|
316 | begin
|
---|
317 | PDest^ := BGRA(PCurRed^,PCurGreen^,PCurBlue^,PCurAlpha^);
|
---|
318 | inc(PCurBlue);
|
---|
319 | inc(PCurGreen);
|
---|
320 | inc(PCurRed);
|
---|
321 | end;
|
---|
322 | inc(PDest);
|
---|
323 | inc(PCurAlpha);
|
---|
324 | end;
|
---|
325 | end;
|
---|
326 | end else
|
---|
327 | begin
|
---|
328 | a := 255;
|
---|
329 | for y := 0 to h-1 do
|
---|
330 | for x := 0 to w-1 do
|
---|
331 | begin
|
---|
332 | if PCurAlpha <> nil then
|
---|
333 | begin
|
---|
334 | a := PCurAlpha^;
|
---|
335 | inc(PCurAlpha);
|
---|
336 | end;
|
---|
337 | if a = 0 then
|
---|
338 | begin
|
---|
339 | img.Colors[x,y] := colTransparent;
|
---|
340 | end else
|
---|
341 | begin
|
---|
342 | c.red := PCurRed^ + (PCurRed^ shl 8);
|
---|
343 | c.green := PCurGreen^ + (PCurGreen^ shl 8);
|
---|
344 | c.blue := PCurBlue^ + (PCurBlue^ shl 8);
|
---|
345 | c.alpha := a + (a shl 8);
|
---|
346 | Img.Colors[x,y] := c;
|
---|
347 | inc(PCurBlue);
|
---|
348 | inc(PCurGreen);
|
---|
349 | inc(PCurRed);
|
---|
350 | end;
|
---|
351 | end;
|
---|
352 | end;
|
---|
353 | end else
|
---|
354 | begin
|
---|
355 | if Img is TBGRACustomBitmap then
|
---|
356 | TBGRACustomBitmap(Img).FillTransparent else
|
---|
357 | begin
|
---|
358 | for y := 0 to h-1 do
|
---|
359 | for x := 0 to w-1 do
|
---|
360 | img.Colors[x,y] := colTransparent;
|
---|
361 | end;
|
---|
362 | end;
|
---|
363 | finally
|
---|
364 | If Assigned(PAlpha) then FreeMem(PAlpha);
|
---|
365 | if Assigned(PBlue) and (PBlue <> PGreen) and (PBlue <> PRed) then FreeMem(PBlue);
|
---|
366 | if Assigned(PGreen) and (PGreen <> PRed) then FreeMem(PGreen);
|
---|
367 | If Assigned(PRed) then FreeMem(PRed);
|
---|
368 | end;
|
---|
369 | end;
|
---|
370 |
|
---|
371 | initialization
|
---|
372 |
|
---|
373 | if DefaultBGRAImageReader[ifLazPaint] = nil then
|
---|
374 | DefaultBGRAImageReader[ifLazPaint] := TBGRAReaderLazPaint;
|
---|
375 |
|
---|
376 | end.
|
---|