1 // SPDX-License-Identifier: LGPL-3.0-linking-exception
2 {
3 The original file is part of the Free Pascal run time library.
4 Copyright (c) 2003 by the Free Pascal development team
5
6 PNG writer class modified by circular.
7
8 **********************************************************************
9
10 Fix for images with grayscale and alpha,
11 and for images with transparent pixels
12 }
13 unit BGRAWritePNG;
14
15 {$mode objfpc}{$H+}
16
17 interface
18
19
20 uses sysutils, BGRAClasses, FPImage, FPImgCmn, PNGcomn, ZStream, BGRABitmapTypes;
21
22 type
23 THeaderChunk = packed record
24 Width, height : LongWord;
25 BitDepth, ColorType, Compression, Filter, Interlace : byte;
26 end;
27
28 TGetPixelFunc = function (x,y : LongWord) : TColorData of object;
29 TGetPixelBGRAFunc = function (p: PBGRAPixel) : TColorData of object;
30
31 TColorFormatFunction = function (color:TFPColor) : TColorData of object;
32
33 { TBGRAWriterPNG }
34
35 TBGRAWriterPNG = class (TBGRACustomWriterPNG)
36 private
37 FUsetRNS, FCompressedText, FWordSized, FIndexed,
38 FUseAlpha, FGrayScale : boolean;
39 FByteWidth : byte;
40 FChunk : TChunk;
41 CFmt : TColorFormat; // format of the colors to convert from
42 FFmtColor : TColorFormatFunction;
FTransparentColornull43 FTransparentColor : TFPColor;
44 FTransparentColorOk: boolean;
45 FSwitchLine, FCurrentLine, FPreviousLine : pByteArray;
46 FPalette : TFPPalette;
47 OwnsPalette : boolean;
48 FHeader : THeaderChunk;
49 FGetPixel : TGetPixelFunc;
50 FGetPixelBGRA : TGetPixelBGRAFunc;
51 FDatalineLength : LongWord;
52 ZData : TMemoryStream; // holds uncompressed data until all blocks are written
53 Compressor : TCompressionStream; // compresses the data
54 FCompressionLevel : TCompressionLevel;
55 procedure WriteChunk;
GetColorPixelnull56 function GetColorPixel (x,y:LongWord) : TColorData;
GetPalettePixelnull57 function GetPalettePixel (x,y:LongWord) : TColorData;
GetColPalPixelnull58 function GetColPalPixel (x,y:LongWord) : TColorData;
GetColorPixelBGRAnull59 function GetColorPixelBGRA (p: PBGRAPixel) : TColorData;
GetPalettePixelBGRAnull60 function GetPalettePixelBGRA (p: PBGRAPixel) : TColorData;
GetColPalPixelBGRAnull61 function GetColPalPixelBGRA (p: PBGRAPixel) : TColorData;
62 procedure InitWriteIDAT;
63 procedure Gatherdata;
64 procedure WriteCompressedData;
65 procedure FinalWriteIDAT;
66 protected
67 property Header : THeaderChunk read FHeader;
68 procedure InternalWrite ({%H-}Str:TStream; {%H-}Img:TFPCustomImage); override;
GetUseAlphanull69 function GetUseAlpha: boolean; override;
70 procedure SetUseAlpha(AValue: boolean); override;
71 procedure WriteIHDR; virtual;
72 procedure WritePLTE; virtual;
73 procedure WritetRNS; virtual;
74 procedure WriteIDAT; virtual;
75 procedure WriteTexts; virtual;
76 procedure WriteIEND; virtual;
CurrentLinenull77 function CurrentLine (x:LongWord) : byte; inline;
PrevSamplenull78 function PrevSample (x:LongWord): byte; inline;
PreviousLinenull79 function PreviousLine (x:LongWord) : byte; inline;
PrevLinePrevSamplenull80 function PrevLinePrevSample (x:LongWord): byte; inline;
DoFilternull81 function DoFilter (LineFilter:byte;index:LongWord; b:byte) : byte; virtual;
82 procedure SetChunkLength (aValue : LongWord);
83 procedure SetChunkType (ct : TChunkTypes); overload;
84 procedure SetChunkType (ct : TChunkCode); overload;
DecideGetPixelnull85 function DecideGetPixel : TGetPixelFunc; virtual;
DecideGetPixelBGRAnull86 function DecideGetPixelBGRA : TGetPixelBGRAFunc; virtual;
87 procedure DetermineHeader (var AHeader : THeaderChunk); virtual;
DetermineFilternull88 function DetermineFilter ({%H-}Current, {%H-}Previous:PByteArray; {%H-}linelength:LongWord):byte; virtual;
89 procedure FillScanLine (y : integer; ScanLine : pByteArray); virtual;
ColorDataGrayBnull90 function ColorDataGrayB(color:TFPColor) : TColorData;
ColorDataColorBnull91 function ColorDataColorB(color:TFPColor) : TColorData;
ColorDataGrayWnull92 function ColorDataGrayW(color:TFPColor) : TColorData;
ColorDataColorWnull93 function ColorDataColorW(color:TFPColor) : TColorData;
ColorDataGrayABnull94 function ColorDataGrayAB(color:TFPColor) : TColorData;
ColorDataColorABnull95 function ColorDataColorAB(color:TFPColor) : TColorData;
ColorDataGrayAWnull96 function ColorDataGrayAW(color:TFPColor) : TColorData;
ColorDataColorAWnull97 function ColorDataColorAW(color:TFPColor) : TColorData;
98 property ChunkDataBuffer : pByteArray read FChunk.data;
99 property UsetRNS : boolean read FUsetRNS;
100 property SingleTransparentColor : TFPColor read FTransparentColor;
101 property SingleTransparentColorOk : boolean read FTransparentColorOk;
102 property ThePalette : TFPPalette read FPalette;
103 property ColorFormat : TColorformat read CFmt;
readnull104 property ColorFormatFunc : TColorFormatFunction read FFmtColor;
105 property byteWidth : byte read FByteWidth;
106 property DatalineLength : LongWord read FDatalineLength;
107 public
108 constructor create; override;
109 destructor destroy; override;
110 property GrayScale : boolean read FGrayscale write FGrayScale;
111 property Indexed : boolean read FIndexed write FIndexed;
112 property CompressedText : boolean read FCompressedText write FCompressedText;
113 property WordSized : boolean read FWordSized write FWordSized;
114 property CompressionLevel : TCompressionLevel read FCompressionLevel write FCompressionLevel;
115 end;
116
117 implementation
118
119 constructor TBGRAWriterPNG.create;
120 begin
121 inherited;
122 Fchunk.acapacity := 0;
123 Fchunk.data := nil;
124 FGrayScale := False;
125 FIndexed := False;
126 FCompressedText := True;
127 FWordSized := False;
128 FUseAlpha := True;
129 FCompressionLevel:=clDefault;
130 end;
131
132 destructor TBGRAWriterPNG.destroy;
133 begin
134 if OwnsPalette then FreeAndNil(FPalette);
135 with Fchunk do
136 if acapacity > 0 then
137 freemem (data);
138 inherited;
139 end;
140
141 procedure TBGRAWriterPNG.WriteChunk;
142 var chead : TChunkHeader;
143 c : LongWord;
144 begin
145 with FChunk do
146 begin
147 {$IFDEF ENDIAN_LITTLE}
148 chead.CLength := swap (alength);
149 {$ELSE}
150 chead.CLength := alength;
151 {$ENDIF}
152 if (ReadType = '') then
153 if atype <> ctUnknown then
154 chead.CType := ChunkTypes[aType]
155 else
156 raise PNGImageException.create ('Doesn''t have a chunktype to write')
157 else
158 chead.CType := ReadType;
159 c := CalculateCRC (All1Bits, ReadType, sizeOf(ReadType));
160 c := CalculateCRC (c, data^, alength);
161 {$IFDEF ENDIAN_LITTLE}
162 crc := swap(c xor All1Bits);
163 {$ELSE}
164 crc := c xor All1Bits;
165 {$ENDIF}
166 with TheStream do
167 begin
168 Write (chead, sizeof(chead));
169 Write (data^[0], alength);
170 Write (crc, sizeof(crc));
171 end;
172 end;
173 end;
174
175 procedure TBGRAWriterPNG.SetChunkLength(aValue : LongWord);
176 begin
177 with Fchunk do
178 begin
179 alength := aValue;
180 if aValue > acapacity then
181 begin
182 if acapacity > 0 then
183 freemem (data);
184 GetMem (data, alength);
185 acapacity := alength;
186 end;
187 end;
188 end;
189
190 procedure TBGRAWriterPNG.SetChunkType (ct : TChunkTypes);
191 begin
192 with Fchunk do
193 begin
194 aType := ct;
195 ReadType := ChunkTypes[ct];
196 end;
197 end;
198
199 procedure TBGRAWriterPNG.SetChunkType (ct : TChunkCode);
200 begin
201 with FChunk do
202 begin
203 ReadType := ct;
204 aType := low(TChunkTypes);
205 while (aType < high(TChunkTypes)) and (ChunkTypes[aType] <> ct) do
206 inc (aType);
207 end;
208 end;
209
CurrentLinenull210 function TBGRAWriterPNG.CurrentLine(x:LongWord):byte;
211 begin
212 result := FCurrentLine^[x];
213 end;
214
PrevSamplenull215 function TBGRAWriterPNG.PrevSample (x:LongWord): byte;
216 begin
217 if x < byteWidth then
218 result := 0
219 else
220 result := FCurrentLine^[x - bytewidth];
221 end;
222
PreviousLinenull223 function TBGRAWriterPNG.PreviousLine (x:LongWord) : byte;
224 begin
225 result := FPreviousline^[x];
226 end;
227
TBGRAWriterPNG.PrevLinePrevSamplenull228 function TBGRAWriterPNG.PrevLinePrevSample (x:LongWord): byte;
229 begin
230 if x < byteWidth then
231 result := 0
232 else
233 result := FPreviousLine^[x - bytewidth];
234 end;
235
DoFilternull236 function TBGRAWriterPNG.DoFilter(LineFilter:byte;index:LongWord; b:byte) : byte;
237 var diff : byte;
238 procedure FilterSub;
239 begin
240 diff := PrevSample(index);
241 end;
242 procedure FilterUp;
243 begin
244 diff := PreviousLine(index);
245 end;
246 procedure FilterAverage;
247 var l, p : word;
248 begin
249 l := PrevSample(index);
250 p := PreviousLine(index);
251 Diff := (l + p) div 2;
252 end;
253 procedure FilterPaeth;
254 var dl, dp, dlp : word; // index for previous and distances for:
255 l, p, lp : byte; // r:predictor, Left, Previous, LeftPrevious
256 r : integer;
257 begin
258 l := PrevSample(index);
259 lp := PrevLinePrevSample(index);
260 p := PreviousLine(index);
261 r := Int32or64(l) + Int32or64(p) - Int32or64(lp);
262 dl := abs (r - l);
263 dlp := abs (r - lp);
264 dp := abs (r - p);
265 if (dl <= dp) and (dl <= dlp) then
266 diff := l
267 else if dp <= dlp then
268 diff := p
269 else
270 diff := lp;
271 end;
272 begin
273 case LineFilter of
274 0 : diff := 0;
275 1 : FilterSub;
276 2 : FilterUp;
277 3 : FilterAverage;
278 4 : FilterPaeth;
279 end;
280 if diff > b then
281 result := (b + $100 - diff)
282 else
283 result := b - diff;
284 end;
285
286 procedure TBGRAWriterPNG.DetermineHeader (var AHeader : THeaderChunk);
287 var c : integer;
288
ReducedColorEqualsnull289 function ReducedColorEquals(const c1,c2: TFPColor): boolean;
290 var g1,g2: word;
291 begin
292 if FGrayScale then
293 begin
294 g1 := CalculateGray(c1);
295 g2 := CalculateGray(c2);
296 if fwordsized then
297 result := (g1 = g2)
298 else
299 result := (g1 shr 8 = g2 shr 8);
300 end else
301 begin
302 if FWordSized then
303 result := (c1.red = c2.red) and (c1.green = c2.green) and (c1.blue = c2.blue)
304 else
305 result := (c1.red shr 8 = c2.red shr 8) and (c1.green shr 8 = c2.green shr 8) and (c1.blue shr 8 = c2.blue shr 8);
306 end;
307 end;
308
CountAlphasnull309 function CountAlphas : integer;
310 var none, half : boolean;
311 maxTransparentAlpha: word;
312
313 procedure CountFromPalettedImage;
314 var
315 p : integer;
316 a : word;
317 c : TFPColor;
318 begin
319 with TheImage.Palette do
320 begin
321 p := count-1;
322 FTransparentColor.alpha := alphaOpaque;
323 while (p >= 0) do
324 begin
325 c := color[p];
326 a := c.Alpha;
327 if a < FTransparentColor.alpha then //we're looking for the most transparent color
328 FTransparentColor := c;
329 if a <= maxTransparentAlpha then none := true
330 else if a <> alphaOpaque then half := true;
331 dec (p);
332 end;
333
334 //check transparent color is used consistently
335 FTransparentColorOk := true;
336 p := count-1;
337 while (p >= 0) do
338 begin
339 c := color[p];
340 if c.alpha > maxTransparentAlpha then
341 begin
342 if ReducedColorEquals(c, FTransparentColor) then
343 begin
344 FTransparentColorOk := false;
345 break;
346 end;
347 end
348 else
349 begin
350 if not ReducedColorEquals(c, FTransparentColor) then
351 begin
352 FTransparentColorOk := false;
353 break;
354 end;
355 end;
356 dec(p);
357 end;
358 end;
359 end;
360
361 procedure CountFromRGBImage;
362 var
363 a : word;
364 c : TFPColor;
365 x,y : longint; // checks on < 0
366 begin
367 with TheImage do
368 begin
369 x := width-1;
370 y := height-1;
371 FTransparentColor.alpha := alphaOpaque;
372 while (y >= 0) and not half do //we stop if we already need a full alpha
373 begin
374 c := colors[x,y];
375 a := c.Alpha;
376 if a < FTransparentColor.alpha then //we're looking for the most transparent color
377 FTransparentColor := c;
378 if a <= maxTransparentAlpha then none := true
379 else if a <> alphaOpaque then half := true;
380 dec (x);
381 if (x < 0) then
382 begin
383 dec (y);
384 x := width-1;
385 end;
386 end;
387
388 //check transparent color is used consistently
389 FTransparentColorOk := true;
390 x := width-1;
391 y := height-1;
392 while (y >= 0) do
393 begin
394 c := colors[x,y];
395 if c.alpha > maxTransparentAlpha then
396 begin
397 if ReducedColorEquals(c, FTransparentColor) then
398 begin
399 FTransparentColorOk := false;
400 break;
401 end;
402 end
403 else
404 begin
405 if not ReducedColorEquals(c, FTransparentColor) then
406 begin
407 FTransparentColorOk := false;
408 break;
409 end;
410 end;
411 dec (x);
412 if (x < 0) then
413 begin
414 dec (y);
415 x := width-1;
416 end;
417 end;
418 end;
419 end;
420
421 begin
422 FTransparentColorOk := false;
423 if FWordSized then maxTransparentAlpha := 0
424 else maxTransparentAlpha := $00ff;
425 half := false;
426 none := false;
427 with TheImage do
428 if UsePalette then
429 CountFromPalettedImage
430 else
431 CountFromRGBImage;
432
433 if half then
434 result := 3
435 else
436 if none then
437 begin
438 if FTransparentColorOk then
439 result := 2
440 else
441 result := 3;
442 end
443 else
444 result := 1;
445 end;
446 procedure DetermineColorFormat;
447 begin
448 with AHeader do
449 case colortype of
450 0 : if FWordSized then
451 begin
452 FFmtColor := @ColorDataGrayW;
453 FByteWidth := 2;
454 //CFmt := cfGray16
455 end
456 else
457 begin
458 FFmtColor := @ColorDataGrayB;
459 FByteWidth := 1;
460 //CFmt := cfGray8;
461 end;
462 2 : if FWordSized then
463 begin
464 FFmtColor := @ColorDataColorW;
465 FByteWidth := 6;
466 //CFmt := cfBGR48
467 end
468 else
469 begin
470 FFmtColor := @ColorDataColorB;
471 FByteWidth := 3;
472 //CFmt := cfBGR24;
473 end;
474 4 : if FWordSized then
475 begin
476 FFmtColor := @ColorDataGrayAW;
477 FByteWidth := 4;
478 //CFmt := cfGrayA32
479 end
480 else
481 begin
482 FFmtColor := @ColorDataGrayAB;
483 FByteWidth := 2;
484 //CFmt := cfGrayA16;
485 end;
486 6 : if FWordSized then
487 begin
488 FFmtColor := @ColorDataColorAW;
489 FByteWidth := 8;
490 //CFmt := cfABGR64
491 end
492 else
493 begin
494 FFmtColor := @ColorDataColorAB;
495 FByteWidth := 4;
496 //CFmt := cfABGR32;
497 end;
498 end;
499 end;
500 begin
501 with AHeader do
502 begin
503 {$IFDEF ENDIAN_LITTLE}
504 // problem: TheImage has integer width, PNG header LongWord width.
505 // Integer Swap can give negative value
506 Width := swap (LongWord(TheImage.Width));
507 height := swap (LongWord(TheImage.Height));
508 {$ELSE}
509 Width := TheImage.Width;
510 height := TheImage.Height;
511 {$ENDIF}
512 if FUseAlpha then
513 c := CountAlphas
514 else
515 c := 0;
516 if FIndexed then
517 begin
518 if OwnsPalette then FreeAndNil(FPalette);
519 OwnsPalette := not TheImage.UsePalette;
520 if OwnsPalette then
521 begin
522 FPalette := TFPPalette.Create (16);
523 FPalette.Build (TheImage);
524 end
525 else
526 FPalette := TheImage.Palette;
527 if ThePalette.count > 256 then
528 raise PNGImageException.Create ('Too many colors to use indexed PNG color type');
529 ColorType := 3;
530 FUsetRNS := C > 1;
531 BitDepth := 8;
532 FByteWidth := 1;
533 end
534 else
535 begin
536 if c = 3 then
537 ColorType := 4;
538 FUsetRNS := (c = 2);
539 if not FGrayScale then
540 ColorType := ColorType + 2;
541 if FWordSized then
542 BitDepth := 16
543 else
544 BitDepth := 8;
545 DetermineColorFormat;
546 end;
547 Compression := 0;
548 Filter := 0;
549 Interlace := 0;
550 end;
551 end;
552
553 procedure TBGRAWriterPNG.WriteIHDR;
554 begin
555 // signature for PNG
556 TheStream.writeBuffer(Signature,sizeof(Signature));
557 // Determine all settings for filling the header
558 fillchar(fheader,sizeof(fheader),#0);
559 DetermineHeader (FHeader);
560 // write the header chunk
561 SetChunkLength (sizeof(FHeader));
562 move (FHeader, ChunkDataBuffer^, sizeof(FHeader));
563 SetChunkType (ctIHDR);
564 WriteChunk;
565 end;
566
567 { Color convertions }
568
ColorDataGrayBnull569 function TBGRAWriterPNG.ColorDataGrayB(color:TFPColor) : TColorData;
570 var t : word;
571 begin
572 t := CalculateGray (color);
573 result := hi(t);
574 end;
575
ColorDataGrayWnull576 function TBGRAWriterPNG.ColorDataGrayW(color:TFPColor) : TColorData;
577 begin
578 result := CalculateGray (color);
579 end;
580
TBGRAWriterPNG.ColorDataGrayABnull581 function TBGRAWriterPNG.ColorDataGrayAB(color:TFPColor) : TColorData;
582 begin
583 result := ColorDataGrayB (color);
584 result := (color.Alpha and $ff00) or result;
585 end;
586
TBGRAWriterPNG.ColorDataGrayAWnull587 function TBGRAWriterPNG.ColorDataGrayAW(color:TFPColor) : TColorData;
588 begin
589 result := ColorDataGrayW (color);
590 result := (color.Alpha shl 16) or result;
591 end;
592
TBGRAWriterPNG.ColorDataColorBnull593 function TBGRAWriterPNG.ColorDataColorB(color:TFPColor) : TColorData;
594 begin
595 {$PUSH}{$HINTS OFF}
596 with color do
597 result := hi(red) + (green and $FF00) + (hi(blue) shl 16);
598 {$POP}
599 end;
600
ColorDataColorWnull601 function TBGRAWriterPNG.ColorDataColorW(color:TFPColor) : TColorData;
602 begin
603 {$PUSH}{$HINTS OFF}
604 with color do
605 result := red + (green shl 16) + (qword(blue) shl 32);
606 {$POP}
607 end;
608
ColorDataColorABnull609 function TBGRAWriterPNG.ColorDataColorAB(color:TFPColor) : TColorData;
610 begin
611 {$PUSH}{$HINTS OFF}
612 with color do
613 result := hi(red) + (green and $FF00) + (hi(blue) shl 16) + (hi(alpha) shl 24);
614 {$POP}
615 end;
616
ColorDataColorAWnull617 function TBGRAWriterPNG.ColorDataColorAW(color:TFPColor) : TColorData;
618 begin
619 {$PUSH}{$HINTS OFF}
620 with color do
621 result := red + (green shl 16) + (qword(blue) shl 32) + (qword(alpha) shl 48);
622 {$POP}
623 end;
624
625 { Data making routines }
626
TBGRAWriterPNG.GetColorPixelnull627 function TBGRAWriterPNG.GetColorPixel (x,y:LongWord) : TColorData;
628 begin
629 result := FFmtColor (TheImage[x,y]);
630 end;
631
TBGRAWriterPNG.GetPalettePixelnull632 function TBGRAWriterPNG.GetPalettePixel (x,y:LongWord) : TColorData;
633 begin
634 result := TheImage.Pixels[x,y];
635 end;
636
TBGRAWriterPNG.GetColPalPixelnull637 function TBGRAWriterPNG.GetColPalPixel (x,y:LongWord) : TColorData;
638 begin
639 result := ThePalette.IndexOf (TheImage.Colors[x,y]);
640 end;
641
TBGRAWriterPNG.GetColorPixelBGRAnull642 function TBGRAWriterPNG.GetColorPixelBGRA(p: PBGRAPixel): TColorData;
643 begin
644 result := FFmtColor(p^.ToFPColor);
645 end;
646
GetPalettePixelBGRAnull647 function TBGRAWriterPNG.GetPalettePixelBGRA(p: PBGRAPixel): TColorData;
648 begin
649 result := TheImage.Palette.IndexOf(p^.ToFPColor);
650 end;
651
GetColPalPixelBGRAnull652 function TBGRAWriterPNG.GetColPalPixelBGRA(p: PBGRAPixel): TColorData;
653 begin
654 result := ThePalette.IndexOf(p^.ToFPColor);
655 end;
656
DecideGetPixelnull657 function TBGRAWriterPNG.DecideGetPixel : TGetPixelFunc;
658 begin
659 case Fheader.colortype of
660 3 : if TheImage.UsePalette then
661 result := @GetPalettePixel
662 else result := @GetColPalPixel;
663 else result := @GetColorPixel;
664 end;
665 end;
666
TBGRAWriterPNG.DecideGetPixelBGRAnull667 function TBGRAWriterPNG.DecideGetPixelBGRA: TGetPixelBGRAFunc;
668 begin
669 case Fheader.colortype of
670 3 : if TheImage.UsePalette then
671 result := @GetPalettePixelBGRA
672 else result := @GetColPalPixelBGRA;
673 else result := @GetColorPixelBGRA;
674 end;
675 end;
676
677 procedure TBGRAWriterPNG.WritePLTE;
678 var r,t : integer;
679 c : TFPColor;
680 begin
681 with ThePalette do
682 begin
683 SetChunkLength (count*3);
684 SetChunkType (ctPLTE);
685 t := 0;
686 For r := 0 to count-1 do
687 begin
688 c := Color[r];
689 ChunkdataBuffer^[t] := c.red div 256;
690 inc (t);
691 ChunkdataBuffer^[t] := c.green div 256;
692 inc (t);
693 ChunkdataBuffer^[t] := c.blue div 256;
694 inc (t);
695 end;
696 end;
697 WriteChunk;
698 end;
699
700 procedure TBGRAWriterPNG.InitWriteIDAT;
701 begin
702 FDatalineLength := TheImage.Width*ByteWidth;
703 GetMem (FPreviousLine, FDatalineLength);
704 GetMem (FCurrentLine, FDatalineLength);
705 fillchar (FCurrentLine^,FDatalineLength,0);
706 ZData := TMemoryStream.Create;
707 Compressor := TCompressionStream.Create (FCompressionLevel,ZData);
708 FGetPixel := DecideGetPixel;
709 FGetPixelBGRA := DecideGetPixelBGRA;
710 end;
711
712 procedure TBGRAWriterPNG.FinalWriteIDAT;
713 begin
714 ZData.Free;
715 FreeMem (FPreviousLine);
716 FreeMem (FCurrentLine);
717 end;
718
TBGRAWriterPNG.DetermineFilternull719 function TBGRAWriterPNG.DetermineFilter (Current, Previous:PByteArray; linelength:LongWord) : byte;
720 begin
721 result := 0;
722 end;
723
724 procedure TBGRAWriterPNG.FillScanLine (y : integer; ScanLine : pByteArray);
725 var x : integer;
726 cd : TColorData;
727 r, index : LongWord;
728 b : byte;
729 p : PBGRAPixel;
730 begin
731 index := 0;
732 if TheImage is TBGRACustomBitmap then
733 begin
734 p := TBGRACustomBitmap(TheImage).ScanLine[y];
735 if FHeader.BitDepth <> 16 then
736 case FByteWidth of
737 1: for x := pred(TheImage.Width) downto 0 do
738 begin
739 cd := FGetPixelBGRA(p);
740 ScanLine^[index] := cd;
741 inc (index);
742 inc(p);
743 end;
744 2: for x := pred(TheImage.Width) downto 0 do
745 begin
746 cd := FGetPixelBGRA(p);
747 ScanLine^[index] := cd and $ff;
748 ScanLine^[index+1] := cd shr 8;
749 inc (index,2);
750 inc(p);
751 end;
752 3: for x := pred(TheImage.Width) downto 0 do
753 begin
754 ScanLine^[index] := p^.red;
755 ScanLine^[index+1] := p^.green;
756 ScanLine^[index+2] := p^.blue;
757 inc (index,3);
758 inc(p);
759 end;
760 4: for x := pred(TheImage.Width) downto 0 do
761 begin
762 ScanLine^[index] := p^.red;
763 ScanLine^[index+1] := p^.green;
764 ScanLine^[index+2] := p^.blue;
765 ScanLine^[index+3] := p^.alpha;
766 inc (index,4);
767 inc(p);
768 end;
769 else raise exception.Create('Unexpected byte width');
770 end else
771 for x := pred(TheImage.Width) downto 0 do
772 begin
773 cd := FGetPixelBGRA(p);
774 {$IFDEF ENDIAN_BIG}
775 cd:=swap(cd);
776 {$ENDIF}
777 move (cd, ScanLine^[index], FBytewidth);
778 if WordSized then
779 begin
780 r := 0;
781 while (r+1 < FByteWidth) do
782 begin
783 b := Scanline^[index+r+1];
784 Scanline^[index+r+1] := Scanline^[index+r];
785 Scanline^[index+r] := b;
786 inc (r,2);
787 end;
788 end;
789 inc (index, FByteWidth);
790 inc(p);
791 end;
792 end
793 else
794 for x := 0 to pred(TheImage.Width) do
795 begin
796 cd := FGetPixel (x,y);
797 {$IFDEF ENDIAN_BIG}
798 cd:=swap(cd);
799 {$ENDIF}
800 move (cd, ScanLine^[index], FBytewidth);
801 if WordSized then
802 begin
803 r := 0;
804 while (r+1 < FByteWidth) do
805 begin
806 b := Scanline^[index+r+1];
807 Scanline^[index+r+1] := Scanline^[index+r];
808 Scanline^[index+r] := b;
809 inc (r,2);
810 end;
811 end;
812 inc (index, FByteWidth);
813 end;
814 end;
815
816 procedure TBGRAWriterPNG.Gatherdata;
817 var x,y : integer;
818 lf : byte;
819 begin
820 for y := 0 to pred(TheImage.height) do
821 begin
822 FSwitchLine := FCurrentLine;
823 FCurrentLine := FPreviousLine;
824 FPreviousLine := FSwitchLine;
825 FillScanLine (y, FCurrentLine);
826 lf := DetermineFilter (FCurrentLine, FpreviousLine, FDataLineLength);
827 if lf <> 0 then
828 for x := 0 to FDatalineLength-1 do
829 FCurrentLine^[x] := DoFilter (lf, x, FCurrentLine^[x]);
830 Compressor.Write (lf, sizeof(lf));
831 Compressor.Write (FCurrentLine^, FDataLineLength);
832 end;
833 end;
834
835 procedure TBGRAWriterPNG.WriteCompressedData;
836 var l : LongWord;
837 begin
838 Compressor.Free; // Close compression and finish the writing in ZData
839 l := ZData.position;
840 ZData.position := 0;
841 SetChunkLength(l);
842 SetChunkType (ctIDAT);
843 ZData.Read (ChunkdataBuffer^, l);
844 WriteChunk;
845 end;
846
847 procedure TBGRAWriterPNG.WriteIDAT;
848 begin
849 InitWriteIDAT;
850 GatherData;
851 WriteCompressedData;
852 FinalWriteIDAT;
853 end;
854
855 procedure TBGRAWriterPNG.WritetRNS;
856 procedure PaletteAlpha;
857 var r : integer;
858 begin
859 with TheImage.palette do
860 begin
861 // search last palette entry with transparency
862 r := count;
863 repeat
864 dec (r);
865 until (r < 0) or (color[r].alpha <> alphaOpaque);
866 if r >= 0 then // there is at least 1 transparent color
867 begin
868 // from this color we go to the first palette entry
869 SetChunkLength (r+1);
870 repeat
871 chunkdatabuffer^[r] := (color[r].alpha shr 8);
872 dec (r);
873 until (r < 0);
874 end;
875 writechunk;
876 end;
877 end;
878 procedure GrayAlpha;
879 var g : word;
880 begin
881 SetChunkLength(2);
882 if WordSized then
883 g := CalculateGray (SingleTransparentColor)
884 else
885 g := hi (CalculateGray(SingleTransparentColor));
886 {$IFDEF ENDIAN_LITTLE}
887 g := swap (g);
888 {$ENDIF}
889 move (g,ChunkDataBuffer^[0],2);
890 WriteChunk;
891 end;
892 procedure ColorAlpha;
893 var g : TFPColor;
894 begin
895 SetChunkLength(6);
896 g := SingleTransparentColor;
897 with g do
898 if WordSized then
899 begin
900 {$IFDEF ENDIAN_LITTLE}
901 red := swap (red);
902 green := swap (green);
903 blue := swap (blue);
904 {$ENDIF}
905 move (g, ChunkDatabuffer^[0], 6);
906 end
907 else
908 begin
909 ChunkDataBuffer^[0] := 0;
910 ChunkDataBuffer^[1] := red shr 8;
911 ChunkDataBuffer^[2] := 0;
912 ChunkDataBuffer^[3] := green shr 8;
913 ChunkDataBuffer^[4] := 0;
914 ChunkDataBuffer^[5] := blue shr 8;
915 end;
916 WriteChunk;
917 end;
918 begin
919 SetChunkType (cttRNS);
920 case fheader.colortype of
921 6,4 : raise PNGImageException.create ('tRNS chunk forbidden for full alpha channels');
922 3 : PaletteAlpha;
923 2 : ColorAlpha;
924 0 : GrayAlpha;
925 end;
926 end;
927
928 procedure TBGRAWriterPNG.WriteTexts;
929 begin
930 end;
931
932 procedure TBGRAWriterPNG.WriteIEND;
933 begin
934 SetChunkLength(0);
935 SetChunkType (ctIEND);
936 WriteChunk;
937 end;
938
939 procedure TBGRAWriterPNG.InternalWrite (Str:TStream; Img:TFPCustomImage);
940 begin
941 WriteIHDR;
942 if Fheader.colorType = 3 then
943 WritePLTE;
944 if FUsetRNS then
945 WritetRNS;
946 WriteIDAT;
947 WriteTexts;
948 WriteIEND;
949 end;
950
TBGRAWriterPNG.GetUseAlphanull951 function TBGRAWriterPNG.GetUseAlpha: boolean;
952 begin
953 result := FUseAlpha;
954 end;
955
956 procedure TBGRAWriterPNG.SetUseAlpha(AValue: boolean);
957 begin
958 FUseAlpha := AValue;
959 end;
960
961 initialization
962
963 DefaultBGRAImageWriter[ifPng] := TBGRAWriterPNG;
964
965 end.
966