source: trunk/Packages/bgrabitmap/bgrawritelzp.pas

Last change on this file was 2, checked in by chronos, 5 years ago
File size: 11.8 KB
Line 
1unit BGRAWriteLzp;
2
3{$mode objfpc}{$H+}
4
5interface
6
7uses
8 Classes, SysUtils, FPimage, BGRALzpCommon, BGRABitmapTypes, BGRABitmap;
9
10type
11 { TBGRAWriterLazPaint }
12
13 TBGRAWriterLazPaint = class(TFPCustomImageWriter)
14 private
15 function GetCompression: TLzpCompression;
16 function GetIncludeThumbnail: boolean;
17 procedure SetCompression(AValue: TLzpCompression);
18 procedure SetIncludeThumbnail(AValue: boolean);
19 function WriteThumbnail(Str: TStream; Img: TFPCustomImage): boolean;
20 protected
21 CompressionMode: DWord;
22 procedure InternalWrite(Str: TStream; Img: TFPCustomImage); override;
23 function InternalWriteLayers({%H-}Str: TStream; {%H-}Img: TFPCustomImage): boolean; virtual;
24 function GetNbLayers: integer; virtual;
25 public
26 Caption: string;
27 constructor Create; override;
28 class procedure WriteRLEImage(Str: TStream; Img: TFPCustomImage; ACaption: string= '');
29 property Compression: TLzpCompression read GetCompression write SetCompression;
30 property IncludeThumbnail: boolean read GetIncludeThumbnail write SetIncludeThumbnail;
31 end;
32
33implementation
34
35uses BGRACompressableBitmap;
36
37{ TBGRAWriterLazPaint }
38
39function TBGRAWriterLazPaint.WriteThumbnail(Str: TStream; Img: TFPCustomImage): boolean;
40var w,h: integer;
41 thumbStream: TStream;
42 OldResampleFilter: TResampleFilter;
43 thumbnail: TBGRACustomBitmap;
44begin
45 result := false;
46 if not (Img is TBGRACustomBitmap) then exit;
47 if (Img.Width > LazpaintThumbMaxWidth) or
48 (Img.Height > LazpaintThumbMaxHeight) then
49 begin
50 if Img.Width > LazpaintThumbMaxWidth then
51 begin
52 w := LazpaintThumbMaxWidth;
53 h := round(Img.Height* (w/Img.Width));
54 end else
55 begin
56 w := Img.Width;
57 h := Img.Height;
58 end;
59 if h > LazpaintThumbMaxHeight then
60 begin
61 h := LazpaintThumbMaxHeight;
62 w := round(Img.Width* (h/Img.Height));
63 end;
64 OldResampleFilter:= TBGRACustomBitmap(Img).ResampleFilter;
65 TBGRACustomBitmap(Img).ResampleFilter:= rfMitchell;
66 thumbnail := TBGRACustomBitmap(Img).Resample(w,h,rmFineResample);
67 TBGRACustomBitmap(Img).ResampleFilter := OldResampleFilter;
68
69 try
70 thumbStream := TMemoryStream.Create;
71 try
72 thumbnail.SaveToStreamAsPng(thumbStream);
73 thumbStream.Position:= 0;
74 Str.CopyFrom(thumbStream, thumbStream.Size);
75 result := true;
76 finally
77 thumbStream.Free;
78 end;
79 finally
80 thumbnail.Free;
81 end;
82 end else
83 begin
84 thumbStream := TMemoryStream.Create;
85 try
86 TBGRACustomBitmap(Img).SaveToStreamAsPng(thumbStream);
87 thumbStream.Position:= 0;
88 Str.CopyFrom(thumbStream, thumbStream.Size);
89 result := true;
90 finally
91 thumbStream.Free;
92 end;
93 end;
94end;
95
96function TBGRAWriterLazPaint.GetCompression: TLzpCompression;
97begin
98 if (CompressionMode and LAZPAINT_COMPRESSION_MASK) = LAZPAINT_COMPRESSION_MODE_ZSTREAM then
99 result := lzpZStream
100 else
101 result := lzpRLE;
102end;
103
104function TBGRAWriterLazPaint.GetIncludeThumbnail: boolean;
105begin
106 result := (CompressionMode and LAZPAINT_THUMBNAIL_PNG) <> 0;
107end;
108
109procedure TBGRAWriterLazPaint.SetCompression(AValue: TLzpCompression);
110begin
111 if AValue = lzpZStream then
112 CompressionMode := (CompressionMode and not LAZPAINT_COMPRESSION_MASK) or LAZPAINT_COMPRESSION_MODE_ZSTREAM
113 else
114 CompressionMode := (CompressionMode and not LAZPAINT_COMPRESSION_MASK) or LAZPAINT_COMPRESSION_MODE_RLE;
115end;
116
117procedure TBGRAWriterLazPaint.SetIncludeThumbnail(AValue: boolean);
118begin
119 if AValue then
120 CompressionMode := CompressionMode or LAZPAINT_THUMBNAIL_PNG else
121 CompressionMode := CompressionMode and not LAZPAINT_THUMBNAIL_PNG;
122end;
123
124procedure TBGRAWriterLazPaint.InternalWrite(Str: TStream; Img: TFPCustomImage);
125var {%H-}header: TLazPaintImageHeader;
126 compBmp: TBGRACompressableBitmap;
127 startPos, endPos: int64;
128begin
129 startPos := str.Position;
130 fillchar({%H-}header,sizeof(header),0);
131 header.magic := LAZPAINT_MAGIC_HEADER;
132 header.zero1 := 0;
133 header.headerSize:= sizeof(header);
134 header.width := Img.Width;
135 header.height := img.Height;
136 header.nbLayers:= GetNbLayers;
137 header.previewOffset:= 0;
138 header.zero2 := 0;
139 header.compressionMode:= CompressionMode;
140 header.reserved1:= 0;
141 header.layersOffset:= 0;
142 LazPaintImageHeader_SwapEndianIfNeeded(header);
143 str.WriteBuffer(header,sizeof(header));
144 LazPaintImageHeader_SwapEndianIfNeeded(header);
145
146 if IncludeThumbnail then
147 if not WriteThumbnail(Str, Img) then
148 begin
149 IncludeThumbnail := false;
150 header.compressionMode:= CompressionMode; //update field for thumbnail
151 end;
152
153 header.previewOffset:= Str.Position - startPos;
154 if Compression = lzpRLE then
155 WriteRLEImage(Str, Img, Caption)
156 else
157 begin
158 compBmp := TBGRACompressableBitmap.Create(Img as TBGRABitmap);
159 compBmp.Caption := Caption;
160 compBmp.WriteToStream(Str);
161 compBmp.Free;
162 end;
163
164 endPos := str.Position;
165 if InternalWriteLayers(Str, Img) then
166 begin
167 header.layersOffset := endPos - startPos;
168 endPos := str.Position;
169 end;
170
171 str.Position:= startPos;
172 LazPaintImageHeader_SwapEndianIfNeeded(header);
173 str.WriteBuffer(header,sizeof(header));
174 str.Position:= endPos;
175end;
176
177function TBGRAWriterLazPaint.InternalWriteLayers(Str: TStream;
178 Img: TFPCustomImage): boolean;
179begin
180 result := false;
181end;
182
183function TBGRAWriterLazPaint.GetNbLayers: integer;
184begin
185 result := 1;
186end;
187
188constructor TBGRAWriterLazPaint.Create;
189begin
190 inherited Create;
191 CompressionMode:= LAZPAINT_COMPRESSION_MODE_RLE;
192end;
193
194class procedure TBGRAWriterLazPaint.WriteRLEImage(Str: TStream;
195 Img: TFPCustomImage; ACaption: string);
196const PossiblePlanes = 4;
197var
198 PPlane,PPlaneCur: array[0..PossiblePlanes-1] of PByte;
199 CompressedPlane: array[0..PossiblePlanes-1] of TMemoryStream;
200 NbPixels, NbNonTranspPixels, NbOpaquePixels: integer;
201 Colors: array[0..255] of Int32or64;
202 ColorCount: Int32or64;
203 CompressedRGB: array[0..3] of TMemoryStream;
204 ColorTab: packed array[0..256*3-1] of byte;
205 Indexed: PByte;
206 NonRGBSize,RGBSize: int64;
207
208 procedure OutputPlane(AIndex: integer);
209 begin
210 str.WriteDWord(NtoLE(DWord(CompressedPlane[AIndex].Size)));
211 CompressedPlane[AIndex].Position:= 0;
212 str.CopyFrom(CompressedPlane[AIndex],CompressedPlane[AIndex].Size);
213 end;
214
215 procedure OutputRGB(AIndex: integer);
216 begin
217 str.WriteDWord(NtoLE(DWord(CompressedRGB[AIndex].Size)));
218 CompressedRGB[AIndex].Position:= 0;
219 str.CopyFrom(CompressedRGB[AIndex],CompressedRGB[AIndex].Size);
220 end;
221
222 function BuildPalette: boolean;
223 var n,i: Int32or64;
224 lastColor,color,colorIndex: Int32or64;
225 found: boolean;
226 begin
227 ColorCount := 0;
228 ColorIndex := 0;
229 lastColor := -1;
230 GetMem(Indexed, NbNonTranspPixels);
231 for n := 0 to NbNonTranspPixels-1 do
232 begin
233 color := (PPlane[0]+n)^+ ((PPlane[1]+n)^ shl 8)+ ((PPlane[2]+n)^ shl 16);
234 if color = lastColor then
235 begin
236 (Indexed+n)^ := ColorIndex;
237 continue;
238 end;
239 found := false;
240 for i := 0 to ColorCount-1 do
241 begin
242 if colors[i] = color then
243 begin
244 found := true;
245 ColorIndex := i;
246 break;
247 end;
248 end;
249 if not found then
250 begin
251 inc(ColorCount);
252 if ColorCount > 256 then
253 begin
254 result := false;
255 ReAllocMem(Indexed,0);
256 exit;
257 end;
258 colors[colorCount-1] := color;
259 ColorIndex := ColorCount-1;
260 end;
261 (Indexed+n)^ := ColorIndex;
262 lastColor := color;
263 end;
264 result := true;
265 end;
266
267var
268 i,x,y: integer;
269 PlaneFlags: Byte;
270 a: NativeInt;
271
272begin
273 NbPixels := Img.Width*img.Height;
274
275 for i := 0 to PossiblePlanes-1 do
276 begin
277 getmem(PPlane[i],NbPixels);
278 PPlaneCur[i] := PPlane[i];
279 CompressedPlane[i] := nil;
280 end;
281
282 NbNonTranspPixels := 0;
283 NbOpaquePixels:= 0;
284 for y := 0 to img.Height-1 do
285 for x := 0 to img.Width-1 do
286 begin
287 with img.Colors[x,y] do
288 begin
289 a := alpha shr 8;
290 PPlaneCur[3]^ := a;
291 inc(PPlaneCur[3]);
292 if a = 0 then continue;
293 if a = 255 then inc(NbOpaquePixels);
294
295 inc(NbNonTranspPixels);
296 PPlaneCur[0]^ := red shr 8;
297 PPlaneCur[1]^ := green shr 8;
298 PPlaneCur[2]^ := blue shr 8;
299 inc(PPlaneCur[0]);
300 inc(PPlaneCur[1]);
301 inc(PPlaneCur[2]);
302 end;
303 end;
304
305 PlaneFlags := 0;
306 if NbOpaquePixels = NbPixels then PlaneFlags := PlaneFlags or LazpaintChannelNoAlpha;
307 if CompareMem(PPlane[1],PPlane[0],NbNonTranspPixels) then PlaneFlags := PlaneFlags or LazpaintChannelGreenFromRed;
308 if CompareMem(PPlane[2],PPlane[0],NbNonTranspPixels) then PlaneFlags := PlaneFlags or LazpaintChannelBlueFromRed else
309 if CompareMem(PPlane[2],PPlane[1],NbNonTranspPixels) then PlaneFlags := PlaneFlags or LazpaintChannelBlueFromGreen;
310
311 //if we cannot reduce to one plane, maybe we will have more luck with a palette
312 for i := 0 to 3 do CompressedRGB[i] := nil;
313 Indexed := nil;
314 RGBSize := 0;
315 if ((PlaneFlags and LazpaintChannelGreenFromRed) = 0) or
316 ((PlaneFlags and (LazpaintChannelBlueFromRed or LazpaintChannelBlueFromGreen)) = 0) and (NbNonTranspPixels > 0) then
317 begin
318 if BuildPalette then
319 begin
320 if ColorCount shl 1 < NbNonTranspPixels then
321 begin
322 fillchar({%H-}ColorTab, sizeof(ColorTab), 0);
323 for i := 0 to ColorCount-1 do
324 begin
325 colorTab[i] := Colors[i] and 255;
326 colorTab[i+256] := (Colors[i] shr 8) and 255;
327 colorTab[i+512] := (Colors[i] shr 16) and 255;
328 end;
329 CompressedRGB[0] := TMemoryStream.Create;
330 EncodeLazRLE(colorTab[0], ColorCount, CompressedRGB[0]);
331 if (PlaneFlags and LazpaintChannelGreenFromRed) = 0 then
332 begin
333 CompressedRGB[1] := TMemoryStream.Create;
334 EncodeLazRLE(colorTab[256], ColorCount, CompressedRGB[1]);
335 end;
336 if (PlaneFlags and (LazpaintChannelBlueFromRed or LazpaintChannelBlueFromGreen)) = 0 then
337 begin
338 CompressedRGB[2] := TMemoryStream.Create;
339 EncodeLazRLE(colorTab[512], ColorCount, CompressedRGB[2]);
340 end;
341 CompressedRGB[3] := TMemoryStream.Create;
342 EncodeLazRLE(Indexed^,NbNonTranspPixels,CompressedRGB[3]);
343
344 for i := 0 to 3 do
345 if CompressedRGB[i] <> nil then
346 inc(RGBSize,CompressedRGB[i].Size);
347 end;
348 ReAllocMem(Indexed,0);
349 end;
350 end;
351
352 if (PlaneFlags and LazpaintChannelGreenFromRed) <> 0 then ReAllocMem(PPlane[1],0);
353 if (PlaneFlags and (LazpaintChannelBlueFromRed or LazpaintChannelBlueFromGreen)) <> 0 then ReAllocMem(PPlane[2],0);
354
355 NonRGBSize := 0;
356 for i := 0 to PossiblePlanes-1 do
357 if PPlane[i] <> nil then
358 begin
359 CompressedPlane[i] := TMemoryStream.Create;
360 if i = 3 then
361 EncodeLazRLE(PPlane[i]^, NbPixels,CompressedPlane[i])
362 else
363 EncodeLazRLE(PPlane[i]^, NbNonTranspPixels,CompressedPlane[i]);
364 inc(NonRGBSize, CompressedPlane[i].Size);
365 end;
366
367 if (CompressedRGB[3] <> nil) and (NonRGBSize > RGBSize) then
368 PlaneFlags:= PlaneFlags or LazpaintPalettedRGB;
369
370 str.WriteDWord(NtoLE(DWord(img.width)));
371 str.WriteDWord(NtoLE(DWord(img.Height)));
372 str.WriteDWord(NtoLE(DWord(length(ACaption))));
373 if length(ACaption)>0 then str.WriteBuffer(ACaption[1],length(ACaption));
374 str.WriteByte(PlaneFlags);
375
376 if (PlaneFlags and LazpaintChannelNoAlpha) = 0 then OutputPlane(3);
377 if (PlaneFlags and LazpaintPalettedRGB) <> 0 then
378 begin
379 for i := 0 to 3 do
380 if CompressedRGB[i] <> nil then
381 OutputRGB(i);
382 end else
383 begin
384 OutputPlane(0);
385 if (PlaneFlags and LazpaintChannelGreenFromRed) = 0 then OutputPlane(1);
386 if (PlaneFlags and (LazpaintChannelBlueFromRed or LazpaintChannelBlueFromGreen)) = 0 then OutputPlane(2);
387 end;
388
389 for i := 0 to PossiblePlanes-1 do
390 begin
391 freemem(PPlane[i]);
392 CompressedPlane[i].Free;
393 end;
394 for i := 0 to 3 do
395 CompressedRGB[i].Free;
396end;
397
398initialization
399
400 DefaultBGRAImageWriter[ifLazPaint] := TBGRAWriterLazPaint;
401
402end.
Note: See TracBrowser for help on using the repository browser.