pngimage.pas
上传用户:ctlcnc
上传日期:2021-12-10
资源大小:4933k
文件大小:166k
源码类别:

2D图形编程

开发平台:

Delphi

  1.       Dest2 := pChar(Longint(Dest) + Col div 2);
  2.       {Copy data}
  3.       Byte(Dest2^) := Byte(Dest2^) or ((((Byte(Src^) shr CurBit) shl 2) and $F)
  4.          shl (4 - (Col*4) mod 8));
  5.       {Move to next column}
  6.       inc(Col, ColumnIncrement[Pass]);
  7.       {Will read next bits}
  8.       dec(CurBit, 2);
  9.     until CurBit < 0;
  10.     {Move to next byte in source}
  11.     inc(Src);
  12.   until Col >= ImageWidth;
  13. end;
  14. {Copy 韒ages with palette using 2 bytes for each pixel}
  15. procedure TChunkIDAT.CopyInterlacedGrayscale16(const Pass: Byte;
  16.   Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
  17. var
  18.   Col: Integer;
  19. begin
  20.   {Get first column and enter in loop}
  21.   Col := ColumnStart[Pass];
  22.   Dest := pChar(Longint(Dest) + Col);
  23.   repeat
  24.     {Copy this row}
  25.     Dest^ := Src^; inc(Dest);
  26.     {$IFDEF Store16bits}
  27.     Extra^ := pChar(Longint(Src) + 1)^; inc(Extra);
  28.     {$ENDIF}
  29.     {Move to next column}
  30.     inc(Src, 2);
  31.     inc(Dest, ColumnIncrement[Pass] - 1);
  32.     inc(Col, ColumnIncrement[Pass]);
  33.   until Col >= ImageWidth;
  34. end;
  35. {Decodes interlaced RGB alpha with 1 byte for each sample}
  36. procedure TChunkIDAT.CopyInterlacedRGBAlpha8(const Pass: Byte;
  37.   Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
  38. var
  39.   Col: Integer;
  40. begin
  41.   {Get first column and enter in loop}
  42.   Col := ColumnStart[Pass];
  43.   Dest := pChar(Longint(Dest) + Col * 3);
  44.   Trans := pChar(Longint(Trans) + Col);
  45.   repeat
  46.     {Copy this row and alpha value}
  47.     Trans^ := pChar(Longint(Src) + 3)^;
  48.     Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest);
  49.     Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Dest);
  50.     Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src)    )^]; inc(Dest);
  51.     {Move to next column}
  52.     inc(Src, 4);
  53.     inc(Dest, ColumnIncrement[Pass] * 3 - 3);
  54.     inc(Trans, ColumnIncrement[Pass]);
  55.     inc(Col, ColumnIncrement[Pass]);
  56.   until Col >= ImageWidth;
  57. end;
  58. {Decodes interlaced RGB alpha with 2 bytes for each sample}
  59. procedure TChunkIDAT.CopyInterlacedRGBAlpha16(const Pass: Byte;
  60.   Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
  61. var
  62.   Col: Integer;
  63. begin
  64.   {Get first column and enter in loop}
  65.   Col := ColumnStart[Pass];
  66.   Dest := pChar(Longint(Dest) + Col * 3);
  67.   Trans := pChar(Longint(Trans) + Col);
  68.   repeat
  69.     {Copy this row and alpha value}
  70.     Trans^ := pChar(Longint(Src) + 6)^;
  71.     Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src) + 4)^]; inc(Dest);
  72.     Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest);
  73.     Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src)    )^]; inc(Dest);
  74.     {$IFDEF Store16bits}
  75.     {Copy extra pixel values}
  76.     Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 5)^]; inc(Extra);
  77.     Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 3)^]; inc(Extra);
  78.     Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Extra);
  79.     {$ENDIF}
  80.     {Move to next column}
  81.     inc(Src, 8);
  82.     inc(Dest, ColumnIncrement[Pass] * 3 - 3);
  83.     inc(Trans, ColumnIncrement[Pass]);
  84.     inc(Col, ColumnIncrement[Pass]);
  85.   until Col >= ImageWidth;
  86. end;
  87. {Decodes 8 bit grayscale image followed by an alpha sample}
  88. procedure TChunkIDAT.CopyInterlacedGrayscaleAlpha8(const Pass: Byte;
  89.   Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
  90. var
  91.   Col: Integer;
  92. begin
  93.   {Get first column, pointers to the data and enter in loop}
  94.   Col := ColumnStart[Pass];
  95.   Dest := pChar(Longint(Dest) + Col);
  96.   Trans := pChar(Longint(Trans) + Col);
  97.   repeat
  98.     {Copy this grayscale value and alpha}
  99.     Dest^ := Src^;  inc(Src);
  100.     Trans^ := Src^; inc(Src);
  101.     {Move to next column}
  102.     inc(Dest, ColumnIncrement[Pass]);
  103.     inc(Trans, ColumnIncrement[Pass]);
  104.     inc(Col, ColumnIncrement[Pass]);
  105.   until Col >= ImageWidth;
  106. end;
  107. {Decodes 16 bit grayscale image followed by an alpha sample}
  108. procedure TChunkIDAT.CopyInterlacedGrayscaleAlpha16(const Pass: Byte;
  109.   Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
  110. var
  111.   Col: Integer;
  112. begin
  113.   {Get first column, pointers to the data and enter in loop}
  114.   Col := ColumnStart[Pass];
  115.   Dest := pChar(Longint(Dest) + Col);
  116.   Trans := pChar(Longint(Trans) + Col);
  117.   repeat
  118.     {$IFDEF Store16bits}
  119.     Extra^ := pChar(Longint(Src) + 1)^; inc(Extra);
  120.     {$ENDIF}
  121.     {Copy this grayscale value and alpha, transforming 16 bits into 8}
  122.     Dest^ := Src^;  inc(Src, 2);
  123.     Trans^ := Src^; inc(Src, 2);
  124.     {Move to next column}
  125.     inc(Dest, ColumnIncrement[Pass]);
  126.     inc(Trans, ColumnIncrement[Pass]);
  127.     inc(Col, ColumnIncrement[Pass]);
  128.   until Col >= ImageWidth;
  129. end;
  130. {Decodes an interlaced image}
  131. procedure TChunkIDAT.DecodeInterlacedAdam7(Stream: TStream;
  132.   var ZLIBStream: TZStreamRec2; const Size: Integer; var crcfile: Cardinal);
  133. var
  134.   CurrentPass: Byte;
  135.   PixelsThisRow: Integer;
  136.   CurrentRow: Integer;
  137.   Trans, Data{$IFDEF Store16bits}, Extra{$ENDIF}: pChar;
  138.   CopyProc: procedure(const Pass: Byte; Src, Dest,
  139.     Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar) of object;
  140. begin
  141.   CopyProc := nil; {Initialize}
  142.   {Determine method to copy the image data}
  143.   case Header.ColorType of
  144.     {R, G, B values for each pixel}
  145.     COLOR_RGB:
  146.       case Header.BitDepth of
  147.         8:  CopyProc := CopyInterlacedRGB8;
  148.        16:  CopyProc := CopyInterlacedRGB16;
  149.       end {case Header.BitDepth};
  150.     {Palette}
  151.     COLOR_PALETTE, COLOR_GRAYSCALE:
  152.       case Header.BitDepth of
  153.         1, 4, 8: CopyProc := CopyInterlacedPalette148;
  154.         2      : if Header.ColorType = COLOR_PALETTE then
  155.                    CopyProc := CopyInterlacedPalette2
  156.                  else
  157.                    CopyProc := CopyInterlacedGray2;
  158.         16     : CopyProc := CopyInterlacedGrayscale16;
  159.       end;
  160.     {RGB followed by alpha}
  161.     COLOR_RGBALPHA:
  162.       case Header.BitDepth of
  163.         8:  CopyProc := CopyInterlacedRGBAlpha8;
  164.        16:  CopyProc := CopyInterlacedRGBAlpha16;
  165.       end;
  166.     {Grayscale followed by alpha}
  167.     COLOR_GRAYSCALEALPHA:
  168.       case Header.BitDepth of
  169.         8:  CopyProc := CopyInterlacedGrayscaleAlpha8;
  170.        16:  CopyProc := CopyInterlacedGrayscaleAlpha16;
  171.       end;
  172.   end {case Header.ColorType};
  173.   {Adam7 method has 7 passes to make the final image}
  174.   FOR CurrentPass := 0 TO 6 DO
  175.   begin
  176.     {Calculates the number of pixels and bytes for this pass row}
  177.     PixelsThisRow := (ImageWidth - ColumnStart[CurrentPass] +
  178.       ColumnIncrement[CurrentPass] - 1) div ColumnIncrement[CurrentPass];
  179.     Row_Bytes := BytesForPixels(PixelsThisRow, Header.ColorType,
  180.       Header.BitDepth);
  181.     {Clear buffer for this pass}
  182.     ZeroMemory(Row_Buffer[not RowUsed], Row_Bytes);
  183.     {Get current row index}
  184.     CurrentRow := RowStart[CurrentPass];
  185.     {Get a pointer to the current row image data}
  186.     Data := Ptr(Longint(Header.ImageData) + Header.BytesPerRow *
  187.       (ImageHeight - 1 - CurrentRow));
  188.     Trans := Ptr(Longint(Header.ImageAlpha) + ImageWidth * CurrentRow);
  189.     {$IFDEF Store16bits}
  190.     Extra := Ptr(Longint(Header.ExtraImageData) + Header.BytesPerRow *
  191.       (ImageHeight - 1 - CurrentRow));
  192.     {$ENDIF}
  193.     if Row_Bytes > 0 then {There must have bytes for this interlaced pass}
  194.       while CurrentRow < ImageHeight do
  195.       begin
  196.         {Reads this line and filter}
  197.         if IDATZlibRead(ZLIBStream, @Row_Buffer[RowUsed][0], Row_Bytes + 1,
  198.           EndPos, CRCFile) = 0 then break;
  199.         FilterRow;
  200.         {Copy image data}
  201.         CopyProc(CurrentPass, @Row_Buffer[RowUsed][1], Data, Trans
  202.           {$IFDEF Store16bits}, Extra{$ENDIF});
  203.         {Use the other RowBuffer item}
  204.         RowUsed := not RowUsed;
  205.         {Move to the next row}
  206.         inc(CurrentRow, RowIncrement[CurrentPass]);
  207.         {Move pointer to the next line}
  208.         dec(Data, RowIncrement[CurrentPass] * Header.BytesPerRow);
  209.         inc(Trans, RowIncrement[CurrentPass] * ImageWidth);
  210.         {$IFDEF Store16bits}
  211.         dec(Extra, RowIncrement[CurrentPass] * Header.BytesPerRow);
  212.         {$ENDIF}
  213.       end {while CurrentRow < ImageHeight};
  214.   end {FOR CurrentPass};
  215. end;
  216. {Copy 8 bits RGB image}
  217. procedure TChunkIDAT.CopyNonInterlacedRGB8(
  218.   Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
  219. var
  220.   I: Integer;
  221. begin
  222.   FOR I := 1 TO ImageWidth DO
  223.   begin
  224.     {Copy pixel values}
  225.     Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest);
  226.     Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Dest);
  227.     Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src)    )^]; inc(Dest);
  228.     {Move to next pixel}
  229.     inc(Src, 3);
  230.   end {for I}
  231. end;
  232. {Copy 16 bits RGB image}
  233. procedure TChunkIDAT.CopyNonInterlacedRGB16(
  234.   Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
  235. var
  236.   I: Integer;
  237. begin
  238.   FOR I := 1 TO ImageWidth DO
  239.   begin
  240.     //Since windows does not supports 2 bytes for
  241.     //each R, G, B value, the method will read only 1 byte from it
  242.     {Copy pixel values}
  243.     Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 4)^]; inc(Dest);
  244.     Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest);
  245.     Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src)    )^]; inc(Dest);
  246.     {$IFDEF Store16bits}
  247.     {Copy extra pixel values}
  248.     Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 5)^]; inc(Extra);
  249.     Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 3)^]; inc(Extra);
  250.     Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Extra);
  251.     {$ENDIF}
  252.     {Move to next pixel}
  253.     inc(Src, 6);
  254.   end {for I}
  255. end;
  256. {Copy types using palettes (1, 4 or 8 bits per pixel)}
  257. procedure TChunkIDAT.CopyNonInterlacedPalette148(
  258.   Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
  259. begin
  260.   {It's simple as copying the data}
  261.   CopyMemory(Dest, Src, Row_Bytes);
  262. end;
  263. {Copy grayscale types using 2 bits for each pixel}
  264. procedure TChunkIDAT.CopyNonInterlacedGray2(
  265.   Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
  266. var
  267.   i: Integer;
  268. begin
  269.   {2 bits is not supported, this routine will converted into 4 bits}
  270.   FOR i := 1 TO Row_Bytes do
  271.   begin
  272.     Byte(Dest^) := ((Byte(Src^) shr 2) and $F) or ((Byte(Src^)) and $F0); inc(Dest);
  273.     Byte(Dest^) := ((Byte(Src^) shl 2) and $F) or ((Byte(Src^) shl 4) and $F0); inc(Dest);
  274.     inc(Src);
  275.   end {FOR i}
  276. end;
  277. {Copy types using palette with 2 bits for each pixel}
  278. procedure TChunkIDAT.CopyNonInterlacedPalette2(
  279.   Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
  280. var
  281.   i: Integer;
  282. begin
  283.   {2 bits is not supported, this routine will converted into 4 bits}
  284.   FOR i := 1 TO Row_Bytes do
  285.   begin
  286.     Byte(Dest^) := ((Byte(Src^) shr 4) and $3) or ((Byte(Src^) shr 2) and $30); inc(Dest);
  287.     Byte(Dest^) := (Byte(Src^) and $3) or ((Byte(Src^) shl 2) and $30); inc(Dest);
  288.     inc(Src);
  289.   end {FOR i}
  290. end;
  291. {Copy grayscale images with 16 bits}
  292. procedure TChunkIDAT.CopyNonInterlacedGrayscale16(
  293.   Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
  294. var
  295.   I: Integer;
  296. begin
  297.   FOR I := 1 TO ImageWidth DO
  298.   begin
  299.     {Windows does not supports 16 bits for each pixel in grayscale}
  300.     {mode, so reduce to 8}
  301.     Dest^ := Src^; inc(Dest);
  302.     {$IFDEF Store16bits}
  303.     Extra^ := pChar(Longint(Src) + 1)^; inc(Extra);
  304.     {$ENDIF}
  305.     {Move to next pixel}
  306.     inc(Src, 2);
  307.   end {for I}
  308. end;
  309. {Copy 8 bits per sample RGB images followed by an alpha byte}
  310. procedure TChunkIDAT.CopyNonInterlacedRGBAlpha8(
  311.   Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
  312. var
  313.   i: Integer;
  314. begin
  315.   FOR I := 1 TO ImageWidth DO
  316.   begin
  317.     {Copy pixel values and transparency}
  318.     Trans^ := pChar(Longint(Src) + 3)^;
  319.     Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest);
  320.     Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Dest);
  321.     Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src)    )^]; inc(Dest);
  322.     {Move to next pixel}
  323.     inc(Src, 4); inc(Trans);
  324.   end {for I}
  325. end;
  326. {Copy 16 bits RGB image with alpha using 2 bytes for each sample}
  327. procedure TChunkIDAT.CopyNonInterlacedRGBAlpha16(
  328.   Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
  329. var
  330.   I: Integer;
  331. begin
  332.   FOR I := 1 TO ImageWidth DO
  333.   begin
  334.     //Copy rgb and alpha values (transforming from 16 bits to 8 bits)
  335.     {Copy pixel values}
  336.     Trans^ := pChar(Longint(Src) + 6)^;
  337.     Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src) + 4)^]; inc(Dest);
  338.     Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest);
  339.     Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src)    )^]; inc(Dest);
  340.     {$IFDEF Store16bits}
  341.     {Copy extra pixel values}
  342.     Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 5)^]; inc(Extra);
  343.     Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 3)^]; inc(Extra);
  344.     Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Extra);
  345.     {$ENDIF}
  346.     {Move to next pixel}
  347.     inc(Src, 8); inc(Trans);
  348.   end {for I}
  349. end;
  350. {Copy 8 bits per sample grayscale followed by alpha}
  351. procedure TChunkIDAT.CopyNonInterlacedGrayscaleAlpha8(
  352.   Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
  353. var
  354.   I: Integer;
  355. begin
  356.   FOR I := 1 TO ImageWidth DO
  357.   begin
  358.     {Copy alpha value and then gray value}
  359.     Dest^  := Src^;  inc(Src);
  360.     Trans^ := Src^;  inc(Src);
  361.     inc(Dest); inc(Trans);
  362.   end;
  363. end;
  364. {Copy 16 bits per sample grayscale followed by alpha}
  365. procedure TChunkIDAT.CopyNonInterlacedGrayscaleAlpha16(
  366.   Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
  367. var
  368.   I: Integer;
  369. begin
  370.   FOR I := 1 TO ImageWidth DO
  371.   begin
  372.     {Copy alpha value and then gray value}
  373.     {$IFDEF Store16bits}
  374.     Extra^ := pChar(Longint(Src) + 1)^; inc(Extra);
  375.     {$ENDIF}
  376.     Dest^  := Src^;  inc(Src, 2);
  377.     Trans^ := Src^;  inc(Src, 2);
  378.     inc(Dest); inc(Trans);
  379.   end;
  380. end;
  381. {Decode non interlaced image}
  382. procedure TChunkIDAT.DecodeNonInterlaced(Stream: TStream;
  383.   var ZLIBStream: TZStreamRec2; const Size: Integer; var crcfile: Cardinal);
  384. var
  385.   j: Cardinal;
  386.   Trans, Data{$IFDEF Store16bits}, Extra{$ENDIF}: pChar;
  387.   CopyProc: procedure(
  388.     Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar) of object;
  389. begin
  390.   CopyProc := nil; {Initialize}
  391.   {Determines the method to copy the image data}
  392.   case Header.ColorType of
  393.     {R, G, B values}
  394.     COLOR_RGB:
  395.       case Header.BitDepth of
  396.         8: CopyProc := CopyNonInterlacedRGB8;
  397.        16: CopyProc := CopyNonInterlacedRGB16;
  398.       end;
  399.     {Types using palettes}
  400.     COLOR_PALETTE, COLOR_GRAYSCALE:
  401.       case Header.BitDepth of
  402.         1, 4, 8: CopyProc := CopyNonInterlacedPalette148;
  403.         2      : if Header.ColorType = COLOR_PALETTE then
  404.                    CopyProc := CopyNonInterlacedPalette2
  405.                  else
  406.                    CopyProc := CopyNonInterlacedGray2;
  407.         16     : CopyProc := CopyNonInterlacedGrayscale16;
  408.       end;
  409.     {R, G, B followed by alpha}
  410.     COLOR_RGBALPHA:
  411.       case Header.BitDepth of
  412.         8  : CopyProc := CopyNonInterlacedRGBAlpha8;
  413.        16  : CopyProc := CopyNonInterlacedRGBAlpha16;
  414.       end;
  415.     {Grayscale followed by alpha}
  416.     COLOR_GRAYSCALEALPHA:
  417.       case Header.BitDepth of
  418.         8  : CopyProc := CopyNonInterlacedGrayscaleAlpha8;
  419.        16  : CopyProc := CopyNonInterlacedGrayscaleAlpha16;
  420.       end;
  421.   end;
  422.   {Get the image data pointer}
  423.   Longint(Data) := Longint(Header.ImageData) +
  424.     Header.BytesPerRow * (ImageHeight - 1);
  425.   Trans := Header.ImageAlpha;
  426.   {$IFDEF Store16bits}
  427.   Longint(Extra) := Longint(Header.ExtraImageData) +
  428.     Header.BytesPerRow * (ImageHeight - 1);
  429.   {$ENDIF}
  430.   {Reads each line}
  431.   FOR j := 0 to ImageHeight - 1 do
  432.   begin
  433.     {Read this line Row_Buffer[RowUsed][0] if the filter type for this line}
  434.     if IDATZlibRead(ZLIBStream, @Row_Buffer[RowUsed][0], Row_Bytes + 1, EndPos,
  435.       CRCFile) = 0 then break;
  436.     {Filter the current row}
  437.     FilterRow;
  438.     {Copies non interlaced row to image}
  439.     CopyProc(@Row_Buffer[RowUsed][1], Data, Trans{$IFDEF Store16bits}, Extra
  440.       {$ENDIF});
  441.     {Invert line used}
  442.     RowUsed := not RowUsed;
  443.     dec(Data, Header.BytesPerRow);
  444.     {$IFDEF Store16bits}dec(Extra, Header.BytesPerRow);{$ENDIF}
  445.     inc(Trans, ImageWidth);
  446.   end {for I};
  447. end;
  448. {Filter the current line}
  449. procedure TChunkIDAT.FilterRow;
  450. var
  451.   pp: Byte;
  452.   vv, left, above, aboveleft: Integer;
  453.   Col: Cardinal;
  454. begin
  455.   {Test the filter}
  456.   case Row_Buffer[RowUsed]^[0] of
  457.     {No filtering for this line}
  458.     FILTER_NONE: begin end;
  459.     {AND 255 serves only to never let the result be larger than one byte}
  460.     {Sub filter}
  461.     FILTER_SUB:
  462.       FOR Col := Offset + 1 to Row_Bytes DO
  463.         Row_Buffer[RowUsed][Col] := (Row_Buffer[RowUsed][Col] +
  464.           Row_Buffer[RowUsed][Col - Offset]) and 255;
  465.     {Up filter}
  466.     FILTER_UP:
  467.       FOR Col := 1 to Row_Bytes DO
  468.         Row_Buffer[RowUsed][Col] := (Row_Buffer[RowUsed][Col] +
  469.           Row_Buffer[not RowUsed][Col]) and 255;
  470.     {Average filter}
  471.     FILTER_AVERAGE:
  472.       FOR Col := 1 to Row_Bytes DO
  473.       begin
  474.         {Obtains up and left pixels}
  475.         above := Row_Buffer[not RowUsed][Col];
  476.         if col - 1 < Offset then
  477.           left := 0
  478.         else
  479.           Left := Row_Buffer[RowUsed][Col - Offset];
  480.         {Calculates}
  481.         Row_Buffer[RowUsed][Col] := (Row_Buffer[RowUsed][Col] +
  482.           (left + above) div 2) and 255;
  483.       end;
  484.     {Paeth filter}
  485.     FILTER_PAETH:
  486.     begin
  487.       {Initialize}
  488.       left := 0;
  489.       aboveleft := 0;
  490.       {Test each byte}
  491.       FOR Col := 1 to Row_Bytes DO
  492.       begin
  493.         {Obtains above pixel}
  494.         above := Row_Buffer[not RowUsed][Col];
  495.         {Obtains left and top-left pixels}
  496.         if (col - 1 >= offset) Then
  497.         begin
  498.           left := row_buffer[RowUsed][col - offset];
  499.           aboveleft := row_buffer[not RowUsed][col - offset];
  500.         end;
  501.         {Obtains current pixel and paeth predictor}
  502.         vv := row_buffer[RowUsed][Col];
  503.         pp := PaethPredictor(left, above, aboveleft);
  504.         {Calculates}
  505.         Row_Buffer[RowUsed][Col] := (pp + vv) and $FF;
  506.       end {for};
  507.     end;
  508.       
  509.   end {case};
  510. end;
  511. {Reads the image data from the stream}
  512. function TChunkIDAT.LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
  513.   Size: Integer): Boolean;
  514. var
  515.   ZLIBStream: TZStreamRec2;
  516.   CRCCheck,
  517.   CRCFile  : Cardinal;
  518. begin
  519.   {Get pointer to the header chunk}
  520.   Header := Owner.Chunks.Item[0] as TChunkIHDR;
  521.   {Build palette if necessary}
  522.   if Header.HasPalette then PreparePalette();
  523.   {Copy image width and height}
  524.   ImageWidth := Header.Width;
  525.   ImageHeight := Header.Height;
  526.   {Initialize to calculate CRC}
  527.   {$IFDEF CheckCRC}
  528.     CRCFile := update_crc($ffffffff, @ChunkName[0], 4);
  529.   {$ENDIF}
  530.   Owner.GetPixelInfo(Row_Bytes, Offset); {Obtain line information}
  531.   ZLIBStream := ZLIBInitInflate(Stream);  {Initializes decompression}
  532.   {Calculate ending position for the current IDAT chunk}
  533.   EndPos := Stream.Position + Size;
  534.   {Allocate memory}
  535.   GetMem(Row_Buffer[false], Row_Bytes + 1);
  536.   GetMem(Row_Buffer[true], Row_Bytes + 1);
  537.   ZeroMemory(Row_Buffer[false], Row_bytes + 1);
  538.   {Set the variable to alternate the Row_Buffer item to use}
  539.   RowUsed := TRUE;
  540.   {Call special methods for the different interlace methods}
  541.   case Owner.InterlaceMethod of
  542.     imNone:  DecodeNonInterlaced(stream, ZLIBStream, Size, crcfile);
  543.     imAdam7: DecodeInterlacedAdam7(stream, ZLIBStream, size, crcfile);
  544.   end;
  545.   {Free memory}
  546.   ZLIBTerminateInflate(ZLIBStream); {Terminates decompression}
  547.   FreeMem(Row_Buffer[False], Row_Bytes + 1);
  548.   FreeMem(Row_Buffer[True], Row_Bytes + 1);
  549.   {Now checks CRC}
  550.   Stream.Read(CRCCheck, 4);
  551.   {$IFDEF CheckCRC}
  552.     CRCFile := CRCFile xor $ffffffff;
  553.     CRCCheck := ByteSwap(CRCCheck);
  554.     Result := CRCCheck = CRCFile;
  555.     {Handle CRC error}
  556.     if not Result then
  557.     begin
  558.       {In case it coult not load chunk}
  559.       Owner.RaiseError(EPngInvalidCRC, EPngInvalidCRCText);
  560.       exit;
  561.     end;
  562.   {$ELSE}Result := TRUE; {$ENDIF}
  563. end;
  564. const
  565.   IDATHeader: Array[0..3] of char = ('I', 'D', 'A', 'T');
  566.   BUFFER = 5;
  567. {Saves the IDAT chunk to a stream}
  568. function TChunkIDAT.SaveToStream(Stream: TStream): Boolean;
  569. var
  570.   ZLIBStream : TZStreamRec2;
  571. begin
  572.   {Get pointer to the header chunk}
  573.   Header := Owner.Chunks.Item[0] as TChunkIHDR;
  574.   {Copy image width and height}
  575.   ImageWidth := Header.Width;
  576.   ImageHeight := Header.Height;
  577.   Owner.GetPixelInfo(Row_Bytes, Offset); {Obtain line information}
  578.   {Allocate memory}
  579.   GetMem(Encode_Buffer[BUFFER], Row_Bytes);
  580.   ZeroMemory(Encode_Buffer[BUFFER], Row_Bytes);
  581.   {Allocate buffers for the filters selected}
  582.   {Filter none will always be calculated to the other filters to work}
  583.   GetMem(Encode_Buffer[FILTER_NONE], Row_Bytes);
  584.   ZeroMemory(Encode_Buffer[FILTER_NONE], Row_Bytes);
  585.   if pfSub in Owner.Filters then
  586.     GetMem(Encode_Buffer[FILTER_SUB], Row_Bytes);
  587.   if pfUp in Owner.Filters then
  588.     GetMem(Encode_Buffer[FILTER_UP], Row_Bytes);
  589.   if pfAverage in Owner.Filters then
  590.     GetMem(Encode_Buffer[FILTER_AVERAGE], Row_Bytes);
  591.   if pfPaeth in Owner.Filters then
  592.     GetMem(Encode_Buffer[FILTER_PAETH], Row_Bytes);
  593.   {Initialize ZLIB}
  594.   ZLIBStream := ZLIBInitDeflate(Stream, Owner.fCompressionLevel,
  595.     Owner.MaxIdatSize);
  596.   {Write data depending on the interlace method}
  597.   case Owner.InterlaceMethod of
  598.     imNone: EncodeNonInterlaced(stream, ZLIBStream);
  599.     imAdam7: EncodeInterlacedAdam7(stream, ZLIBStream);
  600.   end;
  601.   {Terminates ZLIB}
  602.   ZLIBTerminateDeflate(ZLIBStream);
  603.   {Release allocated memory}
  604.   FreeMem(Encode_Buffer[BUFFER], Row_Bytes);
  605.   FreeMem(Encode_Buffer[FILTER_NONE], Row_Bytes);
  606.   if pfSub in Owner.Filters then
  607.     FreeMem(Encode_Buffer[FILTER_SUB], Row_Bytes);
  608.   if pfUp in Owner.Filters then
  609.     FreeMem(Encode_Buffer[FILTER_UP], Row_Bytes);
  610.   if pfAverage in Owner.Filters then
  611.     FreeMem(Encode_Buffer[FILTER_AVERAGE], Row_Bytes);
  612.   if pfPaeth in Owner.Filters then
  613.     FreeMem(Encode_Buffer[FILTER_PAETH], Row_Bytes);
  614.   {Everything went ok}
  615.   Result := True;
  616. end;
  617. {Writes the IDAT using the settings}
  618. procedure WriteIDAT(Stream: TStream; Data: Pointer; const Length: Cardinal);
  619. var
  620.   ChunkLen, CRC: Cardinal;
  621. begin
  622.   {Writes IDAT header}
  623.   ChunkLen := ByteSwap(Length);
  624.   Stream.Write(ChunkLen, 4);                      {Chunk length}
  625.   Stream.Write(IDATHeader[0], 4);                 {Idat header}
  626.   CRC := update_crc($ffffffff, @IDATHeader[0], 4); {Crc part for header}
  627.   {Writes IDAT data and calculates CRC for data}
  628.   Stream.Write(Data^, Length);
  629.   CRC := Byteswap(update_crc(CRC, Data, Length) xor $ffffffff);
  630.   {Writes final CRC}
  631.   Stream.Write(CRC, 4);
  632. end;
  633. {Compress and writes IDAT chunk data}
  634. procedure TChunkIDAT.IDATZlibWrite(var ZLIBStream: TZStreamRec2;
  635.   Buffer: Pointer; const Length: Cardinal);
  636. begin
  637.   with ZLIBStream, ZLIBStream.ZLIB do
  638.   begin
  639.     {Set data to be compressed}
  640.     next_in := Buffer;
  641.     avail_in := Length;
  642.     {Compress all the data avaliable to compress}
  643.     while avail_in > 0 do
  644.     begin
  645.       deflate(ZLIB, Z_NO_FLUSH);
  646.       {The whole buffer was used, save data to stream and restore buffer}
  647.       if avail_out = 0 then
  648.       begin
  649.         {Writes this IDAT chunk}
  650.         WriteIDAT(fStream, Data, Owner.MaxIdatSize);
  651.         {Restore buffer}
  652.         next_out := Data;
  653.         avail_out := Owner.MaxIdatSize;
  654.       end {if avail_out = 0};
  655.     end {while avail_in};
  656.   end {with ZLIBStream, ZLIBStream.ZLIB}
  657. end;
  658. {Finishes compressing data to write IDAT chunk}
  659. procedure TChunkIDAT.FinishIDATZlib(var ZLIBStream: TZStreamRec2);
  660. begin
  661.   with ZLIBStream, ZLIBStream.ZLIB do
  662.   begin
  663.     {Set data to be compressed}
  664.     next_in := nil;
  665.     avail_in := 0;
  666.     while deflate(ZLIB,Z_FINISH) <> Z_STREAM_END do
  667.     begin
  668.       {Writes this IDAT chunk}
  669.       WriteIDAT(fStream, Data, Owner.MaxIdatSize - avail_out);
  670.       {Re-update buffer}
  671.       next_out := Data;
  672.       avail_out := Owner.MaxIdatSize;
  673.     end;
  674.     if avail_out < Owner.MaxIdatSize then
  675.       {Writes final IDAT}
  676.       WriteIDAT(fStream, Data, Owner.MaxIdatSize - avail_out);
  677.   end {with ZLIBStream, ZLIBStream.ZLIB};
  678. end;
  679. {Copy memory to encode RGB image with 1 byte for each color sample}
  680. procedure TChunkIDAT.EncodeNonInterlacedRGB8(Src, Dest, Trans: pChar);
  681. var
  682.   I: Integer;
  683. begin
  684.   FOR I := 1 TO ImageWidth DO
  685.   begin
  686.     {Copy pixel values}
  687.     Byte(Dest^) := fOwner.InverseGamma[pByte(Longint(Src) + 2)^]; inc(Dest);
  688.     Byte(Dest^) := fOwner.InverseGamma[pByte(Longint(Src) + 1)^]; inc(Dest);
  689.     Byte(Dest^) := fOwner.InverseGamma[pByte(Longint(Src)    )^]; inc(Dest);
  690.     {Move to next pixel}
  691.     inc(Src, 3);
  692.   end {for I}
  693. end;
  694. {Copy memory to encode RGB images with 16 bits for each color sample}
  695. procedure TChunkIDAT.EncodeNonInterlacedRGB16(Src, Dest, Trans: pChar);
  696. var
  697.   I: Integer;
  698. begin
  699.   FOR I := 1 TO ImageWidth DO
  700.   begin
  701.     //Now we copy from 1 byte for each sample stored to a 2 bytes (or 1 word)
  702.     //for sample
  703.     {Copy pixel values}
  704.     pWORD(Dest)^ := fOwner.InverseGamma[pByte(Longint(Src) + 2)^]; inc(Dest, 2);
  705.     pWORD(Dest)^ := fOwner.InverseGamma[pByte(Longint(Src) + 1)^]; inc(Dest, 2);
  706.     pWORD(Dest)^ := fOwner.InverseGamma[pByte(Longint(Src)    )^]; inc(Dest, 2);
  707.     {Move to next pixel}
  708.     inc(Src, 3);
  709.   end {for I}
  710. end;
  711. {Copy memory to encode types using palettes (1, 4 or 8 bits per pixel)}
  712. procedure TChunkIDAT.EncodeNonInterlacedPalette148(Src, Dest, Trans: pChar);
  713. begin
  714.   {It's simple as copying the data}
  715.   CopyMemory(Dest, Src, Row_Bytes);
  716. end;
  717. {Copy memory to encode grayscale images with 2 bytes for each sample}
  718. procedure TChunkIDAT.EncodeNonInterlacedGrayscale16(Src, Dest, Trans: pChar);
  719. var
  720.   I: Integer;
  721. begin
  722.   FOR I := 1 TO ImageWidth DO
  723.   begin
  724.     //Now we copy from 1 byte for each sample stored to a 2 bytes (or 1 word)
  725.     //for sample
  726.     pWORD(Dest)^ := pByte(Longint(Src))^; inc(Dest, 2);
  727.     {Move to next pixel}
  728.     inc(Src);
  729.   end {for I}
  730. end;
  731. {Encode images using RGB followed by an alpha value using 1 byte for each}
  732. procedure TChunkIDAT.EncodeNonInterlacedRGBAlpha8(Src, Dest, Trans: pChar);
  733. var
  734.   i: Integer;
  735. begin
  736.   {Copy the data to the destination, including data from Trans pointer}
  737.   FOR i := 1 TO ImageWidth do
  738.   begin
  739.     Byte(Dest^) := Owner.InverseGamma[PByte(Longint(Src) + 2)^]; inc(Dest);
  740.     Byte(Dest^) := Owner.InverseGamma[PByte(Longint(Src) + 1)^]; inc(Dest);
  741.     Byte(Dest^) := Owner.InverseGamma[PByte(Longint(Src)    )^]; inc(Dest);
  742.     Dest^ := Trans^; inc(Dest);
  743.     inc(Src, 3); inc(Trans);
  744.   end {for i};
  745. end;
  746. {Encode images using RGB followed by an alpha value using 2 byte for each}
  747. procedure TChunkIDAT.EncodeNonInterlacedRGBAlpha16(Src, Dest, Trans: pChar);
  748. var
  749.   i: Integer;
  750. begin
  751.   {Copy the data to the destination, including data from Trans pointer}
  752.   FOR i := 1 TO ImageWidth do
  753.   begin
  754.     pWord(Dest)^ := Owner.InverseGamma[PByte(Longint(Src) + 2)^]; inc(Dest, 2);
  755.     pWord(Dest)^ := Owner.InverseGamma[PByte(Longint(Src) + 1)^]; inc(Dest, 2);
  756.     pWord(Dest)^ := Owner.InverseGamma[PByte(Longint(Src)    )^]; inc(Dest, 2);
  757.     pWord(Dest)^ := PByte(Longint(Trans)  )^; inc(Dest, 2);
  758.     inc(Src, 3); inc(Trans);
  759.   end {for i};
  760. end;
  761. {Encode grayscale images followed by an alpha value using 1 byte for each}
  762. procedure TChunkIDAT.EncodeNonInterlacedGrayscaleAlpha8(
  763.   Src, Dest, Trans: pChar);
  764. var
  765.   i: Integer;
  766. begin
  767.   {Copy the data to the destination, including data from Trans pointer}
  768.   FOR i := 1 TO ImageWidth do
  769.   begin
  770.     Dest^ := Src^; inc(Dest);
  771.     Dest^ := Trans^; inc(Dest);
  772.     inc(Src); inc(Trans);
  773.   end {for i};
  774. end;
  775. {Encode grayscale images followed by an alpha value using 2 byte for each}
  776. procedure TChunkIDAT.EncodeNonInterlacedGrayscaleAlpha16(
  777.   Src, Dest, Trans: pChar);
  778. var
  779.   i: Integer;
  780. begin
  781.   {Copy the data to the destination, including data from Trans pointer}
  782.   FOR i := 1 TO ImageWidth do
  783.   begin
  784.     pWord(Dest)^ := pByte(Src)^;    inc(Dest, 2);
  785.     pWord(Dest)^ := pByte(Trans)^;  inc(Dest, 2);
  786.     inc(Src); inc(Trans);
  787.   end {for i};
  788. end;
  789. {Encode non interlaced images}
  790. procedure TChunkIDAT.EncodeNonInterlaced(Stream: TStream;
  791.   var ZLIBStream: TZStreamRec2);
  792. var
  793.   {Current line}
  794.   j: Cardinal;
  795.   {Pointers to image data}
  796.   Data, Trans: PChar;
  797.   {Filter used for this line}
  798.   Filter: Byte;
  799.   {Method which will copy the data into the buffer}
  800.   CopyProc: procedure(Src, Dest, Trans: pChar) of object;
  801. begin
  802.   CopyProc := nil;  {Initialize to avoid warnings}
  803.   {Defines the method to copy the data to the buffer depending on}
  804.   {the image parameters}
  805.   case Header.ColorType of
  806.     {R, G, B values}
  807.     COLOR_RGB:
  808.       case Header.BitDepth of
  809.         8: CopyProc := EncodeNonInterlacedRGB8;
  810.        16: CopyProc := EncodeNonInterlacedRGB16;
  811.       end;
  812.     {Palette and grayscale values}
  813.     COLOR_GRAYSCALE, COLOR_PALETTE:
  814.       case Header.BitDepth of
  815.         1, 4, 8: CopyProc := EncodeNonInterlacedPalette148;
  816.              16: CopyProc := EncodeNonInterlacedGrayscale16;
  817.       end;
  818.     {RGB with a following alpha value}
  819.     COLOR_RGBALPHA:
  820.       case Header.BitDepth of
  821.           8: CopyProc := EncodeNonInterlacedRGBAlpha8;
  822.          16: CopyProc := EncodeNonInterlacedRGBAlpha16;
  823.       end;
  824.     {Grayscale images followed by an alpha}
  825.     COLOR_GRAYSCALEALPHA:
  826.       case Header.BitDepth of
  827.         8:  CopyProc := EncodeNonInterlacedGrayscaleAlpha8;
  828.        16:  CopyProc := EncodeNonInterlacedGrayscaleAlpha16;
  829.       end;
  830.   end {case Header.ColorType};
  831.   {Get the image data pointer}
  832.   Longint(Data) := Longint(Header.ImageData) +
  833.     Header.BytesPerRow * (ImageHeight - 1);
  834.   Trans := Header.ImageAlpha;
  835.   {Writes each line}
  836.   FOR j := 0 to ImageHeight - 1 do
  837.   begin
  838.     {Copy data into buffer}
  839.     CopyProc(Data, @Encode_Buffer[BUFFER][0], Trans);
  840.     {Filter data}
  841.     Filter := FilterToEncode;
  842.     {Compress data}
  843.     IDATZlibWrite(ZLIBStream, @Filter, 1);
  844.     IDATZlibWrite(ZLIBStream, @Encode_Buffer[Filter][0], Row_Bytes);
  845.     {Adjust pointers to the actual image data}
  846.     dec(Data, Header.BytesPerRow);
  847.     inc(Trans, ImageWidth);
  848.   end;
  849.   {Compress and finishes copying the remaining data}
  850.   FinishIDATZlib(ZLIBStream);
  851. end;
  852. {Copy memory to encode interlaced images using RGB value with 1 byte for}
  853. {each color sample}
  854. procedure TChunkIDAT.EncodeInterlacedRGB8(const Pass: Byte;
  855.   Src, Dest, Trans: pChar);
  856. var
  857.   Col: Integer;
  858. begin
  859.   {Get first column and enter in loop}
  860.   Col := ColumnStart[Pass];
  861.   Src := pChar(Longint(Src) + Col * 3);
  862.   repeat
  863.     {Copy this row}
  864.     Byte(Dest^) := fOwner.InverseGamma[pByte(Longint(Src) + 2)^]; inc(Dest);
  865.     Byte(Dest^) := fOwner.InverseGamma[pByte(Longint(Src) + 1)^]; inc(Dest);
  866.     Byte(Dest^) := fOwner.InverseGamma[pByte(Longint(Src)    )^]; inc(Dest);
  867.     {Move to next column}
  868.     inc(Src, ColumnIncrement[Pass] * 3);
  869.     inc(Col, ColumnIncrement[Pass]);
  870.   until Col >= ImageWidth;
  871. end;
  872. {Copy memory to encode interlaced RGB images with 2 bytes each color sample}
  873. procedure TChunkIDAT.EncodeInterlacedRGB16(const Pass: Byte;
  874.   Src, Dest, Trans: pChar);
  875. var
  876.   Col: Integer;
  877. begin
  878.   {Get first column and enter in loop}
  879.   Col := ColumnStart[Pass];
  880.   Src := pChar(Longint(Src) + Col * 3);
  881.   repeat
  882.     {Copy this row}
  883.     pWord(Dest)^ := Owner.InverseGamma[pByte(Longint(Src) + 2)^]; inc(Dest, 2);
  884.     pWord(Dest)^ := Owner.InverseGamma[pByte(Longint(Src) + 1)^]; inc(Dest, 2);
  885.     pWord(Dest)^ := Owner.InverseGamma[pByte(Longint(Src)    )^]; inc(Dest, 2);
  886.     {Move to next column}
  887.     inc(Src, ColumnIncrement[Pass] * 3);
  888.     inc(Col, ColumnIncrement[Pass]);
  889.   until Col >= ImageWidth;
  890. end;
  891. {Copy memory to encode interlaced images using palettes using bit depths}
  892. {1, 4, 8 (each pixel in the image)}
  893. procedure TChunkIDAT.EncodeInterlacedPalette148(const Pass: Byte;
  894.   Src, Dest, Trans: pChar);
  895. const
  896.   BitTable: Array[1..8] of Integer = ($1, $3, 0, $F, 0, 0, 0, $FF);
  897.   StartBit: Array[1..8] of Integer = (7 , 0 , 0, 4,  0, 0, 0, 0);
  898. var
  899.   CurBit, Col: Integer;
  900.   Src2: PChar;
  901. begin
  902.   {Clean the line}
  903.   fillchar(Dest^, Row_Bytes, #0);
  904.   {Get first column and enter in loop}
  905.   Col := ColumnStart[Pass];
  906.   with Header.BitmapInfo.bmiHeader do
  907.     repeat
  908.       {Copy data}
  909.       CurBit := StartBit[biBitCount];
  910.       repeat
  911.         {Adjust pointer to pixel byte bounds}
  912.         Src2 := pChar(Longint(Src) + (biBitCount * Col) div 8);
  913.         {Copy data}
  914.         Byte(Dest^) := Byte(Dest^) or
  915.           (((Byte(Src2^) shr (StartBit[Header.BitDepth] - (biBitCount * Col)
  916.             mod 8))) and (BitTable[biBitCount])) shl CurBit;
  917.         {Move to next column}
  918.         inc(Col, ColumnIncrement[Pass]);
  919.         {Will read next bits}
  920.         dec(CurBit, biBitCount);
  921.       until CurBit < 0;
  922.       {Move to next byte in source}
  923.       inc(Dest);
  924.     until Col >= ImageWidth;
  925. end;
  926. {Copy to encode interlaced grayscale images using 16 bits for each sample}
  927. procedure TChunkIDAT.EncodeInterlacedGrayscale16(const Pass: Byte;
  928.   Src, Dest, Trans: pChar);
  929. var
  930.   Col: Integer;
  931. begin
  932.   {Get first column and enter in loop}
  933.   Col := ColumnStart[Pass];
  934.   Src := pChar(Longint(Src) + Col);
  935.   repeat
  936.     {Copy this row}
  937.     pWord(Dest)^ := Byte(Src^); inc(Dest, 2);
  938.     {Move to next column}
  939.     inc(Src, ColumnIncrement[Pass]);
  940.     inc(Col, ColumnIncrement[Pass]);
  941.   until Col >= ImageWidth;
  942. end;
  943. {Copy to encode interlaced rgb images followed by an alpha value, all using}
  944. {one byte for each sample}
  945. procedure TChunkIDAT.EncodeInterlacedRGBAlpha8(const Pass: Byte;
  946.   Src, Dest, Trans: pChar);
  947. var
  948.   Col: Integer;
  949. begin
  950.   {Get first column and enter in loop}
  951.   Col := ColumnStart[Pass];
  952.   Src := pChar(Longint(Src) + Col * 3);
  953.   Trans := pChar(Longint(Trans) + Col);
  954.   repeat
  955.     {Copy this row}
  956.     Byte(Dest^) := Owner.InverseGamma[pByte(Longint(Src) + 2)^]; inc(Dest);
  957.     Byte(Dest^) := Owner.InverseGamma[pByte(Longint(Src) + 1)^]; inc(Dest);
  958.     Byte(Dest^) := Owner.InverseGamma[pByte(Longint(Src)    )^]; inc(Dest);
  959.     Dest^ := Trans^; inc(Dest);
  960.     {Move to next column}
  961.     inc(Src, ColumnIncrement[Pass] * 3);
  962.     inc(Trans, ColumnIncrement[Pass]);
  963.     inc(Col, ColumnIncrement[Pass]);
  964.   until Col >= ImageWidth;
  965. end;
  966. {Copy to encode interlaced rgb images followed by an alpha value, all using}
  967. {two byte for each sample}
  968. procedure TChunkIDAT.EncodeInterlacedRGBAlpha16(const Pass: Byte;
  969.   Src, Dest, Trans: pChar);
  970. var
  971.   Col: Integer;
  972. begin
  973.   {Get first column and enter in loop}
  974.   Col := ColumnStart[Pass];
  975.   Src := pChar(Longint(Src) + Col * 3);
  976.   Trans := pChar(Longint(Trans) + Col);
  977.   repeat
  978.     {Copy this row}
  979.     pWord(Dest)^ := pByte(Longint(Src) + 2)^; inc(Dest, 2);
  980.     pWord(Dest)^ := pByte(Longint(Src) + 1)^; inc(Dest, 2);
  981.     pWord(Dest)^ := pByte(Longint(Src)    )^; inc(Dest, 2);
  982.     pWord(Dest)^ := pByte(Trans)^; inc(Dest, 2);
  983.     {Move to next column}
  984.     inc(Src, ColumnIncrement[Pass] * 3);
  985.     inc(Trans, ColumnIncrement[Pass]);
  986.     inc(Col, ColumnIncrement[Pass]);
  987.   until Col >= ImageWidth;
  988. end;
  989. {Copy to encode grayscale interlaced images followed by an alpha value, all}
  990. {using 1 byte for each sample}
  991. procedure TChunkIDAT.EncodeInterlacedGrayscaleAlpha8(const Pass: Byte;
  992.   Src, Dest, Trans: pChar);
  993. var
  994.   Col: Integer;
  995. begin
  996.   {Get first column and enter in loop}
  997.   Col := ColumnStart[Pass];
  998.   Src := pChar(Longint(Src) + Col);
  999.   Trans := pChar(Longint(Trans) + Col);
  1000.   repeat
  1001.     {Copy this row}
  1002.     Dest^ := Src^;   inc(Dest);
  1003.     Dest^ := Trans^; inc(Dest);
  1004.     {Move to next column}
  1005.     inc(Src, ColumnIncrement[Pass]);
  1006.     inc(Trans, ColumnIncrement[Pass]);
  1007.     inc(Col, ColumnIncrement[Pass]);
  1008.   until Col >= ImageWidth;
  1009. end;
  1010. {Copy to encode grayscale interlaced images followed by an alpha value, all}
  1011. {using 2 bytes for each sample}
  1012. procedure TChunkIDAT.EncodeInterlacedGrayscaleAlpha16(const Pass: Byte;
  1013.   Src, Dest, Trans: pChar);
  1014. var
  1015.   Col: Integer;
  1016. begin
  1017.   {Get first column and enter in loop}
  1018.   Col := ColumnStart[Pass];
  1019.   Src := pChar(Longint(Src) + Col);
  1020.   Trans := pChar(Longint(Trans) + Col);
  1021.   repeat
  1022.     {Copy this row}
  1023.     pWord(Dest)^ := pByte(Src)^; inc(Dest, 2);
  1024.     pWord(Dest)^ := pByte(Trans)^; inc(Dest, 2);
  1025.     {Move to next column}
  1026.     inc(Src, ColumnIncrement[Pass]);
  1027.     inc(Trans, ColumnIncrement[Pass]);
  1028.     inc(Col, ColumnIncrement[Pass]);
  1029.   until Col >= ImageWidth;
  1030. end;
  1031. {Encode interlaced images}
  1032. procedure TChunkIDAT.EncodeInterlacedAdam7(Stream: TStream;
  1033.   var ZLIBStream: TZStreamRec2);
  1034. var
  1035.   CurrentPass, Filter: Byte;
  1036.   PixelsThisRow: Integer;
  1037.   CurrentRow : Integer;
  1038.   Trans, Data: pChar;
  1039.   CopyProc: procedure(const Pass: Byte;
  1040.     Src, Dest, Trans: pChar) of object;
  1041. begin
  1042.   CopyProc := nil;  {Initialize to avoid warnings}
  1043.   {Defines the method to copy the data to the buffer depending on}
  1044.   {the image parameters}
  1045.   case Header.ColorType of
  1046.     {R, G, B values}
  1047.     COLOR_RGB:
  1048.       case Header.BitDepth of
  1049.         8: CopyProc := EncodeInterlacedRGB8;
  1050.        16: CopyProc := EncodeInterlacedRGB16;
  1051.       end;
  1052.     {Grayscale and palette}
  1053.     COLOR_PALETTE, COLOR_GRAYSCALE:
  1054.       case Header.BitDepth of
  1055.         1, 4, 8: CopyProc := EncodeInterlacedPalette148;
  1056.              16: CopyProc := EncodeInterlacedGrayscale16;
  1057.       end;
  1058.     {RGB followed by alpha}
  1059.     COLOR_RGBALPHA:
  1060.       case Header.BitDepth of
  1061.           8: CopyProc := EncodeInterlacedRGBAlpha8;
  1062.          16: CopyProc := EncodeInterlacedRGBAlpha16;
  1063.       end;
  1064.     COLOR_GRAYSCALEALPHA:
  1065.     {Grayscale followed by alpha}
  1066.       case Header.BitDepth of
  1067.           8: CopyProc := EncodeInterlacedGrayscaleAlpha8;
  1068.          16: CopyProc := EncodeInterlacedGrayscaleAlpha16;
  1069.       end;
  1070.   end {case Header.ColorType};
  1071.   {Compress the image using the seven passes for ADAM 7}
  1072.   FOR CurrentPass := 0 TO 6 DO
  1073.   begin
  1074.     {Calculates the number of pixels and bytes for this pass row}
  1075.     PixelsThisRow := (ImageWidth - ColumnStart[CurrentPass] +
  1076.       ColumnIncrement[CurrentPass] - 1) div ColumnIncrement[CurrentPass];
  1077.     Row_Bytes := BytesForPixels(PixelsThisRow, Header.ColorType,
  1078.       Header.BitDepth);
  1079.     ZeroMemory(Encode_Buffer[FILTER_NONE], Row_Bytes);
  1080.     {Get current row index}
  1081.     CurrentRow := RowStart[CurrentPass];
  1082.     {Get a pointer to the current row image data}
  1083.     Data := Ptr(Longint(Header.ImageData) + Header.BytesPerRow *
  1084.       (ImageHeight - 1 - CurrentRow));
  1085.     Trans := Ptr(Longint(Header.ImageAlpha) + ImageWidth * CurrentRow);
  1086.     {Process all the image rows}
  1087.     if Row_Bytes > 0 then
  1088.       while CurrentRow < ImageHeight do
  1089.       begin
  1090.         {Copy data into buffer}
  1091.         CopyProc(CurrentPass, Data, @Encode_Buffer[BUFFER][0], Trans);
  1092.         {Filter data}
  1093.         Filter := FilterToEncode;
  1094.         {Compress data}
  1095.         IDATZlibWrite(ZLIBStream, @Filter, 1);
  1096.         IDATZlibWrite(ZLIBStream, @Encode_Buffer[Filter][0], Row_Bytes);
  1097.         {Move to the next row}
  1098.         inc(CurrentRow, RowIncrement[CurrentPass]);
  1099.         {Move pointer to the next line}
  1100.         dec(Data, RowIncrement[CurrentPass] * Header.BytesPerRow);
  1101.         inc(Trans, RowIncrement[CurrentPass] * ImageWidth);
  1102.       end {while CurrentRow < ImageHeight}
  1103.   end {CurrentPass};
  1104.   {Compress and finishes copying the remaining data}
  1105.   FinishIDATZlib(ZLIBStream);
  1106. end;
  1107. {Filters the row to be encoded and returns the best filter}
  1108. function TChunkIDAT.FilterToEncode: Byte;
  1109. var
  1110.   Run, LongestRun, ii, jj: Cardinal;
  1111.   Last, Above, LastAbove: Byte;
  1112. begin
  1113.   {Selecting more filters using the Filters property from TPngObject}
  1114.   {increases the chances to the file be much smaller, but decreases}
  1115.   {the performace}
  1116.   {This method will creates the same line data using the different}
  1117.   {filter methods and select the best}
  1118.   {Sub-filter}
  1119.   if pfSub in Owner.Filters then
  1120.     for ii := 0 to Row_Bytes - 1 do
  1121.     begin
  1122.       {There is no previous pixel when it's on the first pixel, so}
  1123.       {set last as zero when in the first}
  1124.       if (ii >= Offset) then
  1125.         last := Encode_Buffer[BUFFER]^[ii - Offset]
  1126.       else
  1127.         last := 0;
  1128.       Encode_Buffer[FILTER_SUB]^[ii] := Encode_Buffer[BUFFER]^[ii] - last;
  1129.     end;
  1130.   {Up filter}
  1131.   if pfUp in Owner.Filters then
  1132.     for ii := 0 to Row_Bytes - 1 do
  1133.       Encode_Buffer[FILTER_UP]^[ii] := Encode_Buffer[BUFFER]^[ii] -
  1134.         Encode_Buffer[FILTER_NONE]^[ii];
  1135.   {Average filter}
  1136.   if pfAverage in Owner.Filters then
  1137.     for ii := 0 to Row_Bytes - 1 do
  1138.     begin
  1139.       {Get the previous pixel, if the current pixel is the first, the}
  1140.       {previous is considered to be 0}
  1141.       if (ii >= Offset) then
  1142.         last := Encode_Buffer[BUFFER]^[ii - Offset]
  1143.       else
  1144.         last := 0;
  1145.       {Get the pixel above}
  1146.       above := Encode_Buffer[FILTER_NONE]^[ii];
  1147.       {Calculates formula to the average pixel}
  1148.       Encode_Buffer[FILTER_AVERAGE]^[ii] := Encode_Buffer[BUFFER]^[ii] -
  1149.         (above + last) div 2 ;
  1150.     end;
  1151.   {Paeth filter (the slower)}
  1152.   if pfPaeth in Owner.Filters then
  1153.   begin
  1154.     {Initialize}
  1155.     last := 0;
  1156.     lastabove := 0;
  1157.     for ii := 0 to Row_Bytes - 1 do
  1158.     begin
  1159.       {In case this pixel is not the first in the line obtains the}
  1160.       {previous one and the one above the previous}
  1161.       if (ii >= Offset) then
  1162.       begin
  1163.         last := Encode_Buffer[BUFFER]^[ii - Offset];
  1164.         lastabove := Encode_Buffer[FILTER_NONE]^[ii - Offset];
  1165.       end;
  1166.       {Obtains the pixel above}
  1167.       above := Encode_Buffer[FILTER_NONE]^[ii];
  1168.       {Calculate paeth filter for this byte}
  1169.       Encode_Buffer[FILTER_PAETH]^[ii] := Encode_Buffer[BUFFER]^[ii] -
  1170.         PaethPredictor(last, above, lastabove);
  1171.     end;
  1172.   end;
  1173.   {Now calculates the same line using no filter, which is necessary}
  1174.   {in order to have data to the filters when the next line comes}
  1175.   CopyMemory(@Encode_Buffer[FILTER_NONE]^[0],
  1176.     @Encode_Buffer[BUFFER]^[0], Row_Bytes);
  1177.   {If only filter none is selected in the filter list, we don't need}
  1178.   {to proceed and further}
  1179.   if (Owner.Filters = [pfNone]) or (Owner.Filters = []) then
  1180.   begin
  1181.     Result := FILTER_NONE;
  1182.     exit;
  1183.   end {if (Owner.Filters = [pfNone...};
  1184.   {Check which filter is the best by checking which has the larger}
  1185.   {sequence of the same byte, since they are best compressed}
  1186.   LongestRun := 0; Result := FILTER_NONE;
  1187.   for ii := FILTER_NONE TO FILTER_PAETH do
  1188.     {Check if this filter was selected}
  1189.     if TFilter(ii) in Owner.Filters then
  1190.     begin
  1191.       Run := 0;
  1192.       {Check if it's the only filter}
  1193.       if Owner.Filters = [TFilter(ii)] then
  1194.       begin
  1195.         Result := ii;
  1196.         exit;
  1197.       end;
  1198.       {Check using a sequence of four bytes}
  1199.       for jj := 2 to Row_Bytes - 1 do
  1200.         if (Encode_Buffer[ii]^[jj] = Encode_Buffer [ii]^[jj-1]) or
  1201.             (Encode_Buffer[ii]^[jj] = Encode_Buffer [ii]^[jj-2]) then
  1202.           inc(Run);  {Count the number of sequences}
  1203.       {Check if this one is the best so far}
  1204.       if (Run > LongestRun) then
  1205.       begin
  1206.         Result := ii;
  1207.         LongestRun := Run;
  1208.       end {if (Run > LongestRun)};
  1209.     end {if TFilter(ii) in Owner.Filters};
  1210. end;
  1211. {TChunkPLTE implementation}
  1212. {Returns an item in the palette}
  1213. function TChunkPLTE.GetPaletteItem(Index: Byte): TRGBQuad;
  1214. begin
  1215.   {Test if item is valid, if not raise error}
  1216.   if Index > Count - 1 then
  1217.     Owner.RaiseError(EPNGError, EPNGUnknownPalEntryText)
  1218.   else
  1219.     {Returns the item}
  1220.     Result := Header.BitmapInfo.bmiColors[Index];
  1221. end;
  1222. {Loads the palette chunk from a stream}
  1223. function TChunkPLTE.LoadFromStream(Stream: TStream;
  1224.   const ChunkName: TChunkName; Size: Integer): Boolean;
  1225. type
  1226.   pPalEntry = ^PalEntry;
  1227.   PalEntry = record
  1228.     r, g, b: Byte;
  1229.   end;
  1230. var
  1231.   j        : Integer;          {For the FOR}
  1232.   PalColor : pPalEntry;
  1233. begin
  1234.   {Let ancestor load data and check CRC}
  1235.   Result := inherited LoadFromStream(Stream, ChunkName, Size);
  1236.   if not Result then exit;
  1237.   {This chunk must be divisible by 3 in order to be valid}
  1238.   if (Size mod 3 <> 0) or (Size div 3 > 256) then
  1239.   begin
  1240.     {Raise error}
  1241.     Result := FALSE;
  1242.     Owner.RaiseError(EPNGInvalidPalette, EPNGInvalidPaletteText);
  1243.     exit;
  1244.   end {if Size mod 3 <> 0};
  1245.   {Fill array with the palette entries}
  1246.   fCount := Size div 3;
  1247.   PalColor := Data;
  1248.   FOR j := 0 TO fCount - 1 DO
  1249.     with Header.BitmapInfo.bmiColors[j] do
  1250.     begin
  1251.       rgbRed  :=  Owner.GammaTable[PalColor.r];
  1252.       rgbGreen := Owner.GammaTable[PalColor.g];
  1253.       rgbBlue :=  Owner.GammaTable[PalColor.b];
  1254.       rgbReserved := 0;
  1255.       {Move to next palette entry}
  1256.       inc(PalColor);
  1257.     end;
  1258. end;
  1259. {Saves the PLTE chunk to a stream}
  1260. function TChunkPLTE.SaveToStream(Stream: TStream): Boolean;
  1261. var
  1262.   J: Integer;
  1263.   DataPtr: pByte;
  1264. begin
  1265.   {Adjust size to hold all the palette items}
  1266.   ResizeData(fCount * 3);
  1267.   {Copy pointer to data}
  1268.   DataPtr := fData;
  1269.   {Copy palette items}
  1270.   with Header do
  1271.     FOR j := 0 TO fCount - 1 DO
  1272.       with BitmapInfo.bmiColors[j] do
  1273.       begin
  1274.         DataPtr^ := Owner.InverseGamma[rgbRed]; inc(DataPtr);
  1275.         DataPtr^ := Owner.InverseGamma[rgbGreen]; inc(DataPtr);
  1276.         DataPtr^ := Owner.InverseGamma[rgbBlue]; inc(DataPtr);
  1277.       end {with BitmapInfo};
  1278.   {Let ancestor do the rest of the work}
  1279.   Result := inherited SaveToStream(Stream);
  1280. end;
  1281. {Assigns from another PLTE chunk}
  1282. procedure TChunkPLTE.Assign(Source: TChunk);
  1283. begin
  1284.   {Copy the number of palette items}
  1285.   if Source is TChunkPLTE then
  1286.     fCount := TChunkPLTE(Source).fCount
  1287.   else
  1288.     Owner.RaiseError(EPNGError, EPNGCannotAssignChunkText);
  1289. end;
  1290. {TChunkgAMA implementation}
  1291. {Assigns from another chunk}
  1292. procedure TChunkgAMA.Assign(Source: TChunk);
  1293. begin
  1294.   {Copy the gamma value}
  1295.   if Source is TChunkgAMA then
  1296.     Gamma := TChunkgAMA(Source).Gamma
  1297.   else
  1298.     Owner.RaiseError(EPNGError, EPNGCannotAssignChunkText);
  1299. end;
  1300. {Gamma chunk being created}
  1301. constructor TChunkgAMA.Create(Owner: TPngObject);
  1302. begin
  1303.   {Call ancestor}
  1304.   inherited Create(Owner);
  1305.   Gamma := 1;  {Initial value}
  1306. end;
  1307. {Returns gamma value}
  1308. function TChunkgAMA.GetValue: Cardinal;
  1309. begin
  1310.   {Make sure that the size is four bytes}
  1311.   if DataSize <> 4 then
  1312.   begin
  1313.     {Adjust size and returns 1}
  1314.     ResizeData(4);
  1315.     Result := 1;
  1316.   end
  1317.   {If it's right, read the value}
  1318.   else Result := Cardinal(ByteSwap(pCardinal(Data)^))
  1319. end;
  1320. function Power(Base, Exponent: Extended): Extended;
  1321. begin
  1322.   if Exponent = 0.0 then
  1323.     Result := 1.0               {Math rule}
  1324.   else if (Base = 0) or (Exponent = 0) then Result := 0
  1325.   else
  1326.     Result := Exp(Exponent * Ln(Base));
  1327. end;
  1328. {Loading the chunk from a stream}
  1329. function TChunkgAMA.LoadFromStream(Stream: TStream;
  1330.   const ChunkName: TChunkName; Size: Integer): Boolean;
  1331. var
  1332.   i: Integer;
  1333.   Value: Cardinal;
  1334. begin
  1335.   {Call ancestor and test if it went ok}
  1336.   Result := inherited LoadFromStream(Stream, ChunkName, Size);
  1337.   if not Result then exit;
  1338.   Value := Gamma;
  1339.   {Build gamma table and inverse table for saving}
  1340.   if Value <> 0 then
  1341.     with Owner do
  1342.       FOR i := 0 TO 255 DO
  1343.       begin
  1344.         GammaTable[I] := Round(Power((I / 255), 1 /
  1345.           (Value / 100000 * 2.2)) * 255);
  1346.         InverseGamma[Round(Power((I / 255), 1 /
  1347.           (Value / 100000 * 2.2)) * 255)] := I;
  1348.       end
  1349. end;
  1350. {Sets the gamma value}
  1351. procedure TChunkgAMA.SetValue(const Value: Cardinal);
  1352. begin
  1353.   {Make sure that the size is four bytes}
  1354.   if DataSize <> 4 then ResizeData(4);
  1355.   {If it's right, set the value}
  1356.   pCardinal(Data)^ := ByteSwap(Value);
  1357. end;
  1358. {TPngObject implementation}
  1359. {Assigns from another object}
  1360. procedure TPngObject.Assign(Source: TPersistent);
  1361. begin
  1362.   {Being cleared}
  1363.   if Source = nil then
  1364.     ClearChunks
  1365.   {Assigns contents from another TPNGObject}
  1366.   else if Source is TPNGObject then
  1367.     AssignPNG(Source as TPNGObject)
  1368.   {Copy contents from a TBitmap}
  1369.   {$IFDEF UseDelphi}else if Source is TBitmap then
  1370.     with Source as TBitmap do
  1371.       AssignHandle(Handle, Transparent,
  1372.         ColorToRGB(TransparentColor)){$ENDIF}
  1373.   {Unknown source, let ancestor deal with it}
  1374.   else
  1375.     inherited;
  1376. end;
  1377. {Clear all the chunks in the list}
  1378. procedure TPngObject.ClearChunks;
  1379. var
  1380.   i: Integer;
  1381. begin
  1382.   {Initialize gamma}
  1383.   InitializeGamma();
  1384.   {Free all the objects and memory (0 chunks Bug fixed by Noel Sharpe)}
  1385.   for i := 0 TO Integer(Chunks.Count) - 1 do
  1386.     TChunk(Chunks.Item[i]).Free;
  1387.   Chunks.Count := 0;
  1388. end;
  1389. {Portable Network Graphics object being created}
  1390. constructor TPngObject.Create;
  1391. begin
  1392.   {Let it be created}
  1393.   inherited Create;
  1394.   {Initial properties}
  1395.   TempPalette := 0;
  1396.   fFilters := [pfSub];
  1397.   fCompressionLevel := 7;
  1398.   fInterlaceMethod := imNone;
  1399.   fMaxIdatSize := High(Word);
  1400.   {Create chunklist object}
  1401.   fChunkList := TPngList.Create(Self);
  1402. end;
  1403. {Portable Network Graphics object being destroyed}
  1404. destructor TPngObject.Destroy;
  1405. begin
  1406.   {Free object list}
  1407.   ClearChunks;
  1408.   fChunkList.Free;
  1409.   {Free the temporary palette}
  1410.   if TempPalette <> 0 then DeleteObject(TempPalette);
  1411.   {Call ancestor destroy}
  1412.   inherited Destroy;
  1413. end;
  1414. {Returns linesize and byte offset for pixels}
  1415. procedure TPngObject.GetPixelInfo(var LineSize, Offset: Cardinal);
  1416. begin
  1417.   {There must be an Header chunk to calculate size}
  1418.   if HeaderPresent then
  1419.   begin
  1420.     {Calculate number of bytes for each line}
  1421.     LineSize := BytesForPixels(Header.Width, Header.ColorType, Header.BitDepth);
  1422.     {Calculates byte offset}
  1423.     Case Header.ColorType of
  1424.       {Grayscale}
  1425.       COLOR_GRAYSCALE:
  1426.         If Header.BitDepth = 16 Then
  1427.           Offset := 2
  1428.         Else
  1429.           Offset := 1 ;
  1430.       {It always smaller or equal one byte, so it occupes one byte}
  1431.       COLOR_PALETTE:
  1432.         offset := 1;
  1433.       {It might be 3 or 6 bytes}
  1434.       COLOR_RGB:
  1435.         offset := 3 * Header.BitDepth Div 8;
  1436.       {It might be 2 or 4 bytes}
  1437.       COLOR_GRAYSCALEALPHA:
  1438.         offset := 2 * Header.BitDepth Div 8;
  1439.       {4 or 8 bytes}
  1440.       COLOR_RGBALPHA:
  1441.         offset := 4 * Header.BitDepth Div 8;
  1442.       else
  1443.         Offset := 0;
  1444.       End ;
  1445.   end
  1446.   else
  1447.   begin
  1448.     {In case if there isn't any Header chunk}
  1449.     Offset := 0;
  1450.     LineSize := 0;
  1451.   end;
  1452. end;
  1453. {Returns image height}
  1454. function TPngObject.GetHeight: Integer;
  1455. begin
  1456.   {There must be a Header chunk to get the size, otherwise returns 0}
  1457.   if HeaderPresent then
  1458.     Result := TChunkIHDR(Chunks.Item[0]).Height
  1459.   else Result := 0;
  1460. end;
  1461. {Returns image width}
  1462. function TPngObject.GetWidth: Integer;
  1463. begin
  1464.   {There must be a Header chunk to get the size, otherwise returns 0}
  1465.   if HeaderPresent then
  1466.     Result := Header.Width
  1467.   else Result := 0;
  1468. end;
  1469. {Returns if the image is empty}
  1470. function TPngObject.GetEmpty: Boolean;
  1471. begin
  1472.   Result := (Chunks.Count = 0);
  1473. end;
  1474. {Raises an error}
  1475. procedure TPngObject.RaiseError(ExceptionClass: ExceptClass; Text: String);
  1476. begin
  1477.   raise ExceptionClass.Create(Text);
  1478. end;
  1479. {Set the maximum size for IDAT chunk}
  1480. procedure TPngObject.SetMaxIdatSize(const Value: Integer);
  1481. begin
  1482.   {Make sure the size is at least 65535}
  1483.   if Value < High(Word) then
  1484.     fMaxIdatSize := High(Word) else fMaxIdatSize := Value;
  1485. end;
  1486. {$IFNDEF UseDelphi}
  1487.   {Creates a file stream reading from the filename in the parameter and load}
  1488.   procedure TPngObject.LoadFromFile(const Filename: String);
  1489.   var
  1490.     FileStream: TFileStream;
  1491.   begin
  1492.     {Test if the file exists}
  1493.     if not FileExists(Filename) then
  1494.     begin
  1495.       {In case it does not exists, raise error}
  1496.       RaiseError(EPNGNotExists, EPNGNotExistsText);
  1497.       exit;
  1498.     end;
  1499.     {Creates the file stream to read}
  1500.     FileStream := TFileStream.Create(Filename, [fsmRead]);
  1501.     LoadFromStream(FileStream);  {Loads the data}
  1502.     FileStream.Free;             {Free file stream}
  1503.   end;
  1504.   {Saves the current png image to a file}
  1505.   procedure TPngObject.SaveToFile(const Filename: String);
  1506.   var
  1507.     FileStream: TFileStream;
  1508.   begin
  1509.     {Creates the file stream to write}
  1510.     FileStream := TFileStream.Create(Filename, [fsmWrite]);
  1511.     SaveToStream(FileStream);    {Saves the data}
  1512.     FileStream.Free;             {Free file stream}
  1513.   end;
  1514. {$ENDIF}
  1515. {Returns pointer to the chunk TChunkIHDR which should be the first}
  1516. function TPngObject.GetHeader: TChunkIHDR;
  1517. begin
  1518.   {If there is a TChunkIHDR returns it, otherwise returns nil}
  1519.   if (Chunks.Count <> 0) and (Chunks.Item[0] is TChunkIHDR) then
  1520.     Result := Chunks.Item[0] as TChunkIHDR
  1521.   else
  1522.   begin
  1523.     {No header, throw error message}
  1524.     RaiseError(EPNGHeaderNotPresent, EPNGHeaderNotPresentText);
  1525.     Result := nil
  1526.   end
  1527. end;
  1528. {Draws using partial transparency}
  1529. procedure TPngObject.DrawPartialTrans(DC: HDC; Rect: TRect);
  1530.   {Adjust the rectangle structure}
  1531.   procedure AdjustRect(var Rect: TRect);
  1532.   var
  1533.     t: Integer;
  1534.   begin
  1535.     if Rect.Right < Rect.Left then
  1536.     begin
  1537.       t := Rect.Right;
  1538.       Rect.Right := Rect.Left;
  1539.       Rect.Left := t;
  1540.     end;
  1541.     if Rect.Bottom < Rect.Top then
  1542.     begin
  1543.       t := Rect.Bottom;
  1544.       Rect.Bottom := Rect.Top;
  1545.       Rect.Top := t;
  1546.     end
  1547.   end;
  1548. type
  1549.   {Access to pixels}
  1550.   TPixelLine = Array[Word] of TRGBQuad;
  1551.   pPixelLine = ^TPixelLine;
  1552. const
  1553.   {Structure used to create the bitmap}
  1554.   BitmapInfoHeader: TBitmapInfoHeader =
  1555.     (biSize: sizeof(TBitmapInfoHeader);
  1556.      biWidth: 100;
  1557.      biHeight: 100;
  1558.      biPlanes: 1;
  1559.      biBitCount: 32;
  1560.      biCompression: BI_RGB;
  1561.      biSizeImage: 0;
  1562.      biXPelsPerMeter: 0;
  1563.      biYPelsPerMeter: 0;
  1564.      biClrUsed: 0;
  1565.      biClrImportant: 0);
  1566. var
  1567.   {Buffer bitmap creation}
  1568.   BitmapInfo  : TBitmapInfo;
  1569.   BufferDC    : HDC;
  1570.   BufferBits  : Pointer;
  1571.   OldBitmap,
  1572.   BufferBitmap: HBitmap;
  1573.   Header: TChunkIHDR;
  1574.   {Transparency/palette chunks}
  1575.   TransparencyChunk: TChunktRNS;
  1576.   PaletteChunk: TChunkPLTE;
  1577.   TransValue, PaletteIndex: Byte;
  1578.   CurBit: Integer;
  1579.   Data: PByte;
  1580.   {Buffer bitmap modification}
  1581.   BytesPerRowDest,
  1582.   BytesPerRowSrc,
  1583.   BytesPerRowAlpha: Integer;
  1584.   ImageSource, ImageSourceOrg,
  1585.   AlphaSource     : pByteArray;
  1586.   ImageData       : pPixelLine;
  1587.   i, j, i2, j2    : Integer;
  1588.   {For bitmap stretching}
  1589.   W, H            : Cardinal;
  1590.   Stretch         : Boolean;
  1591.   FactorX, FactorY: Double;
  1592. begin
  1593.   {Prepares the rectangle structure to stretch draw}
  1594.   if (Rect.Right = Rect.Left) or (Rect.Bottom = Rect.Top) then exit;
  1595.   AdjustRect(Rect);
  1596.   {Gets the width and height}
  1597.   W := Rect.Right - Rect.Left;
  1598.   H := Rect.Bottom - Rect.Top;
  1599.   Header := Self.Header; {Fast access to header}
  1600.   Stretch := (W <> Header.Width) or (H <> Header.Height);
  1601.   if Stretch then FactorX := W / Header.Width else FactorX := 1;
  1602.   if Stretch then FactorY := H / Header.Height else FactorY := 1;
  1603.   {Prepare to create the bitmap}
  1604.   Fillchar(BitmapInfo, sizeof(BitmapInfo), #0);
  1605.   BitmapInfoHeader.biWidth := W;
  1606.   BitmapInfoHeader.biHeight := -Integer(H);
  1607.   BitmapInfo.bmiHeader := BitmapInfoHeader;
  1608.   {Create the bitmap which will receive the background, the applied}
  1609.   {alpha blending and then will be painted on the background}
  1610.   BufferDC := CreateCompatibleDC(0);
  1611.   {In case BufferDC could not be created}
  1612.   if (BufferDC = 0) then RaiseError(EPNGOutMemory, EPNGOutMemoryText);
  1613.   BufferBitmap := CreateDIBSection(BufferDC, BitmapInfo, DIB_RGB_COLORS,
  1614.     BufferBits, 0, 0);
  1615.   {In case buffer bitmap could not be created}
  1616.   if (BufferBitmap = 0) or (BufferBits = Nil) then
  1617.   begin
  1618.     if BufferBitmap <> 0 then DeleteObject(BufferBitmap);
  1619.     DeleteDC(BufferDC);
  1620.     RaiseError(EPNGOutMemory, EPNGOutMemoryText);
  1621.   end;
  1622.   {Selects new bitmap and release old bitmap}
  1623.   OldBitmap := SelectObject(BufferDC, BufferBitmap);
  1624.   {Draws the background on the buffer image}
  1625.   BitBlt(BufferDC, 0, 0, W, H, DC, Rect.Left, Rect.Top, SRCCOPY);
  1626.   {Obtain number of bytes for each row}
  1627.   BytesPerRowAlpha := Header.Width;
  1628.   BytesPerRowDest := (((BitmapInfo.bmiHeader.biBitCount * W) + 31)
  1629.     and not 31) div 8; {Number of bytes for each image row in destination}
  1630.   BytesPerRowSrc := (((Header.BitmapInfo.bmiHeader.biBitCount * Header.Width) +
  1631.     31) and not 31) div 8; {Number of bytes for each image row in source}
  1632.   {Obtains image pointers}
  1633.   ImageData := BufferBits;
  1634.   AlphaSource := Header.ImageAlpha;
  1635.   Longint(ImageSource) := Longint(Header.ImageData) +
  1636.     Header.BytesPerRow * Longint(Header.Height - 1);
  1637.   ImageSourceOrg := ImageSource;
  1638.   case Header.BitmapInfo.bmiHeader.biBitCount of
  1639.     {R, G, B images}
  1640.     24:
  1641.       FOR j := 1 TO H DO
  1642.       begin
  1643.         {Process all the pixels in this line}
  1644.         FOR i := 0 TO W - 1 DO
  1645.         begin
  1646.           if Stretch then i2 := trunc(i / FactorX) else i2 := i;
  1647.           {Optmize when we don磘 have transparency}
  1648.           if (AlphaSource[i2] <> 0) then
  1649.             if (AlphaSource[i2] = 255) then
  1650.               ImageData[i] := pRGBQuad(@ImageSource[i2 * 3])^
  1651.             else
  1652.               with ImageData[i] do
  1653.               begin
  1654.                 rgbRed := (255+ImageSource[2+i2*3] * AlphaSource[i2] + rgbRed *
  1655.                   (not AlphaSource[i2])) shr 8;
  1656.                 rgbGreen := (255+ImageSource[1+i2*3] * AlphaSource[i2] +
  1657.                   rgbGreen * (not AlphaSource[i2])) shr 8;
  1658.                 rgbBlue := (255+ImageSource[i2*3] * AlphaSource[i2] + rgbBlue *
  1659.                  (not AlphaSource[i2])) shr 8;
  1660.             end;
  1661.           end;
  1662.         {Move pointers}
  1663.         inc(Longint(ImageData), BytesPerRowDest);
  1664.         if Stretch then j2 := trunc(j / FactorY) else j2 := j;
  1665.         Longint(ImageSource) := Longint(ImageSourceOrg) - BytesPerRowSrc * j2;
  1666.         Longint(AlphaSource) := Longint(Header.ImageAlpha) +
  1667.           BytesPerRowAlpha * j2;
  1668.       end;
  1669.     {Palette images with 1 byte for each pixel}
  1670.     1,4,8: if Header.ColorType = COLOR_GRAYSCALEALPHA then
  1671.       FOR j := 1 TO H DO
  1672.       begin
  1673.         {Process all the pixels in this line}
  1674.         FOR i := 0 TO W - 1 DO
  1675.           with ImageData[i], Header.BitmapInfo do begin
  1676.             if Stretch then i2 := trunc(i / FactorX) else i2 := i;
  1677.             rgbRed := (255 + ImageSource[i2] * AlphaSource[i2] +
  1678.               rgbRed * (255 - AlphaSource[i2])) shr 8;
  1679.             rgbGreen := (255 + ImageSource[i2] * AlphaSource[i2] +
  1680.               rgbGreen * (255 - AlphaSource[i2])) shr 8;
  1681.             rgbBlue := (255 + ImageSource[i2] * AlphaSource[i2] +
  1682.               rgbBlue * (255 - AlphaSource[i2])) shr 8;
  1683.           end;
  1684.         {Move pointers}
  1685.         Longint(ImageData) := Longint(ImageData) + BytesPerRowDest;
  1686.         if Stretch then j2 := trunc(j / FactorY) else j2 := j;
  1687.         Longint(ImageSource) := Longint(ImageSourceOrg) - BytesPerRowSrc * j2;
  1688.         Longint(AlphaSource) := Longint(Header.ImageAlpha) +
  1689.           BytesPerRowAlpha * j2;
  1690.       end
  1691.     else {Palette images}
  1692.     begin
  1693.       {Obtain pointer to the transparency chunk}
  1694.       TransparencyChunk := TChunktRNS(Chunks.ItemFromClass(TChunktRNS));
  1695.       PaletteChunk := TChunkPLTE(Chunks.ItemFromClass(TChunkPLTE));
  1696.       FOR j := 1 TO H DO
  1697.       begin
  1698.         {Process all the pixels in this line}
  1699.         i := 0;
  1700.         repeat
  1701.           CurBit := 0;
  1702.           if Stretch then i2 := trunc(i / FactorX) else i2 := i;
  1703.           Data := @ImageSource[i2];
  1704.           repeat
  1705.             {Obtains the palette index}
  1706.             case Header.BitDepth of
  1707.               1: PaletteIndex := (Data^ shr (7-(I Mod 8))) and 1;
  1708.             2,4: PaletteIndex := (Data^ shr ((1-(I Mod 2))*4)) and $0F;
  1709.              else PaletteIndex := Data^;
  1710.             end;
  1711.             {Updates the image with the new pixel}
  1712.             with ImageData[i] do
  1713.             begin
  1714.               TransValue := TransparencyChunk.PaletteValues[PaletteIndex];
  1715.               rgbRed := (255 + PaletteChunk.Item[PaletteIndex].rgbRed *
  1716.                  TransValue + rgbRed * (255 - TransValue)) shr 8;
  1717.               rgbGreen := (255 + PaletteChunk.Item[PaletteIndex].rgbGreen *
  1718.                  TransValue + rgbGreen * (255 - TransValue)) shr 8;
  1719.               rgbBlue := (255 + PaletteChunk.Item[PaletteIndex].rgbBlue *
  1720.                  TransValue + rgbBlue * (255 - TransValue)) shr 8;
  1721.             end;
  1722.             {Move to next data}
  1723.             inc(i); inc(CurBit, Header.BitmapInfo.bmiHeader.biBitCount);
  1724.           until CurBit >= 8;
  1725.           {Move to next source data}
  1726.           //inc(Data);
  1727.         until i >= Integer(W);
  1728.         {Move pointers}
  1729.         Longint(ImageData) := Longint(ImageData) + BytesPerRowDest;
  1730.         if Stretch then j2 := trunc(j / FactorY) else j2 := j;
  1731.         Longint(ImageSource) := Longint(ImageSourceOrg) - BytesPerRowSrc * j2;
  1732.       end
  1733.     end {Palette images}
  1734.   end {case Header.BitmapInfo.bmiHeader.biBitCount};
  1735.   {Draws the new bitmap on the foreground}
  1736.   BitBlt(DC, Rect.Left, Rect.Top, W, H, BufferDC, 0, 0, SRCCOPY);
  1737.   {Free bitmap}
  1738.   SelectObject(BufferDC, OldBitmap);
  1739.   DeleteObject(BufferBitmap);
  1740.   DeleteDC(BufferDC);
  1741. end;
  1742. {Draws the image into a canvas}
  1743. procedure TPngObject.Draw(ACanvas: TCanvas; const Rect: TRect);
  1744. var
  1745.   Header: TChunkIHDR;
  1746. begin
  1747.   {Quit in case there is no header, otherwise obtain it}
  1748.   if Empty then Exit;
  1749.   Header := Chunks.GetItem(0) as TChunkIHDR;
  1750.   {Copy the data to the canvas}
  1751.   case Self.TransparencyMode of
  1752.   {$IFDEF PartialTransparentDraw}
  1753.     ptmPartial:
  1754.       DrawPartialTrans(ACanvas{$IFDEF UseDelphi}.Handle{$ENDIF}, Rect);
  1755.   {$ENDIF}
  1756.     ptmBit: DrawTransparentBitmap(ACanvas{$IFDEF UseDelphi}.Handle{$ENDIF},
  1757.       Header.ImageData, Header.BitmapInfo.bmiHeader,
  1758.       pBitmapInfo(@Header.BitmapInfo), Rect,
  1759.       {$IFDEF UseDelphi}ColorToRGB({$ENDIF}TransparentColor)
  1760.       {$IFDEF UseDelphi}){$ENDIF}
  1761.     else
  1762.     begin
  1763.       SetStretchBltMode(ACanvas{$IFDEF UseDelphi}.Handle{$ENDIF}, COLORONCOLOR);
  1764.       StretchDiBits(ACanvas{$IFDEF UseDelphi}.Handle{$ENDIF}, Rect.Left,
  1765.         Rect.Top, Rect.Right - Rect.Left, Rect.Bottom - Rect.Top, 0, 0,
  1766.         Header.Width, Header.Height, Header.ImageData,
  1767.         pBitmapInfo(@Header.BitmapInfo)^, DIB_RGB_COLORS, SRCCOPY)
  1768.     end
  1769.   end {case}
  1770. end;
  1771. {Characters for the header}
  1772. const
  1773.   PngHeader: Array[0..7] of Char = (#137, #80, #78, #71, #13, #10, #26, #10);
  1774. {Loads the image from a stream of data}
  1775. procedure TPngObject.LoadFromStream(Stream: TStream);
  1776. var
  1777.   Header    : Array[0..7] of Char;
  1778.   HasIDAT   : Boolean;
  1779.   {Chunks reading}
  1780.   ChunkCount : Cardinal;
  1781.   ChunkLength: Cardinal;
  1782.   ChunkName  : TChunkName;
  1783. begin
  1784.   {Initialize before start loading chunks}
  1785.   ChunkCount := 0;
  1786.   ClearChunks();
  1787.   {Reads the header}
  1788.   Stream.Read(Header[0], 8);
  1789.   {Test if the header matches}
  1790.   if Header <> PngHeader then
  1791.   begin
  1792.     RaiseError(EPNGInvalidFileHeader, EPNGInvalidFileHeaderText);
  1793.     Exit;
  1794.   end;
  1795.   HasIDAT := FALSE;
  1796.   Chunks.Count := 10;
  1797.   {Load chunks}
  1798.   repeat
  1799.     inc(ChunkCount);  {Increment number of chunks}
  1800.     if Chunks.Count < ChunkCount then  {Resize the chunks list if needed}
  1801.       Chunks.Count := Chunks.Count + 10;
  1802.     {Reads chunk length and invert since it is in network order}
  1803.     {also checks the Read method return, if it returns 0, it}
  1804.     {means that no bytes was readed, probably because it reached}
  1805.     {the end of the file}
  1806.     if Stream.Read(ChunkLength, 4) = 0 then
  1807.     begin
  1808.       {In case it found the end of the file here}
  1809.       Chunks.Count := ChunkCount - 1;
  1810.       RaiseError(EPNGUnexpectedEnd, EPNGUnexpectedEndText);
  1811.     end;
  1812.     ChunkLength := ByteSwap(ChunkLength);
  1813.     {Reads chunk name}
  1814.     Stream.Read(Chunkname, 4);
  1815.     {Here we check if the first chunk is the Header which is necessary}
  1816.     {to the file in order to be a valid Portable Network Graphics image}
  1817.     if (ChunkCount = 1) and (ChunkName <> 'IHDR') then
  1818.     begin
  1819.       Chunks.Count := ChunkCount - 1;
  1820.       RaiseError(EPNGIHDRNotFirst, EPNGIHDRNotFirstText);
  1821.       exit;
  1822.     end;
  1823.     {Has a previous IDAT}
  1824.     if (HasIDAT and (ChunkName = 'IDAT')) or (ChunkName = 'cHRM') then
  1825.     begin
  1826.       dec(ChunkCount);
  1827.       Stream.Seek(ChunkLength + 4, soFromCurrent);
  1828.       Continue;
  1829.     end;
  1830.     {Tell it has an IDAT chunk}
  1831.     if ChunkName = 'IDAT' then HasIDAT := TRUE;
  1832.     {Creates object for this chunk}
  1833.     Chunks.SetItem(ChunkCount - 1, CreateClassChunk(Self, ChunkName));
  1834.     {Check if the chunk is critical and unknown}
  1835.     {$IFDEF ErrorOnUnknownCritical}
  1836.       if (TChunk(Chunks.Item[ChunkCount - 1]).ClassType = TChunk) and
  1837.         ((Byte(ChunkName[0]) AND $20) = 0) and (ChunkName <> '') then
  1838.       begin
  1839.         Chunks.Count := ChunkCount;
  1840.         RaiseError(EPNGUnknownCriticalChunk, EPNGUnknownCriticalChunkText);
  1841.       end;
  1842.     {$ENDIF}
  1843.     {Loads it}
  1844.     try if not TChunk(Chunks.Item[ChunkCount - 1]).LoadFromStream(Stream,
  1845.        ChunkName, ChunkLength) then break;
  1846.     except
  1847.       Chunks.Count := ChunkCount;
  1848.       raise;
  1849.     end;
  1850.   {Terminates when it reaches the IEND chunk}
  1851.   until (ChunkName = 'IEND');
  1852.   {Resize the list to the appropriate size}
  1853.   Chunks.Count := ChunkCount;
  1854.   {Check if there is data}
  1855.   if not HasIDAT then
  1856.     RaiseError(EPNGNoImageData, EPNGNoImageDataText);
  1857. end;
  1858. {Changing height is not supported}
  1859. procedure TPngObject.SetHeight(Value: Integer);
  1860. begin
  1861.   RaiseError(EPNGError, EPNGCannotChangeSizeText);
  1862. end;
  1863. {Changing width is not supported}
  1864. procedure TPngObject.SetWidth(Value: Integer);
  1865. begin
  1866.   RaiseError(EPNGError, EPNGCannotChangeSizeText);
  1867. end;
  1868. {$IFDEF UseDelphi}
  1869. {Saves to clipboard format (thanks to Antoine Pottern)}
  1870. procedure TPNGObject.SaveToClipboardFormat(var AFormat: Word;
  1871.   var AData: THandle; var APalette: HPalette);
  1872. begin
  1873.   with TBitmap.Create do
  1874.     try
  1875.       Width := Self.Width;
  1876.       Height := Self.Height;
  1877.       Self.Draw(Canvas, Rect(0, 0, Width, Height));
  1878.       SaveToClipboardFormat(AFormat, AData, APalette);
  1879.     finally
  1880.       Free;
  1881.     end {try}
  1882. end;
  1883. {Loads data from clipboard}
  1884. procedure TPngObject.LoadFromClipboardFormat(AFormat: Word;
  1885.   AData: THandle; APalette: HPalette);
  1886. begin
  1887.   with TBitmap.Create do
  1888.     try
  1889.       LoadFromClipboardFormat(AFormat, AData, APalette);
  1890.       Self.AssignHandle(Handle, False, 0);
  1891.     finally
  1892.       Free;
  1893.     end {try}
  1894. end;
  1895. {Returns if the image is transparent}
  1896. function TPngObject.GetTransparent: Boolean;
  1897. begin
  1898.   Result := (TransparencyMode <> ptmNone);
  1899. end;
  1900. {$ENDIF}
  1901. {Saving the PNG image to a stream of data}
  1902. procedure TPngObject.SaveToStream(Stream: TStream);
  1903. var
  1904.   j: Integer;
  1905. begin
  1906.   {Reads the header}
  1907.   Stream.Write(PNGHeader[0], 8);
  1908.   {Write each chunk}
  1909.   FOR j := 0 TO Chunks.Count - 1 DO
  1910.     Chunks.Item[j].SaveToStream(Stream)
  1911. end;
  1912. {Prepares the Header chunk}
  1913. procedure BuildHeader(Header: TChunkIHDR; Handle: HBitmap; Info: pBitmap;
  1914.   HasPalette: Boolean);
  1915. var
  1916.   DC: HDC;
  1917. begin
  1918.   {Set width and height}
  1919.   Header.Width := Info.bmWidth;
  1920.   Header.Height := abs(Info.bmHeight);
  1921.   {Set bit depth}
  1922.   if Info.bmBitsPixel >= 16 then
  1923.     Header.BitDepth := 8 else Header.BitDepth := Info.bmBitsPixel;
  1924.   {Set color type}
  1925.   if Info.bmBitsPixel >= 16 then
  1926.     Header.ColorType := COLOR_RGB else Header.ColorType := COLOR_PALETTE;
  1927.   {Set other info}
  1928.   Header.CompressionMethod := 0;  {deflate/inflate}
  1929.   Header.InterlaceMethod := 0;    {no interlace}
  1930.   {Prepares bitmap headers to hold data}
  1931.   Header.PrepareImageData();
  1932.   {Copy image data}
  1933.   DC := CreateCompatibleDC(0);
  1934.   GetDIBits(DC, Handle, 0, Header.Height, Header.ImageData,
  1935.     pBitmapInfo(@Header.BitmapInfo)^, DIB_RGB_COLORS);
  1936.   DeleteDC(DC);
  1937. end;
  1938. {Loads the image from a resource}
  1939. procedure TPngObject.LoadFromResourceName(Instance: HInst;
  1940.   const Name: String);
  1941. var
  1942.   ResStream: TResourceStream;
  1943. begin
  1944.   {Creates an especial stream to load from the resource}
  1945.   try ResStream := TResourceStream.Create(Instance, Name, RT_RCDATA);
  1946.   except RaiseError(EPNGCouldNotLoadResource, EPNGCouldNotLoadResourceText);
  1947.   exit; end;
  1948.   {Loads the png image from the resource}
  1949.   try
  1950.     LoadFromStream(ResStream);
  1951.   finally
  1952.     ResStream.Free;
  1953.   end;
  1954. end;
  1955. {Loads the png from a resource ID}
  1956. procedure TPngObject.LoadFromResourceID(Instance: HInst; ResID: Integer);
  1957. begin
  1958.   LoadFromResourceName(Instance, String(ResID));
  1959. end;
  1960. {Assigns this tpngobject to another object}
  1961. procedure TPngObject.AssignTo(Dest: TPersistent);
  1962. {$IFDEF UseDelphi}
  1963. var
  1964.   DeskDC: HDC;
  1965.   TRNS: TChunkTRNS;
  1966. {$ENDIF}
  1967. begin
  1968.   {If the destination is also a TPNGObject make it assign}
  1969.   {this one}
  1970.   if Dest is TPNGObject then
  1971.     TPNGObject(Dest).AssignPNG(Self)
  1972.   {$IFDEF UseDelphi}
  1973.   {In case the destination is a bitmap}
  1974.   else if (Dest is TBitmap) and HeaderPresent then
  1975.   begin
  1976.     {Tests for the best pixelformat
  1977.     case Header.BitmapInfo.bmiHeader.biBitCount of
  1978.       1: TBitmap(Dest).PixelFormat := pf1Bit;
  1979.       4: TBitmap(Dest).PixelFormat := pf4Bit;
  1980.       8: TBitmap(Dest).PixelFormat := pf8Bit;
  1981.      24: TBitmap(Dest).PixelFormat := pf24Bit;
  1982.      32: TBitmap(Dest).PixelFormat := pf32Bit;
  1983.     end {case Header.BitmapInfo.bmiHeader.biBitCount};
  1984.     {Device context}
  1985.     DeskDC := GetDC(0);
  1986.     {Copy the data}
  1987.     TBitmap(Dest).Handle := CreateDIBitmap(DeskDC,
  1988.       Header.BitmapInfo.bmiHeader, CBM_INIT, Header.ImageData,
  1989.       pBitmapInfo(@Header.BitmapInfo)^, DIB_RGB_COLORS);
  1990.     ReleaseDC(0, DeskDC);
  1991.     {Copy transparency mode}
  1992.     if (TransparencyMode = ptmBit) then
  1993.     begin
  1994.       TRNS := Chunks.ItemFromClass(TChunkTRNS) as TChunkTRNS;
  1995.       TBitmap(Dest).TransparentColor := TRNS.TransparentColor;
  1996.       TBitmap(Dest).Transparent := True
  1997.     end {if (TransparencyMode = ptmBit)}
  1998.   end
  1999.   else
  2000.     {Unknown destination kind, }
  2001.     inherited AssignTo(Dest);
  2002.   {$ENDIF}
  2003. end;
  2004. {Assigns from a bitmap object}
  2005. procedure TPngObject.AssignHandle(Handle: HBitmap; Transparent: Boolean;
  2006.   TransparentColor: ColorRef);
  2007. var
  2008.   BitmapInfo: Windows.TBitmap;
  2009.   HasPalette: Boolean;
  2010.   {Chunks}
  2011.   Header: TChunkIHDR;
  2012.   PLTE: TChunkPLTE;
  2013.   IDAT: TChunkIDAT;
  2014.   IEND: TChunkIEND;
  2015.   TRNS: TChunkTRNS;
  2016. begin
  2017.   {Obtain bitmap info}
  2018.   GetObject(Handle, SizeOf(BitmapInfo), @BitmapInfo);
  2019.   {Only bit depths 1, 4 and 8 needs a palette}
  2020.   HasPalette := (BitmapInfo.bmBitsPixel < 16);
  2021.   {Clear old chunks and prepare}
  2022.   ClearChunks();
  2023.   {Create the chunks}
  2024.   Header := TChunkIHDR.Create(Self);
  2025.   if HasPalette then PLTE := TChunkPLTE.Create(Self) else PLTE := nil;
  2026.   if Transparent then TRNS := TChunkTRNS.Create(Self) else TRNS := nil;
  2027.   IDAT := TChunkIDAT.Create(Self);
  2028.   IEND := TChunkIEND.Create(Self);
  2029.   {Add chunks}
  2030.   TPNGPointerList(Chunks).Add(Header);
  2031.   if HasPalette then TPNGPointerList(Chunks).Add(PLTE);
  2032.   if Transparent then TPNGPointerList(Chunks).Add(TRNS);
  2033.   TPNGPointerList(Chunks).Add(IDAT);
  2034.   TPNGPointerList(Chunks).Add(IEND);
  2035.   {This method will fill the Header chunk with bitmap information}
  2036.   {and copy the image data}
  2037.   BuildHeader(Header, Handle, @BitmapInfo, HasPalette);
  2038.   {In case there is a image data, set the PLTE chunk fCount variable}
  2039.   {to the actual number of palette colors which is 2^(Bits for each pixel)}
  2040.   if HasPalette then PLTE.fCount := 1 shl BitmapInfo.bmBitsPixel;
  2041.   {In case it is a transparent bitmap, prepares it}
  2042.   if Transparent then TRNS.TransparentColor := TransparentColor;
  2043. end;
  2044. {Assigns from another PNG}
  2045. procedure TPngObject.AssignPNG(Source: TPNGObject);
  2046. var
  2047.   J: Integer;
  2048. begin
  2049.   {Copy properties}
  2050.   InterlaceMethod := Source.InterlaceMethod;
  2051.   MaxIdatSize := Source.MaxIdatSize;
  2052.   CompressionLevel := Source.CompressionLevel;
  2053.   Filters := Source.Filters;
  2054.   {Clear old chunks and prepare}
  2055.   ClearChunks();
  2056.   Chunks.Count := Source.Chunks.Count;
  2057.   {Create chunks and makes a copy from the source}
  2058.   FOR J := 0 TO Chunks.Count - 1 DO
  2059.     with Source.Chunks do
  2060.     begin
  2061.       Chunks.SetItem(J, TChunkClass(TChunk(Item[J]).ClassType).Create(Self));
  2062.       TChunk(Chunks.Item[J]).Assign(TChunk(Item[J]));
  2063.     end {with};
  2064. end;
  2065. {Returns a alpha data scanline}
  2066. function TPngObject.GetAlphaScanline(const LineIndex: Integer): pByteArray;
  2067. begin
  2068.   with Header do
  2069.     if (ColorType = COLOR_RGBALPHA) or (ColorType = COLOR_GRAYSCALEALPHA) then
  2070.       Longint(Result) := Longint(ImageAlpha) + (LineIndex * Longint(Width))
  2071.     else Result := nil;  {In case the image does not use alpha information}
  2072. end;
  2073. {$IFDEF Store16bits}
  2074. {Returns a png data extra scanline}
  2075. function TPngObject.GetExtraScanline(const LineIndex: Integer): Pointer;
  2076. begin
  2077.   with Header do
  2078.     Longint(Result) := (Longint(ExtraImageData) + ((Longint(Height) - 1) *
  2079.       BytesPerRow)) - (LineIndex * BytesPerRow);
  2080. end;
  2081. {$ENDIF}
  2082. {Returns a png data scanline}
  2083. function TPngObject.GetScanline(const LineIndex: Integer): Pointer;
  2084. begin
  2085.   with Header do
  2086.     Longint(Result) := (Longint(ImageData) + ((Longint(Height) - 1) *
  2087.       BytesPerRow)) - (LineIndex * BytesPerRow);
  2088. end;
  2089. {Initialize gamma table}
  2090. procedure TPngObject.InitializeGamma;
  2091. var
  2092.   i: Integer;
  2093. begin
  2094.   {Build gamma table as if there was no gamma}
  2095.   FOR i := 0 to 255 do
  2096.   begin
  2097.     GammaTable[i] := i;
  2098.     InverseGamma[i] := i;
  2099.   end {for i}
  2100. end;
  2101. {Returns the transparency mode used by this png}
  2102. function TPngObject.GetTransparencyMode: TPNGTransparencyMode;
  2103. var
  2104.   TRNS: TChunkTRNS;
  2105. begin
  2106.   with Header do
  2107.   begin
  2108.     Result := ptmNone; {Default result}
  2109.     {Gets the TRNS chunk pointer}
  2110.     TRNS := Chunks.ItemFromClass(TChunkTRNS) as TChunkTRNS;
  2111.     {Test depending on the color type}
  2112.     case ColorType of
  2113.       {This modes are always partial}
  2114.       COLOR_RGBALPHA, COLOR_GRAYSCALEALPHA: Result := ptmPartial;
  2115.       {This modes support bit transparency}
  2116.       COLOR_RGB, COLOR_GRAYSCALE: if TRNS <> nil then Result := ptmBit;
  2117.       {Supports booth translucid and bit}
  2118.       COLOR_PALETTE:
  2119.         {A TRNS chunk must be present, otherwise it won't support transparency}
  2120.         if TRNS <> nil then
  2121.           if TRNS.BitTransparency then
  2122.             Result := ptmBit else Result := ptmPartial
  2123.     end {case}
  2124.   end {with Header}
  2125. end;
  2126. {Add a text chunk}
  2127. procedure TPngObject.AddtEXt(const Keyword, Text: String);
  2128. var
  2129.   TextChunk: TChunkTEXT;
  2130. begin
  2131.   TextChunk := Chunks.Add(TChunkText) as TChunkTEXT;
  2132.   TextChunk.Keyword := Keyword;
  2133.   TextChunk.Text := Text;
  2134. end;
  2135. {Add a text chunk}
  2136. procedure TPngObject.AddzTXt(const Keyword, Text: String);
  2137. var
  2138.   TextChunk: TChunkzTXt;
  2139. begin
  2140.   TextChunk := Chunks.Add(TChunkzTXt) as TChunkzTXt;
  2141.   TextChunk.Keyword := Keyword;
  2142.   TextChunk.Text := Text;
  2143. end;
  2144. {Removes the image transparency}
  2145. procedure TPngObject.RemoveTransparency;
  2146. var
  2147.   TRNS: TChunkTRNS;
  2148. begin
  2149.   TRNS := Chunks.ItemFromClass(TChunkTRNS) as TChunkTRNS;
  2150.   if TRNS <> nil then Chunks.RemoveChunk(TRNS)
  2151. end;
  2152. {Generates alpha information}
  2153. procedure TPngObject.CreateAlpha;
  2154. var
  2155.   TRNS: TChunkTRNS;
  2156. begin
  2157.   {Generates depending on the color type}
  2158.   with Header do
  2159.     case ColorType of
  2160.       {Png allocates different memory space to hold alpha information}
  2161.       {for these types}
  2162.       COLOR_GRAYSCALE, COLOR_RGB:
  2163.       begin
  2164.         {Transform into the appropriate color type}
  2165.         if ColorType = COLOR_GRAYSCALE then
  2166.           ColorType := COLOR_GRAYSCALEALPHA
  2167.         else ColorType := COLOR_RGBALPHA;
  2168.         {Allocates memory to hold alpha information}
  2169.         GetMem(ImageAlpha, Integer(Width) * Integer(Height));
  2170.         FillChar(ImageAlpha^, Integer(Width) * Integer(Height), #255);
  2171.       end;
  2172.       {Palette uses the TChunktRNS to store alpha}
  2173.       COLOR_PALETTE:
  2174.       begin
  2175.         {Gets/creates TRNS chunk}
  2176.         if Chunks.ItemFromClass(TChunkTRNS) = nil then
  2177.           TRNS := Chunks.Add(TChunkTRNS) as TChunkTRNS
  2178.         else
  2179.           TRNS := Chunks.ItemFromClass(TChunkTRNS) as TChunkTRNS;
  2180.           {Prepares the TRNS chunk}
  2181.           with TRNS do
  2182.           begin
  2183.             Fillchar(PaletteValues[0], 256, 255);
  2184.             fDataSize := 1 shl Header.BitDepth;
  2185.             fBitTransparency := False
  2186.           end {with Chunks.Add};
  2187.         end;
  2188.     end {case Header.ColorType}
  2189. end;
  2190. {Returns transparent color}
  2191. function TPngObject.GetTransparentColor: TColor;
  2192. var
  2193.   TRNS: TChunkTRNS;
  2194. begin
  2195.   TRNS := Chunks.ItemFromClass(TChunkTRNS) as TChunkTRNS;
  2196.   {Reads the transparency chunk to get this info}
  2197.   if Assigned(TRNS) then Result := TRNS.TransparentColor
  2198.     else Result := 0
  2199. end;
  2200. {$OPTIMIZATION OFF}
  2201. procedure TPngObject.SetTransparentColor(const Value: TColor);
  2202. var
  2203.   TRNS: TChunkTRNS;
  2204. begin
  2205.   if HeaderPresent then
  2206.     {Tests the ColorType}
  2207.     case Header.ColorType of
  2208.     {Not allowed for this modes}
  2209.     COLOR_RGBALPHA, COLOR_GRAYSCALEALPHA: Self.RaiseError(
  2210.       EPNGCannotChangeTransparent, EPNGCannotChangeTransparentText);
  2211.     {Allowed}
  2212.     COLOR_PALETTE, COLOR_RGB, COLOR_GRAYSCALE:
  2213.       begin
  2214.         TRNS := Chunks.ItemFromClass(TChunkTRNS) as TChunkTRNS;
  2215.         if not Assigned(TRNS) then TRNS := Chunks.Add(TChunkTRNS) as TChunkTRNS;
  2216.         {Sets the transparency value from TRNS chunk}
  2217.         TRNS.TransparentColor := {$IFDEF UseDelphi}ColorToRGB({$ENDIF}Value{$IFDEF UseDelphi}){$ENDIF}
  2218.       end {COLOR_PALETTE, COLOR_RGB, COLOR_GRAYSCALE)}
  2219.     end {case}
  2220. end;
  2221. {Returns if header is present}
  2222. function TPngObject.HeaderPresent: Boolean;
  2223. begin
  2224.   Result := ((Chunks.Count <> 0) and (Chunks.Item[0] is TChunkIHDR))
  2225. end;
  2226. {Returns pixel for png using palette and grayscale}
  2227. function GetByteArrayPixel(const png: TPngObject; const X, Y: Integer): TColor;
  2228. var
  2229.   ByteData: Byte;
  2230.   DataDepth: Byte;
  2231. begin
  2232.   with png, Header do
  2233.   begin
  2234.     {Make sure the bitdepth is not greater than 8}
  2235.     DataDepth := BitDepth;
  2236.     if DataDepth > 8 then DataDepth := 8;
  2237.     {Obtains the byte containing this pixel}
  2238.     ByteData := pByteArray(png.Scanline[Y])^[X div (8 div DataDepth)];
  2239.     {Moves the bits we need to the right}
  2240.     ByteData := (ByteData shr ((8 - DataDepth) -
  2241.       (X mod (8 div DataDepth)) * DataDepth));
  2242.     {Discard the unwanted pixels}
  2243.     ByteData:= ByteData and ($FF shr (8 - DataDepth));
  2244.     {For palette mode map the palette entry and for grayscale convert and
  2245.     returns the intensity}
  2246.     case ColorType of
  2247.       COLOR_PALETTE:
  2248.         with TChunkPLTE(png.Chunks.ItemFromClass(TChunkPLTE)).Item[ByteData] do
  2249.           Result := rgb(GammaTable[rgbRed], GammaTable[rgbGreen],
  2250.             GammaTable[rgbBlue]);
  2251.       COLOR_GRAYSCALE:
  2252.       begin
  2253.         if BitDepth = 1
  2254.         then ByteData := GammaTable[Byte(ByteData * 255)]
  2255.         else ByteData := GammaTable[Byte(ByteData * ((1 shl DataDepth) + 1))];
  2256.         Result := rgb(ByteData, ByteData, ByteData);
  2257.       end;
  2258.       else Result := 0;
  2259.     end {case};
  2260.   end {with}
  2261. end;
  2262. {In case vcl units are not being used}
  2263. {$IFNDEF UseDelphi}
  2264. function ColorToRGB(const Color: TColor): COLORREF;
  2265. begin
  2266.   Result := Color
  2267. end;
  2268. {$ENDIF}
  2269. {Sets a pixel for grayscale and palette pngs}
  2270. procedure SetByteArrayPixel(const png: TPngObject; const X, Y: Integer;
  2271.   const Value: TColor);
  2272. const
  2273.   ClearFlag: Array[1..8] of Integer = (1, 3, 0, 15, 0, 0, 0, $FF);
  2274. var
  2275.   ByteData: pByte;
  2276.   DataDepth: Byte;
  2277.   ValEntry: Byte;
  2278. begin
  2279.   with png.Header do
  2280.   begin
  2281.     {Map into a palette entry}
  2282.     ValEntry := GetNearestPaletteIndex(Png.Palette, ColorToRGB(Value));
  2283.     {16 bits grayscale extra bits are discarted}
  2284.     DataDepth := BitDepth;
  2285.     if DataDepth > 8 then DataDepth := 8;
  2286.     {Gets a pointer to the byte we intend to change}
  2287.     ByteData := @pByteArray(png.Scanline[Y])^[X div (8 div DataDepth)];
  2288.     {Clears the old pixel data}
  2289.     ByteData^ := ByteData^ and not (ClearFlag[DataDepth] shl ((8 - DataDepth) -
  2290.       (X mod (8 div DataDepth)) * DataDepth));
  2291.     {Setting the new pixel}
  2292.     ByteData^ := ByteData^ or (ValEntry shl ((8 - DataDepth) -
  2293.       (X mod (8 div DataDepth)) * DataDepth));
  2294.   end {with png.Header}
  2295. end;
  2296. {Returns pixel when png uses RGB}
  2297. function GetRGBLinePixel(const png: TPngObject;
  2298.   const X, Y: Integer): TColor;
  2299. begin
  2300.   with pRGBLine(png.Scanline[Y])^[X] do
  2301.     Result := RGB(rgbtRed, rgbtGreen, rgbtBlue)
  2302. end;
  2303. {Sets pixel when png uses RGB}
  2304. procedure SetRGBLinePixel(const png: TPngObject;
  2305.  const X, Y: Integer; Value: TColor);
  2306. begin
  2307.   with pRGBLine(png.Scanline[Y])^[X] do
  2308.   begin
  2309.     rgbtRed := GetRValue(Value);
  2310.     rgbtGreen := GetGValue(Value);
  2311.     rgbtBlue := GetBValue(Value)
  2312.   end
  2313. end;
  2314. {Sets a pixel}
  2315. procedure TPngObject.SetPixels(const X, Y: Integer; const Value: TColor);
  2316. begin
  2317.   if ((X >= 0) and (X <= Width - 1)) and
  2318.         ((Y >= 0) and (Y <= Height - 1)) then
  2319.     with Header do
  2320.     begin
  2321.       if ColorType in [COLOR_GRAYSCALE, COLOR_PALETTE] then
  2322.         SetByteArrayPixel(Self, X, Y, Value)
  2323.       else
  2324.         SetRGBLinePixel(Self, X, Y, Value)
  2325.     end {with}
  2326. end;
  2327. {Returns a pixel}
  2328. function TPngObject.GetPixels(const X, Y: Integer): TColor;
  2329. begin
  2330.   if ((X >= 0) and (X <= Width - 1)) and
  2331.         ((Y >= 0) and (Y <= Height - 1)) then
  2332.     with Header do
  2333.     begin
  2334.       if ColorType in [COLOR_GRAYSCALE, COLOR_PALETTE] then
  2335.         Result := GetByteArrayPixel(Self, X, Y)
  2336.       else
  2337.         Result := GetRGBLinePixel(Self, X, Y)
  2338.     end {with}
  2339.   else Result := 0
  2340. end;
  2341. {Returns the image palette}
  2342. function TPngObject.GetPalette: HPALETTE;
  2343. var
  2344.   LogPalette: TMaxLogPalette;
  2345.   i: Integer;
  2346. begin
  2347.   {Palette is avaliable for COLOR_PALETTE and COLOR_GRAYSCALE modes}
  2348.   if (Header.ColorType in [COLOR_PALETTE, COLOR_GRAYSCALE])  then
  2349.   begin
  2350.     {In case the pal}
  2351.     if TempPalette = 0 then
  2352.       with LogPalette do
  2353.       begin
  2354.         {Prepares the new palette}
  2355.         palVersion := $300;
  2356.         palNumEntries := 256;
  2357.         {Copy entries}
  2358.         for i := 0 to LogPalette.palNumEntries - 1 do
  2359.         begin
  2360.           palPalEntry[i].peRed := Header.BitmapInfo.bmiColors[i].rgbRed;
  2361.           palPalEntry[i].peGreen := Header.BitmapInfo.bmiColors[i].rgbGreen;
  2362.           palPalEntry[i].peBlue := Header.BitmapInfo.bmiColors[i].rgbBlue;
  2363.           palPalEntry[i].peFlags := 0;
  2364.         end {for i};
  2365.         {Creates the palette}
  2366.         TempPalette := CreatePalette(pLogPalette(@LogPalette)^);
  2367.       end {with LogPalette, if Temppalette = 0}
  2368.   end {if Header.ColorType in ...};
  2369.   Result := TempPalette;
  2370. end;
  2371. initialization
  2372.   {Initialize}
  2373.   ChunkClasses := nil;
  2374.   {crc table has not being computed yet}
  2375.   crc_table_computed := FALSE;
  2376.   {Register the necessary chunks for png}
  2377.   RegisterCommonChunks;
  2378.   {Registers TPNGObject to use with TPicture}
  2379.   {$IFDEF UseDelphi}{$IFDEF RegisterGraphic}
  2380.     TPicture.RegisterFileFormat('PNG', 'Portable Network Graphics', TPNGObject);
  2381.   {$ENDIF}{$ENDIF}
  2382. finalization
  2383.   {$IFDEF UseDelphi}{$IFDEF RegisterGraphic}
  2384.     TPicture.UnregisterGraphicClass(TPNGObject);
  2385.   {$ENDIF}{$ENDIF}
  2386.   {Free chunk classes}
  2387.   FreeChunkClassList;
  2388. end.