PNGImage.pas
上传用户:zdp402
上传日期:2022-05-07
资源大小:101k
文件大小:69k
源码类别:

图片显示

开发平台:

Delphi

  1. {*******************************************************}
  2. {                                                       }
  3. {       Portable Graphics Network decoder               }
  4. {       * decode & encode png files in delphi *         }
  5. {                                                       }
  6. {       EMAIL: gustavodaud@uol.com.br                   }
  7. {                                                       }
  8. {*******************************************************}
  9. { Delphi 3 compatibility by Paul TOTH <tothpaul@free.fr> }
  10. unit PNGImage;
  11. interface
  12. uses
  13.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
  14. resourcestring
  15.   {.$INCLUDE Portuguese.TXT}
  16.   {$INCLUDE English.TXT}
  17. {Portable Graphics Network implementation}
  18. type
  19.   {Encoding filter}
  20.   TFilterRow = array[0..4] of PByteArray;
  21.   TEncodeFilter = (efNone, efSub, efUp, efAverage, efPaeth);
  22.   TEncodeFilterSet = set of TEncodeFilter;
  23.   {:Chunk type}
  24.   TChunkType = Array[0..3] of char;
  25.   {Forward declarations}
  26.   TPNGImage = class;
  27.   TChunkList = class;
  28.   TChunkGAMA = class;
  29.   TChunkIHDR = class;
  30.   {:This class handles the chunks}
  31.   TChunk = class
  32.     constructor Create(AOwner: TChunkList); virtual;
  33.     destructor Destroy; override;
  34.   private
  35.     fList  : TChunkList;
  36.     fStream: TMemoryStream;
  37.     function GetSize: Integer;
  38.     {Returns pointer to the most common chunk types}
  39.     function GetIHDR   : TChunkIHDR;
  40.     function GetGAMA   : TChunkGAMA;
  41.     {Return a pointer to the TPNGImage owner}
  42.     function GetBitmap : TPNGImage;
  43.   protected
  44.     fType  : TChunkType;
  45.     function GetIndex: Integer;
  46.     procedure DoAction; virtual;
  47.     property IHDR  : TChunkIHDR read GetIHDR;
  48.     property GAMA  : TChunkGAMA read GetGama;
  49.     property Bitmap: TPNGImage  read GetBitmap;
  50.     property Stream: TMemoryStream read fStream;
  51.   public
  52.     procedure Assign(Source: TChunk); virtual;
  53.     procedure SaveToStream(Stream: TStream); virtual;
  54.     property Index: Integer read GetIndex;
  55.     property Owner: TChunkList read fList;
  56.     property Size: Integer read GetSize;
  57.     property ChunkType: TChunkType read fType;
  58.   end;
  59.   {:IEND Chunk, 0 bytes length}
  60.   TChunkIEND = class(TChunk);
  61.   {:tEXt Chunk, dynamic size, minimum 2 bytes (null separators)}
  62.   TChunkTEXT = Class(TChunk)
  63.     constructor Create(AOwner: TChunkList); override;
  64.   private
  65.     function GetValue(Index: Integer): String;
  66.     procedure SetValue(Index: Integer; Value: String);
  67.   public
  68.     property Keyword: String index 0 read GetValue write SetValue;
  69.     property Text: String index 1 read GetValue write SetValue;
  70.   end;
  71.   {:zTXt Chunk, dynamic size}
  72.   TChunkZTXT = Class(TChunk)
  73.   private
  74.     function GetValue(Index: Integer): String;
  75.     procedure SetValue(Index: Integer; Value: String);
  76.   public
  77.     property Keyword: String index 0 read GetValue write SetValue;
  78.     property Text: String index 1 read GetValue write SetValue;
  79.   end;
  80.   {:gAMA Chunk, 4 bytes length}
  81.   TChunkGAMA = class(TChunk)
  82.     constructor Create(AOwner: TChunkList); override;
  83.     procedure Assign(Source: TChunk); override;
  84.   protected
  85.     GammaTable,
  86.     InverseTable: Array[Byte] of Byte;
  87.     procedure DoAction; override;
  88.   private
  89.     function GetValue: Cardinal;
  90.     procedure SetValue(Value: Cardinal);
  91.   public
  92.     property Value: Cardinal read GetValue write SetValue;
  93.   end;
  94.   {:PLTE Chunk, dynamic length}
  95.   TChunkPLTE = class(TChunk)
  96.     destructor Destroy; Override;
  97.   private
  98.     fPalette: HPalette;
  99.     function GetPalette: HPalette;
  100.   public
  101.     procedure SaveToStream(Stream: TStream); override;
  102.     property Palette: HPalette read GetPalette;
  103.   end;
  104.   {:IHDR Chunk, 13 bytes length}
  105.   TChunkIHDR = class(TChunk)
  106.     procedure SaveToStream(Stream: TStream); override;
  107.     constructor Create(AOwner: TChunkList); override;
  108.   private
  109.     function GetWidth: Cardinal;
  110.     function GetHeight: Cardinal;
  111.     procedure SetWidth(Value: Cardinal);
  112.     procedure SetHeight(Value: Cardinal);
  113.     function GetValue(Index: Integer): Byte;
  114.     procedure SetValue(Index: Integer; Value: Byte);
  115.   public
  116.     property Width: Cardinal read GetWidth write SetWidth;
  117.     property Height: Cardinal read GetHeight write SetHeight;
  118.     property BitDepth: Byte index 0 read GetValue write SetValue;
  119.     property ColorType: Byte index 1 read GetValue write SetValue;
  120.     property Compression: Byte index 2 read GetValue write SetValue;
  121.     property Filter: Byte index 3 read GetValue write SetValue;
  122.     property Interlaced: Byte index 4 read GetValue write SetValue;
  123.   end;
  124.   {:IDAT Chunk, dynamic size}
  125.   TChunkIDAT = class(TChunk)
  126.   public
  127.     procedure SaveToStream(Stream: TStream); override;
  128.   protected
  129.     function GetBufferWidth: Integer;
  130.     procedure FilterRow(Filter: Byte; CurrentRow, LastRow: pbytearray;
  131.      offset, row_buffer_width: Integer);
  132.     function EncodeFilterRow(row_buffer: pbytearray;
  133.       Filter_buffers: TFilterRow; row_width, filter_width: Cardinal): Integer;
  134.     procedure DoAction; override;
  135.     function GetOffset: Integer;
  136.     procedure EncodeImage;
  137.     procedure SetupPixelFormat;
  138.     procedure DecodeNonInterlacedRow(ImageData: Pointer; Data: pByteArray;
  139.       RowBytes: Integer; GamaChunk: TChunkGama);
  140.     procedure DecodeInterlacedRow(ImageData: Pointer; Data: pByteArray;
  141.       ColStart, ColIncrement, RowBytes, Pass: Integer; GamaChunk: TChunkGama);
  142.   end;
  143.   {:tIME Chunk, 7 bytes}
  144.   TChunkTIME = class(TChunk)
  145.     constructor Create(AOwner: TChunkList); override;
  146.     function GetDateTime: TDateTime;
  147.   private
  148.     procedure SetDateTime(const Value: TDateTime);
  149.   public
  150.     property DateTime: TDateTime read GetDateTime write SetDateTime;
  151.   end;
  152.   {:tRNS Chunk, dynamic length}
  153.   TChunkTRNS = class(TChunk)
  154.   private
  155.     function GetRGBColor: TColor;
  156.   public
  157.     procedure SaveToStream(Stream: TStream); override;
  158.     property RGBColor: TColor read GetRGBColor;
  159.   end;
  160.   {:Chunk class handler}
  161.   TChunkClass = Class of TChunk;
  162.   {:Record containg a chunk class info}
  163.   pChunkClassInfo = ^TChunkClassInfo;
  164.   TChunkClassInfo = record
  165.     ChunkType:  TChunkType;
  166.     ChunkClass: TChunkClass;
  167.   end;
  168.   {:This class contains the avaliable kinds of TChunk class}
  169.   TChunkClasses = class
  170.     destructor Destroy; Override;
  171.   private
  172.     fList: TList;
  173.     function GetCount: Integer;
  174.     function GetItem(Index: Integer): TChunkClassInfo;
  175.   public
  176.     property Count: Integer read GetCount;
  177.     function IndexOfType(Item: TChunkType): Integer; { Paul - overload; }
  178.     function IndexOfClass(Item: TChunkClass): Integer; { Paul - overload; }
  179.     procedure Add(ChunkType: TChunkType; ChunkClass: TChunkClass);
  180.     property Item[Index: Integer]: TChunkClassInfo read GetItem; default;
  181.   end;
  182.   {:This class contains the list of avaliable chunks for a TPNGImage }
  183.   {:object class.                                                    }
  184.   TChunkList = class
  185.     constructor Create(AOwner: TPNGImage);
  186.     destructor Destroy; override;
  187.   private
  188.     fImage: TPNGImage;
  189.     fList : TList;
  190.     function GetCount: Integer;
  191.     function GetItem(Index: Integer): TChunk;
  192.   public
  193.     property Owner: TPNGImage read fImage;
  194.     property Count: Integer read GetCount;
  195.     property Item[Index: Integer]: TChunk read GetItem; default;
  196.     procedure Move(Index1, Index2: Integer);
  197.     function AddItem(Item: TChunk): TChunk; { Paul - overload; }
  198.     function AddClass(ChunkClass: TChunkClass): TChunk; { Paul - overload; }
  199.     function AddStream(Stream: TStream): TChunk; { Paul - overload; }
  200.     procedure Remove(Item: TChunk);
  201.     function IndexOfChunk(Chunk: TChunk): Integer; { Paul - overload; }
  202.     function IndexOfClass(ChunkClass: TChunkClass): Integer; { Paul - overload; }
  203.     procedure Clear;
  204.   end;
  205.   {:This format handler is able to load and save booth interlaced and
  206.     non interlaced portable graphics network images using a ZLIB
  207.     compression decoder}
  208.   TPNGImage = class(TBitmap)
  209.     constructor Create; override;
  210.     destructor Destroy; override;
  211.     procedure LoadFromStream(Stream: TStream); override;
  212.     procedure SaveToStream(Stream: TStream); override;
  213.   private
  214.     fMask: TBitmap;
  215.     fEncodeFilter: TEncodeFilterSet;
  216.     fInterlacing: Boolean;
  217.     fChunkList: TChunkList;
  218.     procedure SetFilter(Value: TEncodeFilterSet);
  219.   public
  220.     procedure Assign(Source: TPersistent); override;
  221.     property Filter: TEncodeFilterSet read fEncodeFilter write SetFilter;
  222.     property Interlacing: Boolean read fInterlacing write fInterlacing;
  223.     procedure Clear;
  224.     property Chunks: TChunkList read fChunkList;
  225.     class procedure RegisterChunkClass(ChunkType: TChunkType;
  226.       ChunkClass: TChunkClass);
  227.   end;
  228. implementation
  229. uses
  230.   PNGZLIB{, Math};
  231. { Delphi 3 missing function }
  232. Procedure ReplaceTimePNG(Var D:TDateTime; T:TDateTime);
  233.  begin
  234.   D:=D+T; // this work for PNGImage only !
  235.  end;
  236. (*Procedure ShowMessageFmt(msg:string; fmt:array of const);
  237.  begin
  238.   ShowMessage(Format(msg,fmt));
  239.  end; *)
  240. var
  241.   {Stores the avaliable kinds of TChunk}
  242.   ChunkClasses: TChunkClasses;
  243. const
  244.   FILTERBUFFERCOUNT = 5;
  245.   {Interlacing}
  246.   RowStart: array[0..6] of Integer = (0, 0, 4, 0, 2, 0, 1);
  247.   ColumnStart: array[0..6] of Integer = (0, 4, 0, 2, 0, 1, 0);
  248.   RowIncrement: array[0..6] of Integer = (8, 8, 8, 4, 4, 2, 2);
  249.   ColumnIncrement: array[0..6] of Integer = (8, 8, 4, 4, 2, 2, 1);
  250.   PassMask: array[0..6] of Byte = ($80, $08, $88, $22, $AA, $55, $FF);
  251.   {Color types}
  252.   Grayscale = 0;
  253.   RGB = 2;
  254.   Palette = 3;
  255.   GrayscaleAlpha = 4;
  256.   RGBAlpha = 6;
  257.   {Filter types}
  258.   FILTERNONE = 0;
  259.   FILTERSUB = 1;
  260.   FILTERUP = 2;
  261.   FILTERAVERAGE = 3;
  262.   FILTERPAETH = 4;
  263.   {Valid PNG header (first 8 bytes)}
  264.   PNGHeader: array[0..7] of Byte = (137, 80, 78, 71, 13, 10, 26, 10);
  265. type
  266.   pCardinal = ^Cardinal;
  267.   {Default error handler for PNG format}
  268.   EPNGImageException = Class(Exception);
  269.   {:IHDR Chunk}
  270.   pIHDRChunk = ^TIHDRChunk;
  271.   TIHDRChunk = packed record
  272.     {Width and height give the image dimensions in pixels}
  273.     Width, Height: Cardinal;
  274.     {Bit depth is a single-byte integer giving the number of bits }
  275.     {per sample or per palette index (not per pixel). Valid values}
  276.     {are 1, 2, 4, 8, and 16, although not all values are allowed  }
  277.     {for all color types                                          }
  278.     BitDepth,
  279.     {Color type is a single-byte integer that describes the }
  280.     {interpretation of the image data. Color type codes     }
  281.     {represent sums of the following values:                }
  282.     {1 (palette used)                                       }
  283.     {2 (color used)                                         }
  284.     {4 (alpha channel used).                                }
  285.     {Valid values are 0, 2, 3, 4, and 6.                    }
  286.     ColorType,
  287.     {Compression method is a single-byte integer that indicates}
  288.     {the method used to compress the image data. At present,   }
  289.     {only compression method 0 (deflate/inflate compression    }
  290.     {with a sliding window of at most 32768 bytes) is defined. }
  291.     {All standard PNG images must be compressed with this      }
  292.     {scheme. The compression method field is provided for      }
  293.     {possible future expansion or proprietary variants.        }
  294.     {Decoders must check this byte and report an error if it   }
  295.     {holds an unrecognized code                                }
  296.     Compression,
  297.     {Filter method is a single-byte integer that indicates the }
  298.     {preprocessing method applied to the image data before     }
  299.     {compression. At present, only filter method 0  (adaptive  }
  300.     {filtering with five basic filter types) is defined.       }
  301.     Filter,
  302.     {Interlace method is a single-byte integer that indicates  }
  303.     {the transmission order of the image data. Two values are  }
  304.     {currently defined: 0 (no interlace) or 1 (Adam7 interlace)}
  305.     Interlaced: Byte;
  306.   end;
  307.   {tIME Chunk}
  308.   pTIMEChunk = ^TTimeChunk;
  309.   TTIMEChunk = Record
  310.     Year    : Word;
  311.     Month   : Byte;
  312.     Day     : Byte;
  313.     Hour    : Byte;
  314.     Min     : Byte;
  315.     Sec     : Byte;
  316.   end;
  317.   {Pixel memory access}
  318.   pRGBLine = ^TRGBLine;
  319.   TRGBLine = Array[Word] of TRGBTriple;
  320.   pRGBALine = ^TRGBALine;
  321.   TRGBALine = Array[Word] of TRGBQuad;
  322.   {Standard PNG header}
  323.   TPNGHeader = Array[0..7] of Byte;
  324. procedure ConvertBits(Source: array of Pointer; Target: Pointer;
  325.   Count: Cardinal; Mask: Byte; FSourceBPS, FTargetBPS: Byte); forward;
  326. {Forward declaration for the CRC check function}
  327. function crc(chunktype: tchunktype; buf: pbytearray;
  328.   len: Integer): Cardinal; forward;
  329. {:swaps high and low bytes of the given 32 bit value}
  330. function SwapLong(Value: Cardinal): Cardinal;
  331. asm
  332.   BSWAP EAX
  333. end;
  334. {:Register a new chunk kind class}
  335. procedure RegisterNewChunkClass(ChunkType: TChunkType; ChunkClass: TChunkClass);
  336. begin
  337.   {Add to the list}
  338.   ChunkClasses.Add(ChunkType, ChunkClass);
  339. end;
  340. {:From time to time, shows a message}
  341. procedure Shareware;
  342. begin
  343.   Randomize;
  344.   {From time to time, shows a message}
  345.   if INT(RANDOM(20)) = 1 then
  346.     MessageBox(GetActiveWindow, pchar(PNG_SHARE), pchar(PNG_SHARE_TITLE),
  347.       MB_ICONINFORMATION);
  348. end;
  349. {:Extracted from PNG specification, returns paeth prediction of the values}
  350. function PaethPredictor(a, b, c: Byte): Byte;
  351. var
  352.   p, pa, pb, pc: Integer;
  353. begin
  354.   { a = left, b = above, c = upper left }
  355.   p := a + b - c;        { initial estimate }
  356.   pa := Abs(p - a);      { distances to a, b, c }
  357.   pb := Abs(p - b);
  358.   pc := Abs(p - c);
  359.   { return nearest of a, b, c, breaking ties in order a, b, c }
  360.   if (pa <= pb) and (pa <= pc) then
  361.     Result := a
  362.   else
  363.     if pb <= pc then
  364.       Result := b
  365.     else
  366.       Result := c;
  367. end;
  368. {:Default error handler method}
  369. procedure CallError(ErrorCode: String);
  370. begin
  371.   {Show the error message}
  372.   raise EPNGImageException.CreateFmt('Portable Graphics Network format handler ' +
  373.       'error%s%s', [#13#10#13#10, ErrorCode]);
  374. end;
  375. {Returns the RGB color}
  376. function TChunkTRNS.GetRGBColor: TColor;
  377. var
  378.   Data: pByteArray;
  379. begin
  380.   {Test if the current color type is RGB}
  381.   if IHDR.ColorType <> RGB then
  382.     CallError(PNG_INVALID_COLOR_TYPE);
  383.   Data := fStream.Memory;
  384.   Result := Windows.RGB(Data^[0], Data^[1], Data^[2]);
  385. end;
  386. {When the chunk is being created}
  387. constructor TChunkTIME.Create(AOwner: TChunkList);
  388. begin
  389.   inherited;
  390.   {Initial size and value}
  391.   fStream.SetSize(7); { Paul - fStream.Size := 7; }
  392.   DateTime := Now;
  393. end;
  394. {:Return the value of the date and time stamped on the chunk}
  395. function TChunkTIME.GetDateTime: TDateTime;
  396. var
  397.   Data    : TTimeChunk;
  398. begin
  399.   {Makes sure that the stream size is 7}
  400.   if fStream.Size <> 7 then
  401.     CallError(TIME_CORRUPTED);
  402.   {Read the data into the record}
  403.   Data := pTimeChunk(fStream.Memory)^;
  404.   Data.Year := SwapLong(Data.Year);
  405.   {Return value}
  406.   with Data do
  407.     {Test if time is corrupted}
  408.     try
  409.       if Year = 0 then Year := 2000;
  410.       Result := EncodeDate(Year, Month, Day);
  411.       ReplaceTimePNG(Result, EncodeTime(Hour, Min, Sec, 0));
  412.     except
  413.       ShowMessageFmt('Year: %d, Month: %d, Day: %d, Hour: %d, Min: %d,' +
  414.         'Sec: %d', [Year, Month, Day, Hour, Min, Sec]);
  415.       CallError(TIME_CORRUPTED);
  416.     end;
  417. end;
  418. {:Set the value for the date and time in the chunk}
  419. procedure TChunkTIME.SetDateTime(const Value: TDateTime);
  420. var
  421.   Year,
  422.   Month,
  423.   Day,
  424.   Hour,
  425.   Min,
  426.   Sec,
  427.   MSec   : word;
  428.   Temp   : Byte;
  429. begin
  430.   fStream.Clear;
  431.   {Get the datetime values}
  432.   DecodeTime(Value, Hour, Min, Sec, MSec);
  433.   DecodeDate(Value, Year, Month, Day);
  434.   {Write the values}
  435.   Year := SwapLong(Year);
  436.   fStream.Write(Year, 2);
  437.   Temp := Month; fStream.Write(Temp, 1);
  438.   Temp := Day;   fStream.Write(Temp, 1);
  439.   Temp := Hour;  fStream.Write(Temp, 1);
  440.   Temp := Min;   fStream.Write(Temp, 1);
  441.   Temp := Sec;   fStream.Write(Sec, 1);
  442. end;
  443. {When the chunk is being saved}
  444. procedure TChunkTRNS.SaveToStream(Stream: TStream);
  445. var
  446.   Temp: Byte;
  447. begin
  448.   {Clear the data contents}
  449.   fStream.Clear;
  450.   {Write different transparency for different color formats}
  451.   case IHDR.ColorType of
  452.     RGB:
  453.     begin
  454.       {RGB data}
  455.       Temp := GetRValue(Bitmap.TransparentColor);  fStream.Write(Temp, 1);
  456.       Temp := GetGValue(Bitmap.TransparentColor);  fStream.Write(Temp, 1);
  457.       Temp := GetBValue(Bitmap.TransparentColor);  fStream.Write(Temp, 1);
  458.     end;
  459.   else
  460.     exit;
  461.   end;
  462.   inherited;
  463. end;
  464. {:Return value of one of the properties}
  465. function TChunkZTXT.GetValue(Index: Integer): String;
  466. var
  467.   fKeyword: Pchar;
  468.   DSize   : Integer;
  469.   fText   : Pchar; { Paul - Array of Char; }
  470.   Decode  : TZDecompressionStream;
  471. begin
  472.   {Read the keyword}
  473.   fKeyword := fStream.Memory;
  474.   {Get the size of the uncompressed text and resize the holder}
  475.   DSize := fStream.Size - Length(fKeyword) - 2;
  476.   GetMem(fText,DSize); { Paul - SetLength(fText, DSize); }
  477.   {Create a especial stream to decompress}
  478.   fStream.Position := Length(fKeyword) + 2;
  479.   Decode := TZDecompressionStream.Create(fStream);
  480.   Decode.Read(fText[0], DSize);
  481.   case Index of
  482.   0:
  483.     Result := fKeyword;
  484.   else
  485.     Result := ftext; { Paul - pchar(@fText[0]); }
  486.   end;
  487.   {Free that stream}
  488.   Decode.Free;
  489. end;
  490. {:Set the value of one of the properties}
  491. procedure TChunkZTXT.SetValue(Index: Integer; Value: String);
  492. var
  493.   fKeyword, fText: pchar;
  494.   Encode         : TZCompressionStream;
  495.   Method         : Byte;
  496. begin
  497.   {Test which property to set}
  498.   case Index of
  499.   0: begin
  500.       {Setting keyword}
  501.       fKeyword := pchar(Value);
  502.       fText := pchar(Text);
  503.     end;
  504.   else
  505.     begin
  506.       {Setting text}
  507.       fText := pchar(Value);
  508.       fKeyword := pchar(Keyword);
  509.     end;
  510.   end;
  511.   {Clear the stream for rewriting}
  512.   fStream.Clear;
  513.   fStream.Position := 0;
  514.   Method := 0;
  515.   {Write data}
  516.   fStream.Write(fKeyword[0], Length(fKeyword) + 1); {+1 to include null character}
  517.   fStream.Write(Method, 1);
  518.   Encode := TZCompressionStream.Create(fStream, zcDefault);
  519.   Encode.Write(fText[0], Length(fText));
  520.   Encode.Free;
  521. end;
  522. {:When the TEXT chunk is being created}
  523. constructor TChunkTEXT.Create(AOwner: TChunkList);
  524. begin
  525.   inherited;
  526.   fType := 'tEXt';
  527.   {Set the stream size to 2 and set the two bytes as null}
  528.   fStream.SetSize(2); { Paul - fStream.Size := 2; }
  529.   pByteArray(fStream.Memory)^[0] := 0;
  530.   pByteArray(fStream.Memory)^[1] := 0;
  531. end;
  532. {:Return one of the properties of the chunk TEXT}
  533. function TChunkTEXT.GetValue(Index: Integer): String;
  534. var
  535.   fKeyword, fText: pChar;
  536. begin
  537.   fKeyword := fStream.Memory;
  538.   fText := @pByteArray(fStream.Memory)[Length(fKeyword) + 1];
  539.   {Test which property to return}
  540.   case Index of
  541.   0: Result := fKeyword;
  542.   else
  543.      Result := fText;
  544.   end;
  545. end;
  546. {:Set the value of the TEXT chunk}
  547. procedure TChunkTEXT.SetValue(Index: Integer; Value: String);
  548. var
  549.   fKeyword, fText: pchar;
  550. begin
  551.   {Test which property to set}
  552.   case Index of
  553.   0: begin
  554.       {Setting keyword}
  555.       fKeyword := pchar(Value);
  556.       fText := pchar(Text);
  557.     end;
  558.   else
  559.     begin
  560.       {Setting text}
  561.       fText := pchar(Value);
  562.       fKeyword := pchar(Keyword);
  563.     end;
  564.   end;
  565.   {Clear the stream for rewriting}
  566.   fStream.Clear;
  567.   fStream.Position := 0;
  568.   {Write data}
  569.   fStream.Write(fKeyword[0], Length(fKeyword) + 1); {+1 to include null character}
  570.   fStream.Write(fText[0], Length(fText) + 1);
  571. end;
  572. {:When the object is being destroyed}
  573. destructor TChunkPLTE.Destroy;
  574. begin
  575.   {If the main bitmap is using the palette make it don't use it anymore}
  576.   if Owner.Owner.Palette = fPalette then
  577.     Owner.Owner.Palette := 0;
  578.   {Delete the palette from the memory}
  579.   DeleteObject(fPalette);
  580.   inherited;
  581. end;
  582. {Returns the palette from the image}
  583. function TChunkPLTE.GetPalette: HPalette;
  584. var
  585.   MaxPalette: TMaxLogPalette;
  586.   i: Integer;
  587.   GamaChunk : TChunkGAMA;
  588. begin
  589.   GamaChunk := Gama;
  590.   {Delete the old palette from the memory}
  591.   DeleteObject(fPalette);
  592.   {The palette stream must be divisible by 3}
  593.   if fStream.Size MOD 3 <> 0 then
  594.     CallError(PNG_ERROR_INVALID_PLTE);
  595.   {Set the MaxPalette attributes}
  596.   with MaxPalette do
  597.   begin
  598.     Fillchar(MaxPalette, sizeof(MaxPalette), 0);
  599.     palVersion := $300;
  600.     palNumEntries := fStream.Size DIV 3;
  601.     {Get each value}
  602.     FOR i := 0 to palNumEntries - 1 DO
  603.     WITH palPalEntry[i] do
  604.     BEGIN
  605.       peRed := pByteArray(fStream.Memory)[(i * 3)];
  606.       {Correct red using gamma}
  607.       if Assigned(GamaChunk) then
  608.         peRed := GamaChunk.GammaTable[peRed];
  609.       peGreen := pByteArray(fStream.Memory)[(i * 3) + 1];
  610.       {Correct green using gamma}
  611.       if Assigned(GamaChunk) then
  612.         peGreen := GamaChunk.GammaTable[peGreen];
  613.       peBlue := pByteArray(fStream.Memory)[(i * 3) + 2];
  614.       {Correct red using gamma}
  615.       if Assigned(GamaChunk) then
  616.         peBlue := GamaChunk.GammaTable[peBlue];
  617.       peFlags := 0;
  618.     END;
  619.     IF (IHDR.BitDepth = 2) and (palNumEntries < 16) then
  620.     begin
  621.       {Note: This is really a crazy fix for supporting 2bit}
  622.       {images}
  623.       palNumEntries := 16;
  624.       copymemory(@palpalentry[4], @palpalentry[0], 21);
  625.       copymemory(@palpalentry[8], @palpalentry[0], 21);
  626.       copymemory(@palpalentry[12], @palpalentry[0], 21);
  627.     end;
  628.   end;
  629.   {Create the palette object}
  630.   fPalette := CreatePalette(PLogPalette(@MaxPalette)^);
  631.   {Returns the palette handle}
  632.   Result := fPalette;
  633. end;
  634. {:When the chunk is being saved}
  635. procedure TChunkPLTE.SaveToStream(Stream: TStream);
  636. var
  637.   PaletteSize: Word;
  638.   LogPalette : TMaxLogPalette;
  639.   I          : Integer;
  640.   GamaChunk  : TChunkGama;
  641. begin
  642.   GamaChunk := Gama;
  643.   {Free the stream for rewritting}
  644.   fStream.Clear;
  645.   {If the image does not contains palette, exit}
  646.   if Owner.Owner.Palette = 0 then
  647.     exit
  648.   else
  649.   begin
  650.     {If it does, retrieve the palette}
  651.     {First discover the palette size}
  652.     GetObject(Bitmap.Palette, SizeOf(WORD), @PaletteSize);
  653.     {Now get the entries}
  654.     GetPaletteEntries(Bitmap.Palette, 0, PaletteSize,
  655.       LogPalette.palpalentry);
  656.     {Now write the entries to the stream}
  657.     FOR I := 0 TO PaletteSize - 1 DO
  658.     With LogPalette do
  659.     begin
  660.       {Test if uses gamma}
  661.       if Assigned(GamaChunk) then
  662.       begin
  663.         fStream.Write(GamaChunk.InverseTable[palPalEntry[i].peRed], 1);
  664.         fStream.Write(GamaChunk.InverseTable[palPalEntry[i].peGreen], 1);
  665.         fStream.Write(GamaChunk.InverseTable[palPalEntry[i].peBlue], 1);
  666.       end
  667.       else
  668.       begin
  669.         fStream.Write(palPalEntry[i].peRed, 1);
  670.         fStream.Write(palPalEntry[i].peGreen, 1);
  671.         fStream.Write(palPalEntry[i].peBlue, 1);
  672.       end;
  673.     end;
  674.   end;
  675.   {Call default writting}
  676.   inherited;
  677. end;
  678. {:Copy interlaced data into the current image}
  679. procedure TChunkIDAT.DecodeInterlacedRow(ImageData: Pointer; Data: pByteArray;
  680.   ColStart, ColIncrement, RowBytes, Pass: Integer; GamaChunk: TChunkGama);
  681. var
  682.   J, I: Integer;
  683. begin
  684.   I := ColStart;
  685.   J := 0;
  686.   {Test for color type}
  687.   CASE IHDR.ColorType of
  688.     Palette, Grayscale:
  689.     {Test for bit depth}
  690.     CASE IHDR.BitDepth of
  691.       2: {2 bits per pixel, not supported by TBitmap, so move to 4 bits}
  692.         ConvertBits([@Data[0]], ImageData, IHDR.Width, PassMask[Pass], 2, 4);
  693.       4: {4 bits per pixel}
  694.         ConvertBits([@Data[0]], ImageData, IHDR.Width, PassMask[Pass], 4, 4);
  695.       1: {1 bit per pixel}
  696.         ConvertBits([@Data[0]], ImageData, IHDR.Width, PassMask[Pass], 1, 1);
  697.       8: {1 byte per pixel}
  698.       repeat
  699.         pByteArray(ImageData)^[I] := Data^[J];
  700.         inc(J);
  701.         inc(I, ColIncrement);
  702.       until J >= RowBytes;
  703.      16:  {Grayscale interlaced images with 2 bytes per sample}
  704.       repeat
  705.         pByteArray(ImageData)^[I] := Data^[J];
  706.         inc(J, 2);
  707.         inc(I, ColIncrement);
  708.       until J >= RowBytes;
  709.     END;
  710.     RGB:
  711.     {Test for bit depth}
  712.     CASE IHDR.BitDepth of
  713.       8:   {1 byte per R, G, B}
  714.       repeat
  715.         with PRGBLine(ImageData)^[I] do
  716.         begin
  717.           rgbtRed := Data^[J];
  718.           rgbtGreen := Data^[J + 1];
  719.           rgbtBlue := Data^[J + 2];
  720.           {Gamma correction}
  721.           if Assigned(GamaChunk) then
  722.           begin
  723.             rgbtRed := GamaChunk.GammaTable[rgbtRed];
  724.             rgbtGreen := GamaChunk.GammaTable[rgbtGreen];
  725.             rgbtBlue := GamaChunk.GammaTable[rgbtBlue];
  726.           end;
  727.         end;
  728.         inc(J, 3);
  729.         inc(I, ColIncrement);
  730.       until J >= RowBytes;
  731.      16:   {2 bytes per R, G, B}
  732.       repeat
  733.         with PRGBLine(ImageData)^[I] do
  734.         begin
  735.           rgbtRed := Data^[J];
  736.           rgbtGreen := Data^[J + 2];
  737.           rgbtBlue := Data^[J + 4];
  738.           {Gamma correction}
  739.           if Assigned(GamaChunk) then
  740.           begin
  741.             rgbtRed := GamaChunk.GammaTable[rgbtRed];
  742.             rgbtGreen := GamaChunk.GammaTable[rgbtGreen];
  743.             rgbtBlue := GamaChunk.GammaTable[rgbtBlue];
  744.           end;
  745.         end;
  746.         inc(J, 6);
  747.         inc(I, ColIncrement);
  748.       until J >= RowBytes;
  749.     end;
  750.     RGBALPHA:
  751.     {Test for bit depth}
  752.     CASE IHDR.BitDepth of
  753.       8:   {1 byte per R, G, B, Alpha}
  754.       repeat
  755.         with PRGBLine(ImageData)^[I] do
  756.         begin
  757.           rgbtRed := Data^[J];
  758.           rgbtGreen := Data^[J + 1];
  759.           rgbtBlue := Data^[J + 2];
  760.           {Gamma correction}
  761.           if Assigned(GamaChunk) then
  762.           begin
  763.             rgbtRed := GamaChunk.GammaTable[rgbtRed];
  764.             rgbtGreen := GamaChunk.GammaTable[rgbtGreen];
  765.             rgbtBlue := GamaChunk.GammaTable[rgbtBlue];
  766.           end;
  767.         end;
  768.         inc(J, 4);
  769.         inc(I, ColIncrement);
  770.       until J >= RowBytes;
  771.      16:   {2 bytes per R, G, B, Alpha}
  772.       repeat
  773.         with PRGBLine(ImageData)^[I] do
  774.         begin
  775.           rgbtRed := Data^[J];
  776.           rgbtGreen := Data^[J + 2];
  777.           rgbtBlue := Data^[J + 4];
  778.           {Gamma correction}
  779.           if Assigned(GamaChunk) then
  780.           begin
  781.             rgbtRed := GamaChunk.GammaTable[rgbtRed];
  782.             rgbtGreen := GamaChunk.GammaTable[rgbtGreen];
  783.             rgbtBlue := GamaChunk.GammaTable[rgbtBlue];
  784.           end;
  785.         end;
  786.         inc(J, 8);
  787.         inc(I, ColIncrement);
  788.       until J >= RowBytes;
  789.     END;
  790.     GRAYSCALEALPHA:
  791.     {Test for bit depth}
  792.     CASE IHDR.BitDepth of
  793.       8:   {1 byte per gray and alpha}
  794.       repeat
  795.         pByteArray(ImageData)^[I] := Data^[J];
  796.         inc(J, 2);
  797.         inc(I, ColIncrement);
  798.       until J >= RowBytes;
  799.      16:   {2 bytes per gray and alpha}
  800.       repeat
  801.         pByteArray(ImageData)^[I] := Data^[J];
  802.         inc(J, 4);
  803.         inc(I, ColIncrement);
  804.       until J >= RowBytes;
  805.     END;
  806.   end;
  807. end;
  808. {:Copy non interlaced data into the current image}
  809. procedure TChunkIDAT.DecodeNonInterlacedRow(ImageData: Pointer; Data: pByteArray;
  810.   RowBytes: Integer; GamaChunk: TChunkGama);
  811. var
  812.   Col: Integer;
  813. begin
  814.   {Test for color type}
  815.   case IHDR.ColorType of
  816.     Palette, Grayscale:
  817.     {Test for bit depth}
  818.     CASE IHDR.BitDepth of
  819.       1, 4, 8:  {Simple memory copy}
  820.         CopyMemory(ImageData, Data, RowBytes);
  821.       2: {Pixelformat pf2bits ? not supported (pf4bits being used) }
  822.         ConvertBits([@Data[0]], ImageData, Bitmap.Width, $FF, 2, 4);
  823.      16: {Grayscale with 2 pixels}
  824.       FOR Col := 0 to Bitmap.Width - 1  DO
  825.         pByteArray(ImageData)^[Col] := Data^[Col * 2];
  826.     END;
  827.     RGB:
  828.     {Test for bit depth}
  829.     CASE IHDR.BitDepth of
  830.       8: {1 byte for each R, G AND B values}
  831.       FOR Col := 0 to (Bitmap.Width - 1)  DO
  832.         with PRGBLine(ImageData)^[Col] do
  833.         begin
  834.           rgbtRed := Data^[Col * 3];
  835.           rgbtGreen := Data^[1 + Col * 3];
  836.           rgbtBlue := Data^[2 + Col * 3];
  837.           {Gamma correction}
  838.           if Assigned(GamaChunk) then
  839.           begin
  840.             rgbtRed := GamaChunk.GammaTable[rgbtRed];
  841.             rgbtGreen := GamaChunk.GammaTable[rgbtGreen];
  842.             rgbtBlue := GamaChunk.GammaTable[rgbtBlue];
  843.           end;
  844.         end;
  845.      16: {2 bytes for each R, G AND B values}
  846.       FOR Col := 0 to (Bitmap.Width - 1)  DO
  847.         with PRGBLine(ImageData)^[Col] do
  848.         begin
  849.           rgbtRed := Data^[Col * 6];
  850.           rgbtGreen := Data^[2 + Col * 6];
  851.           rgbtBlue := Data^[4 + Col * 6];
  852.           {Gamma correction}
  853.           if Assigned(GamaChunk) then
  854.           begin
  855.             rgbtRed := GamaChunk.GammaTable[rgbtRed];
  856.             rgbtGreen := GamaChunk.GammaTable[rgbtGreen];
  857.             rgbtBlue := GamaChunk.GammaTable[rgbtBlue];
  858.           end;
  859.         end;
  860.      end;
  861.     RGBALPHA:
  862.     {Test for bit depth}
  863.     CASE IHDR.BitDepth of
  864.       8: {1 byte for each R, G, B AND ALPHA values}
  865.       FOR Col := 0 to (Bitmap.Width - 1)  DO
  866.         with PRGBLine(ImageData)^[Col] do
  867.         begin
  868.           rgbtRed := Data^[Col * 4];
  869.           rgbtGreen := Data^[1 + Col * 4];
  870.           rgbtBlue := Data^[2 + Col * 4];
  871.           {Gamma correction}
  872.           if Assigned(GamaChunk) then
  873.           begin
  874.             rgbtRed := GamaChunk.GammaTable[rgbtRed];
  875.             rgbtGreen := GamaChunk.GammaTable[rgbtGreen];
  876.             rgbtBlue := GamaChunk.GammaTable[rgbtBlue];
  877.           end;
  878.         end;
  879.      16: {2 bytes for each R, G AND B values and 1 for ALPHA}
  880.       FOR Col := 0 to (Bitmap.Width - 1)  DO
  881.         with PRGBLine(ImageData)^[Col] do
  882.         begin
  883.           rgbtRed := Data^[Col * 8];
  884.           rgbtGreen := Data^[2 + Col * 8];
  885.           rgbtBlue := Data^[4 + Col * 8];
  886.           {Gamma correction}
  887.           if Assigned(GamaChunk) then
  888.           begin
  889.             rgbtRed := GamaChunk.GammaTable[rgbtRed];
  890.             rgbtGreen := GamaChunk.GammaTable[rgbtGreen];
  891.             rgbtBlue := GamaChunk.GammaTable[rgbtBlue];
  892.           end;
  893.         end;
  894.     end;
  895.     GRAYSCALEALPHA:
  896.     {Test for bit depth}
  897.     CASE IHDR.BitDepth of
  898.       8: {1 byte for grayscale and 1 for alpha}
  899.       FOR Col := 0 to (Bitmap.Width - 1)  DO
  900.         pByteArray(ImageData)^[Col] := Data^[Col * 2];
  901.      16: {2 bytes for grayscale and 1 for alpha}
  902.       FOR Col := 0 to (Bitmap.Width - 1)  DO
  903.         pByteArray(ImageData)^[Col] := Data^[Col * 4];
  904.     end;
  905.   end;
  906. end;
  907. {:Decode the readed image to the bitmap}
  908. procedure TChunkIDAT.DoAction;
  909. const
  910.   CHAR_BIT = 8;
  911. var
  912.   RowBuffer        : array[Boolean] of pbytearray;
  913.   Row_Buffer_Width : Integer;
  914.   OddLine          : Boolean;
  915.   Offset           : Integer;
  916.   UseProgress      : Boolean;
  917.   j                : Integer;
  918.   Pass             : Integer;
  919.   Decode           : TZDecompressionStream;
  920.   Row              : Integer;
  921.   PixelsThisRow    : Integer;
  922.   RowBytes         : Integer;
  923.   GamaChunk        : TChunkGama;
  924. begin
  925.   GamaChunk := Gama;
  926.   {Create the decompression object}
  927.   Decode := TZDecompressionStream.Create(fStream);
  928.   Decode.Position := 0;
  929.   rowbytes := 0;
  930.   {Filtering is done on corresponding items within a record. Determine}
  931.   {the number of bytes between corresponding items.                   }
  932.   OffSet := GetOffSet;
  933.   {Define if uses OnProgress}
  934.   UseProgress := Assigned(Bitmap.Onprogress);
  935.   {Retrieve the number of bytes per line}
  936.   row_buffer_width := GetBufferWidth;
  937.   {Allocate memory for the row buffers and fill them with zeros}
  938.   OddLine := TRUE;
  939.   GetMem(RowBuffer[True], row_buffer_width + 1);
  940.   GetMem(RowBuffer[False], row_buffer_width + 1);
  941.   ZeroMemory(RowBuffer[False], row_buffer_width + 1);
  942.   {Set the bitmap properties}
  943.   with Bitmap do
  944.   begin
  945.     {Setup pixel formats and palette}
  946.     SetupPixelFormat;
  947.     {Set width and height}
  948.     Width := IHDR.Width;
  949.     Height := IHDR.Height;
  950.   end;
  951.   {Interlace decode}
  952.   if IHDR.Interlaced = 1 then
  953.   begin
  954.     {Each of the interlacing passes}
  955.     FOR Pass := 0 TO 6 DO
  956.     begin
  957.       {Number of pixels in this row}
  958.       pixelsthisrow := (Bitmap.width - ColumnStart[Pass] +
  959.         + ColumnIncrement[Pass] - 1) div ColumnIncrement[Pass] ;
  960.       {Number of bytes}
  961.       case (IHDR.ColorType) of
  962.       Grayscale, Palette:
  963.         rowbytes := (pixelsthisrow * IHDR.BitDepth + CHAR_BIT - 1) div CHAR_BIT ;
  964.       RGB:
  965.         rowbytes := pixelsthisrow * 3 * IHDR.BitDepth div CHAR_BIT ;
  966.       RGBAlpha:
  967.         rowbytes := pixelsthisrow * 4 * IHDR.BitDepth div CHAR_BIT ;
  968.       GrayscaleAlpha:
  969.         rowbytes := pixelsthisrow * 2 * IHDR.BitDepth div CHAR_BIT ;
  970.       end;
  971.       Row := RowStart[Pass];
  972.       while Row < Bitmap.Height do
  973.       begin
  974.         {Read line from the stream}
  975.         Decode.Read(rowBuffer[OddLine][0], rowbytes + 1);
  976.         {Filter the row}
  977.         FilterRow(RowBuffer[OddLine][0], @RowBuffer[OddLine][1],
  978.           @RowBuffer[not OddLine][1], offset, rowbytes);
  979.         {Translate data into the image}
  980.         DecodeInterlacedRow(Bitmap.ScanLine[Row], @RowBuffer[OddLine][1],
  981.           ColumnStart[Pass], ColumnIncrement[Pass], RowBytes, Pass, Gamachunk);
  982.         {Jump to the next line}
  983.         Inc(Row, RowIncrement[Pass]);
  984.         {Change the line}
  985.         OddLine := not OddLine;
  986.       end;
  987.       {Call progress event}
  988.       If UseProgress then
  989.         Bitmap.OnProgress(Bitmap, psRunning, MulDiv(100, Pass, 6),
  990.           True, Rect(0, 0, Bitmap.Width, Bitmap.Height), 'Drawing...');
  991.     end;
  992.   end
  993.   {Non interlace decode}
  994.   else if IHDR.Interlaced = 0 then
  995.   begin
  996.     {Pass each row}
  997.     for j := 0 to Bitmap.Height - 1 DO
  998.     begin
  999.       {Decompress}
  1000.       Decode.Read(RowBuffer[OddLine][0], row_buffer_width + 1);
  1001.       {Filter the current row}
  1002.       FilterRow(RowBuffer[OddLine][0], @RowBuffer[OddLine][1],
  1003.         @RowBuffer[not OddLine][1], OffSet, row_buffer_width);
  1004.       {Translate the data into the image}
  1005.       DecodeNonInterlacedRow(Bitmap.Scanline[j], @RowBuffer[OddLine][1],
  1006.         row_buffer_width, GamaChunk);
  1007.       {Change the line}
  1008.       OddLine := not OddLine;
  1009.       {Call progress event}
  1010.       If UseProgress then
  1011.           Bitmap.OnProgress(Bitmap, psRunning, MulDiv(j, 100, Bitmap.Height),
  1012.             True, Rect(0, j - 1, Bitmap.Width, j), 'Drawing...');
  1013.     end;
  1014.   end
  1015.   else
  1016.   {Unknown interlace method}
  1017.     CallError(PNG_ERROR_INVALID_INTERLACE);
  1018.   {Free memory for the row buffers}
  1019.   FreeMem(RowBuffer[True], row_buffer_width + 1);
  1020.   FreeMem(RowBuffer[False], row_buffer_width + 1);
  1021.   {Free the decompression object}
  1022.   Decode.Free;
  1023.   {$IFDEF SHAREWARE} Shareware {$ENDIF};
  1024. end;
  1025. {:Returns the buffer width}
  1026. function TChunkIDAT.GetBufferWidth: Integer;
  1027. const
  1028.   CHAR_BIT = 8;
  1029. var
  1030.   RowBits         : Integer;
  1031. begin
  1032.   Result := 0;
  1033.   case IHDR.ColorType of
  1034.   Grayscale, Palette:
  1035.   begin
  1036.     rowbits := IHDR.Width * IHDR.BitDepth;
  1037.     Result := (rowbits + CHAR_BIT - 1) div CHAR_BIT;
  1038.   end;
  1039.   GrayscaleAlpha:
  1040.     Result := 2 * IHDR.width * IHDR.BitDepth div CHAR_BIT ;
  1041.   RGB:
  1042.     Result := IHDR.width * 3 * IHDR.BitDepth div CHAR_BIT ;
  1043.   RGBAlpha:
  1044.     Result := IHDR.width * 4 * IHDR.BitDepth div CHAR_BIT ;
  1045.   else
  1046.     {In case we have an undetermined color type}
  1047.     CallError(PNG_ERROR_INVALID_COLOR_TYPE);
  1048.   end;
  1049. end;
  1050. {:Returns the offset for filtering}
  1051. function TChunkIDAT.GetOffset: Integer;
  1052. const
  1053.   CHAR_BIT = 8;
  1054. begin
  1055.   case IHDR.ColorType of
  1056.     Grayscale, Palette:      result := 1;
  1057.     RGB:                     result := 3 * IHDR.BitDepth div CHAR_BIT ;
  1058.     GrayscaleAlpha:          result := 2 * IHDR.BitDepth div CHAR_BIT ;
  1059.     RGBAlpha:                result := 4 * IHDR.BitDepth div CHAR_BIT ;
  1060.     else
  1061.       result := 0;
  1062.   end;
  1063. end;
  1064. {:Filter the row for encoding}
  1065. function TChunkIDAT.EncodeFilterRow(row_buffer: pbytearray;
  1066.   Filter_buffers: TFilterRow; row_width, filter_width: Cardinal): Integer;
  1067. const
  1068.   FTest: Array[0..4] of TEncodeFilter = (efNone, efSub, efUp,
  1069.     efAverage, efPaeth);
  1070. var
  1071.   ii, run, jj: Cardinal;
  1072.   longestrun : Cardinal;
  1073.  last,
  1074.  above,
  1075.  lastabove   : byte;
  1076. begin
  1077.   // Filter for each type in the filter_mask.
  1078.   if efSub in Bitmap.Filter then
  1079.   begin
  1080.     for ii := 0 to row_width - 1 do
  1081.     begin
  1082.       if (ii >= filter_width) then
  1083.         last := row_buffer^[ii-filter_width]
  1084.       else
  1085.         last := 0 ;
  1086.       filter_buffers [FILTERSUB]^[ii] := row_buffer^[ii] - last ;
  1087.     end;
  1088.   end;
  1089.   if efUp in Bitmap.Filter then
  1090.     for ii := 0 to row_width - 1 do
  1091.       filter_buffers[FILTERUP]^[ii] := row_buffer^[ii] -
  1092.         filter_buffers[FILTERNONE]^[ii] ;
  1093.   if efAverage in Bitmap.Filter then
  1094.   begin
  1095.     for ii := 0 to row_width - 1 do
  1096.     begin
  1097.       if (ii >= filter_width) then
  1098.         last := row_buffer^[ii - filter_width]
  1099.       else
  1100.         last := 0 ;
  1101.       above := filter_buffers [FILTERNONE]^[ii] ;
  1102.       filter_buffers [FILTERAVERAGE]^[ii]
  1103.         := row_buffer^[ii] - (above + last) div 2 ;
  1104.     end;
  1105.   end;
  1106.   if efPaeth in Bitmap.Filter then
  1107.   begin
  1108.     for ii := 0 to row_width - 1 do
  1109.     begin
  1110.       if (ii >= filter_width) then
  1111.       begin
  1112.         last := row_buffer^[ii-filter_width] ;
  1113.         lastabove := filter_buffers [FILTERNONE]^[ii - filter_width] ;
  1114.       end
  1115.       else
  1116.       begin
  1117.         last := 0 ;
  1118.         lastabove := 0 ;
  1119.       end;
  1120.       above := filter_buffers [FILTERNONE]^[ii] ;
  1121.       filter_buffers [FILTERPAETH]^[ii]
  1122.         := row_buffer^[ii] - PaethPredictor (last, above, lastabove) ;
  1123.     end;
  1124.   end;
  1125.   // Filter None
  1126.   // THIS MUST BE THE LAST FILTER!!!!!!!!!! We save the value
  1127.   // here to be used in the next call with the filters that require data from the
  1128.   // previous row.
  1129.   for ii := 0 to row_width - 1 do
  1130.     filter_buffers[FILTERNONE]^[ii] := row_buffer^[ii] ;
  1131.   // If we only performed FilterNone then we do not need to proceed
  1132.   // any further.
  1133.   Result := FILTERNONE ;
  1134.   if Bitmap.Filter = [efNone] then
  1135.     exit;
  1136.   // Find the best filter. We do a simple test for the
  1137.   // longest runs of the same value.
  1138.   LongestRun := 0;
  1139.   for ii := 0 to FILTERBUFFERCOUNT - 1 DO
  1140.   begin
  1141.     if FTest[ii] in Bitmap.Filter then
  1142.     begin
  1143.       run := 0;
  1144.       for jj := 4 to row_width - 1 do
  1145.       begin
  1146.         if (filter_buffers[ii]^[jj] = filter_buffers [ii]^[jj-1]) and
  1147.             (filter_buffers[ii]^[jj] = filter_buffers [ii]^[jj-2]) and
  1148.             (filter_buffers[ii]^[jj] = filter_buffers [ii]^[jj-3]) and
  1149.             (filter_buffers[ii]^[jj] = filter_buffers [ii]^[jj-4]) then
  1150.           inc(Run);
  1151.       end;
  1152.       if (run > longestrun) then
  1153.       begin
  1154.         result := ii ;
  1155.         longestrun := run ;
  1156.       end;
  1157.     end;
  1158.   end;
  1159. end;
  1160. {:Encode the actual image from the bitmap}
  1161. procedure TChunkIDAT.EncodeImage;
  1162. var
  1163.   Encode          : TZCompressionStream;
  1164.   j, offset, i    : Integer;
  1165.   row_buffer_width: Integer;
  1166.   filter_buffers  : TFilterRow;
  1167.   Filter          : byte;
  1168.   row_buffer      : pByteArray;
  1169.   Line            : Pointer;
  1170.   GamaChunk       : TChunkGama;
  1171.   function AdjustValue(Value: Byte): Byte;
  1172.   begin
  1173.     if Assigned(GamaChunk) then
  1174.       Result := GamaChunk.InverseTable[Value]
  1175.     else
  1176.       Result := Value;
  1177.   end;
  1178. begin
  1179.   GamaChunk := Gama;
  1180.   {Clear the previous IDAT memory since we will use bitmap}
  1181.   {data to write all over again}
  1182.   fStream.Clear;
  1183.   {Create a stream to handle the compression}
  1184.   Encode := TZCompressionStream.Create(fStream, zcDefault);
  1185.   {Number of bytes in each row}
  1186.   row_buffer_width := GetBufferWidth;
  1187.   offset := GetOffset;
  1188.   {Allocate memory for filtering}
  1189.   GetMem(row_buffer, row_buffer_width);
  1190.   GetMem(filter_buffers[FILTERNONE], row_buffer_width);
  1191.   if efSub in Bitmap.Filter then
  1192.     GetMem(filter_buffers[FILTERSUB], row_buffer_width);
  1193.   if efUp in Bitmap.Filter then
  1194.     GetMem(filter_buffers[FILTERUP], row_buffer_width);
  1195.   if efAverage in Bitmap.Filter then
  1196.     GetMem(filter_buffers[FILTERAVERAGE], row_buffer_width);
  1197.   if efPaeth in Bitmap.Filter then
  1198.     GetMem(filter_buffers[FILTERPAETH], row_buffer_width);
  1199.   {Fill the filternone with zeros}
  1200.   ZeroMemory(@filter_buffers[FILTERNONE][0], row_buffer_width);
  1201.   Bitmap.Interlacing := FALSE;
  1202.   
  1203.   {Testing encoding method}
  1204.   if Bitmap.Interlacing then
  1205.   {No interlacing}
  1206.   begin
  1207.   end
  1208.   else
  1209.   {Interlacing}
  1210.   begin
  1211.     {Pass each row}
  1212.     for j := 0 to Bitmap.Height - 1 do
  1213.     begin
  1214.       {Write depending on the pixel format}
  1215.       case Bitmap.PixelFormat of
  1216.       pf1bit, pf4bit, pf8bit:
  1217.         filter := EncodeFilterRow(Bitmap.ScanLine[j], filter_buffers,
  1218.           row_buffer_width, offset);
  1219.       else
  1220.       begin
  1221.         {Copy pointer to the line bytes}
  1222.         Line := Bitmap.ScanLine[j];
  1223.         {Test the pixel format}
  1224.         case Bitmap.PixelFormat of
  1225.           {3 bytes, just swap}
  1226.           pf24bit:
  1227.           FOR i := 0 to Bitmap.Width - 1 do
  1228.           begin
  1229.             Row_Buffer^[i * 3] := AdjustValue(pRGBLine(Line)^[i].rgbtRed);
  1230.             Row_Buffer^[1 + (i * 3)] := AdjustValue(pRGBLine(Line)^[i].rgbtGreen);
  1231.             Row_Buffer^[2 + (i * 3)] := AdjustValue(pRGBLine(Line)^[i].rgbtBlue);
  1232.           end;
  1233.           {4 bytes, swap and ignore last byte unused}
  1234.           pf32bit:
  1235.           FOR i := 0 to Bitmap.Width - 1 do
  1236.           begin
  1237.             Row_Buffer^[i * 4] := AdjustValue(pRGBALine(Line)^[i].rgbRed);
  1238.             Row_Buffer^[1 + (i * 4)] := AdjustValue(pRGBALine(Line)^[i].rgbGreen);
  1239.             Row_Buffer^[2 + (i * 4)] := AdjustValue(pRGBALine(Line)^[i].rgbBlue);
  1240.           end;
  1241.         end;
  1242.         {Filter the row}
  1243.         filter := EncodeFilterRow(@Row_Buffer[0], filter_buffers,
  1244.           row_buffer_width, offset);
  1245.       end;
  1246.       end;
  1247.       (*Write to stream*)
  1248.       Encode.Write(Filter, 1);
  1249.       Encode.Write(filter_buffers[Filter]^[0], row_buffer_width);
  1250.     end;
  1251.   end;
  1252.   {Free the compression stream}
  1253.   Encode.Free;
  1254.   {Free memory from the filters}
  1255.   FreeMem(row_buffer, row_buffer_width);
  1256.   FreeMem(filter_buffers[FILTERNONE], row_buffer_width);
  1257.   if efSub in Bitmap.Filter then
  1258.     FreeMem(filter_buffers[FILTERSUB], row_buffer_width);
  1259.   if efUp in Bitmap.Filter then
  1260.     FreeMem(filter_buffers[FILTERUP], row_buffer_width);
  1261.   if efAverage in Bitmap.Filter then
  1262.     FreeMem(filter_buffers[FILTERAVERAGE], row_buffer_width);
  1263.   if efPaeth in Bitmap.Filter then
  1264.     FreeMem(filter_buffers[FILTERPAETH], row_buffer_width);
  1265.   {$IFDEF SHAREWARE} Shareware {$ENDIF};
  1266. end;
  1267. {:Adjust image pixel format}
  1268. procedure TChunkIDAT.SetupPixelFormat;
  1269. var
  1270.   PlteIndex, i     : Integer;
  1271.   GrayscalePal     : TMaxLogPalette;
  1272.   GAMACHUNK        : TChunkGAMA;
  1273. begin
  1274.   (*{In case we need an alpha channel bitmap}
  1275.   if (IHDR.ColorType = GrayscaleALpha) or
  1276.     (IHDR.ColorType = RGBAlpha) then
  1277.   begin
  1278.     {Free the old mask}
  1279.     FreeAndNil(Bitmap.fMask);
  1280.     {Create a new bitmap}
  1281.     Bitmap.fMask := TBitmap.Create;
  1282.     {Set its properties}
  1283.     with Bitmap.fMask do
  1284.     begin
  1285.       Width := IHDR.Width;
  1286.       Height := IHDR.Height;
  1287.       PixelFormat := pf8bit;
  1288.     end;
  1289.   end; *)
  1290.   {Retrieve the chunk GAMA}
  1291.   GamaChunk := Gama;
  1292.   {Set the pixel formats}
  1293.   CASE IHDR.ColorType of
  1294.     GrayScale, Palette, GrayScaleAlpha:
  1295.     CASE IHDR.BitDepth of
  1296.       1: Bitmap.PixelFormat := pf1bit;     {1 bit, 2 colors: 2^1}
  1297.       2: Bitmap.PixelFormat := pf4bit;
  1298.       4: Bitmap.PixelFormat := pf4bit;
  1299.       8: Bitmap.PixelFormat := pf8bit;     {1 byte in each pixel, 256 colors}
  1300.      16: Bitmap.PixelFormat := pf8bit;     {2 bytes per sample}
  1301.     END;
  1302.     RGB, RGBALPHA:
  1303.     CASE IHDR.BitDepth of
  1304.       8: Bitmap.PixelFormat := pf24bit;    {R, G, B values for each pixel}
  1305.      16: Bitmap.PixelFormat := pf24bit;    {Increased range of values for RGB}
  1306.     END;
  1307.   END;
  1308.   {Create the palettes for the file formats}
  1309.   CASE IHDR.ColorType of
  1310.   Grayscale, GrayscaleAlpha:
  1311.  {Create grayscale palette}
  1312.   with GrayscalePal do
  1313.   begin
  1314.     palVersion := $300;
  1315.     {Set the number of colors in palette}
  1316.     {Since the max is 256 colors 16bit per sample pixels will be}
  1317.     {averanged to 8}
  1318.     if IHDR.BitDepth = 16 then
  1319.       palNumEntries := 256
  1320.     else
  1321.       palNumEntries := (1 shl IHDR.BitDepth);
  1322.     {Set the palette colors}
  1323.     FOR i := 0 to palNumEntries - 1 DO
  1324.       WITH palPalEntry[i] do
  1325.       begin              //cr
  1326.         {Average the colors}
  1327.         {When i is 0, the color is black}
  1328.         {When i is palNumEntries, the color is white}
  1329.         peRed := MulDiv(i, 255, palNumEntries - 1);
  1330.         {Correct using gamma}
  1331.         if Assigned(GamaChunk) then
  1332.           peRed := GamaChunk.GammaTable[peRed];
  1333.         peGreen := peRed;
  1334.         peBlue := peGreen;
  1335.         peFlags := PC_NOCOLLAPSE;
  1336.       end;
  1337.     IF (IHDR.BitDepth = 2) and (palNumEntries < 16) then
  1338.     begin
  1339.       {Note: This is really a crazy totally nonsence fix for supporting 2bit}
  1340.       palNumEntries := 16;
  1341.       copymemory(@palpalentry[4], @palpalentry[0], 21);
  1342.       copymemory(@palpalentry[8], @palpalentry[0], 21);
  1343.       copymemory(@palpalentry[12], @palpalentry[0], 21);
  1344.     end;
  1345.     {Apply the bitmap palette}
  1346.     Bitmap.Palette := CreatePalette(PLogPalette(@GrayscalePal)^);
  1347.   end;
  1348.   Palette:
  1349.   BEGIN
  1350.     {Test if there is PLTE chunk, if so apply the palette}
  1351.     PlteIndex := Owner.IndexOfClass(TChunkPLTE); { Paul }
  1352.     if PlteIndex <> -1 then
  1353.       Bitmap.Palette := TChunkPLTE(Owner[PlteIndex]).Palette
  1354.     ELSE
  1355.       CallError(PNG_ERROR_NO_PALETTE);
  1356.   END;
  1357.   END;
  1358. end;
  1359. {:Filters the row using definied types}
  1360. procedure TChunkIDAT.FilterRow(Filter: Byte; CurrentRow, LastRow: pByteArray;
  1361.   offset, row_buffer_width: Integer);
  1362. var
  1363.   Col: Integer;  {Current Column}
  1364.   Left, Above, AboveLeft: Integer;
  1365.   vv, pp: Integer;
  1366. begin
  1367.   // Filter the row based upon the filter type.
  1368.   case filter of
  1369.     {No filtering, do nothing}
  1370.     FILTERNONE: begin end;
  1371.     {Sub filter}
  1372.     FILTERSUB:
  1373.     {The value is the difference from the value to the left}
  1374.     for col := offset to row_buffer_width - 1 do
  1375.       CurrentRow[col] := (CurrentRow[col] + CurrentRow[col-offset]) AND $FF;
  1376.     FILTERUP:
  1377.     {The value is the difference from the value in the previous row.}
  1378.     for col := 0 to row_buffer_width - 1 do
  1379.       CurrentRow[col] := (CurrentRow[col] + LastRow[col]) AND $FF ;
  1380.     FILTERAVERAGE:
  1381.     for col := 0 to row_buffer_width - 1 do
  1382.     begin
  1383.       above := LastRow[col];
  1384.       if (col < offset) then
  1385.         left := 0
  1386.       else
  1387.         left := CurrentRow[col-offset] ;
  1388.       CurrentRow[col] := (CurrentRow[col] + (left + above) div 2) AND $FF ;
  1389.     end;
  1390.     FILTERPAETH:
  1391.     for col := 0 to row_buffer_width - 1 do
  1392.     begin
  1393.       above := LastRow[col] ;
  1394.       if (col < offset) then
  1395.       begin
  1396.         left := 0 ;
  1397.         aboveleft := 0 ;
  1398.       end
  1399.       else
  1400.       begin
  1401.         left := CurrentRow[col-offset] ;
  1402.         aboveleft := LastRow[col-offset] ;
  1403.       end;
  1404.       vv := CurrentRow[col] ;
  1405.       pp := PaethPredictor(left, above, aboveleft) ;
  1406.       CurrentRow[col] := (pp + vv) AND $FF ;
  1407.     end;
  1408.     else
  1409.       {In case the filter is not reconized}
  1410.       CallError(PNG_ERROR_INVALID_FILTER_TYPE);
  1411.   end; {Case}
  1412. end;
  1413. {:When the chunk is going to be saved to a stream}
  1414. procedure TChunkIDAT.SaveToStream(Stream: TStream);
  1415. begin
  1416.   {Set to encode the image to the data}
  1417.   EncodeImage;
  1418.   {Then write}
  1419.   inherited;
  1420. end;
  1421. {Assign data from one gama chunk}
  1422. procedure TChunkGAMA.Assign(Source: TChunk);
  1423. begin
  1424.   inherited; // fix 1
  1425.   GammaTable := TChunkGAMA(Source).GammaTable;
  1426.   InverseTable := TChunkGAMA(Source).InverseTable;
  1427. end;
  1428. {When the object is being created}
  1429. constructor TChunkGAMA.Create(AOwner: TChunkList);
  1430. begin
  1431.   inherited;
  1432.   {Set the size of the stream and initial value}
  1433.   fStream.SetSize(4);
  1434.   Value := 1;
  1435. end;
  1436. function Power(Base, Exponent: extended): extended;
  1437. begin
  1438.     if Base > 0 then
  1439.        Power := Exp(Exponent * Ln(Base) )
  1440.     else
  1441.         Power := 0;
  1442. end;
  1443. {:Creates a gamma table for using}
  1444. procedure TChunkGAMA.DoAction;
  1445. var
  1446.   I    : Integer;
  1447.   lX: Extended;
  1448. begin
  1449.   {Create gamma table and inverse gamma table (for saving)}
  1450.   FOR I := 0 TO 255 DO
  1451.   begin
  1452.     if Value = 0 then //problem with Adobe PNG images
  1453.       lX := 0.00001//random(255) +1
  1454.     else
  1455.         lX := Value;
  1456.     GammaTable[I] := Round(Power((I / 255), 1 / (lX / 100000 * 2.2)) * 255);
  1457.     InverseTable[Round(Power((I / 255), 1 / (lX / 100000 * 2.2)) * 255)] := I;
  1458.   end;
  1459. end;
  1460. {Returns the Gama value}
  1461. function TChunkGAMA.GetValue: Cardinal;
  1462. begin
  1463.   Result := SwapLong(pCardinal(fStream.Memory)^);
  1464. end;
  1465. {Sets the Gama value}
  1466. procedure TChunkGAMA.SetValue(Value: Cardinal);
  1467. begin
  1468.   pCardinal(fStream.Memory)^ := SwapLong(Value);
  1469. end;
  1470. {:When the chunk is being saved}
  1471. procedure TChunkIHDR.SaveToStream(Stream: TStream);
  1472. begin
  1473.   {Set the IHDR chunk properties}
  1474.     Compression := 0; {The only compression method avaliable}
  1475.     Filter := 0;      {The only filter scheme avaliable}
  1476.     if Owner.Owner.Interlacing then  {Interlace method}
  1477.       Interlaced := 1           {ADAM 7}
  1478.     else
  1479.       Interlaced := 0;          {NONE}
  1480.     Width := Owner.Owner.Width;
  1481.     Height := Owner.Owner.Height;
  1482.     {Color type}
  1483.     case Owner.Owner.PixelFormat of
  1484.       pf1bit, pf4bit, pf8bit:
  1485.       begin
  1486.         {Palette}
  1487.         ColorType := PALETTE;
  1488.         {Bit depth}
  1489.         case Owner.Owner.PixelFormat of
  1490.           pf1bit: BitDepth := 1;
  1491.           pf4bit: BitDepth := 4;
  1492.           pf8bit: BitDepth := 8;
  1493.         end;
  1494.       end;
  1495.       else
  1496.       begin
  1497.         {R, G, B}
  1498.         Owner.Owner.PixelFormat := pf24bit;
  1499.         ColorType := RGB;
  1500.         BitDepth := 8;
  1501.       end;
  1502.     end;
  1503.   inherited;
  1504. end;
  1505. {Get values for the other properties}
  1506. function TChunkIHDR.GetValue(Index: Integer): Byte;
  1507. begin
  1508.   case Index of
  1509.     0: {Bit depth}   Result := pIHDRChunk(fStream.Memory)^.BitDepth;
  1510.     1: {Color type}  Result := pIHDRChunk(fStream.Memory)^.ColorType;
  1511.     2: {Compression} Result := pIHDRChunk(fStream.Memory)^.Compression;
  1512.     3: {Filter}      Result := pIHDRChunk(fStream.Memory)^.Filter;
  1513.     4: {Interlaced}  Result := pIHDRChunk(fStream.Memory)^.Interlaced;
  1514.     else {Avoid warning}
  1515.       Result := 0;
  1516.   end;
  1517. end;
  1518. {Set value for the other properties}
  1519. procedure TChunkIHDR.SetValue(Index: Integer; Value: Byte);
  1520. begin
  1521.   case Index of
  1522.     0: {Bit depth}   pIHDRChunk(fStream.Memory)^.BitDepth := Value;
  1523.     1: {Color type}  pIHDRChunk(fStream.Memory)^.ColorType := Value;
  1524.     2: {Compression} pIHDRChunk(fStream.Memory)^.Compression := Value;
  1525.     3: {Filter}      pIHDRChunk(fStream.Memory)^.Filter := Value;
  1526.     4: {Interlaced}  pIHDRChunk(fStream.Memory)^.Interlaced := Value;
  1527.   end;
  1528. end;
  1529. {Returns the image height}
  1530. function TChunkIHDR.GetHeight: Cardinal;
  1531. begin
  1532.   Result := SwapLong(pIHDRChunk(fStream.Memory)^.Height);
  1533. end;
  1534. {Returns the image width}
  1535. function TChunkIHDR.GetWidth: Cardinal;
  1536. begin
  1537.   Result := SwapLong(pIHDRChunk(fStream.Memory)^.Width);
  1538. end;
  1539. {Sets the image height}
  1540. procedure TChunkIHDR.SetHeight(Value: Cardinal);
  1541. begin
  1542.   pIHDRChunk(fStream.Memory)^.Height := SwapLong(Value);
  1543.   {Changes the image size}
  1544.   if Owner.Owner.Height <> Int(Value) then
  1545.     Owner.Owner.Height := Value;
  1546. end;
  1547. {Sets the image width}
  1548. procedure TChunkIHDR.SetWidth(Value: Cardinal);
  1549. begin
  1550.   pIHDRChunk(fStream.Memory)^.Width := SwapLong(Value);
  1551.   {Changes the image size}
  1552.   if Owner.Owner.Width <> Int(Value) then
  1553.     Owner.Owner.Width := Value;
  1554. end;
  1555. {:When the object is being created}
  1556. constructor TChunkIHDR.Create(AOwner: TChunkList);
  1557. begin
  1558.   inherited;
  1559.   {Resize the IHDR chunk}
  1560.   fStream.SetSize(13);
  1561. end;
  1562. {:Returns the index of the chunk class}
  1563. function TChunkClasses.IndexOfClass(Item: TChunkClass): Integer; { Paul }
  1564. var
  1565.   i: Integer;
  1566. begin
  1567.   {If none found, return -1}
  1568.   Result := -1;
  1569.   {Test each class}
  1570.   if Count > 0 then
  1571.     FOR i := 0 to Count - 1 DO
  1572.       if Self.Item[I].ChunkClass = Item then
  1573.       begin
  1574.         Result := i;
  1575.         break;
  1576.       end;
  1577. end;
  1578. {:Returns the index of the given chunk type}
  1579. function TChunkClasses.IndexOfType(Item: TChunkType): Integer; { Paul }
  1580. var
  1581.   i: Integer;
  1582. begin
  1583.   {If none found, return -1}
  1584.   Result := -1;
  1585.   {Test each class}
  1586.   if Count > 0 then
  1587.     FOR i := 0 to Count - 1 DO
  1588.       if Self.Item[I].ChunkType = Item then
  1589.       begin
  1590.         Result := i;
  1591.         break;
  1592.       end;
  1593. end;
  1594. {:When the object is being destroyed}
  1595. destructor TChunkClasses.Destroy;
  1596. var
  1597.   i: Integer;
  1598. begin
  1599.   FOR i := 0 TO Count - 1 DO
  1600.     Dispose(pChunkClassInfo(fList[i]));
  1601.   inherited;
  1602. end;
  1603. {:Returns an item from the list}
  1604. function TChunkClasses.GetItem(Index: Integer): TChunkClassInfo;
  1605. begin
  1606.   {Test if the index is valid}
  1607.   if (Index < 0) or (Index > Count - 1) then
  1608.     CallError(PNG_ERROR_INVALID_CHUNK_CLASS_INDEX);
  1609.     Result := pChunkClassInfo(fList[Index])^;
  1610. end;
  1611. {Returns the number of items in the list}
  1612. function TChunkClasses.GetCount: Integer;
  1613. begin
  1614.   {If the list object exists, then return the count from it}
  1615.   {otherwise returns 0                                     }
  1616.   if Assigned(fList) then
  1617.     Result := fList.Count
  1618.   else
  1619.     Result := 0;
  1620. end;
  1621. {:Add a new chunk class to the list of classes}
  1622. procedure TChunkClasses.Add(ChunkType: TChunkType;
  1623.   ChunkClass: TChunkClass);
  1624. var
  1625.   NewItem: pChunkClassInfo;
  1626. begin
  1627.   {Create the list if it does not exists}
  1628.   if not Assigned(fList) then
  1629.     fList := TList.Create;
  1630.   {Allocate memory for the new item}
  1631.   New(NewItem);
  1632.   {Set the new item properties}
  1633.   NewItem^.ChunkType := ChunkType;
  1634.   NewItem^.ChunkClass := ChunkClass;
  1635.   {Add to the list}
  1636.   fList.Add(NewItem);
  1637. end;
  1638. {Do the action when the chunk is read}
  1639. procedure TChunk.DoAction;
  1640. begin
  1641.   inherited;
  1642. end;
  1643. {Returns a pointer to the png image owner}
  1644. function TChunk.GetBitmap: TPNGImage;
  1645. begin
  1646.   Result := Owner.Owner;
  1647. end;
  1648. {Returns a pointer to the GAMA}
  1649. function TChunk.GetGAMA: TChunkGAMA;
  1650. var
  1651.   Pos: Integer;
  1652. begin
  1653.   {Position of the chunk}
  1654.   Pos := Owner.IndexOfClass(TChunkGAMA); { Paul }
  1655.   {Returns nil if the chunk does not exists}
  1656.   if Pos = -1 then
  1657.     Result := nil
  1658.   else
  1659.     Result := TChunkGAMA(Owner[Pos]);
  1660. end;
  1661. {Returns a pointer to the IHDR}
  1662. function TChunk.GetIHDR: TChunkIHDR;
  1663. begin
  1664.   Result := TChunkIHDR(Owner[0]);
  1665. end;
  1666. {:Assign from another chunk}
  1667. procedure TChunk.Assign(Source: TChunk);
  1668. begin
  1669.   {Clear the current stream}
  1670.   fStream.Clear;
  1671.   {Copy data from the other stream}
  1672.   fStream.CopyFrom(Source.fStream, 0);
  1673.   {Copy the chunk name}
  1674.   fType := Source.fType;
  1675. end;
  1676. {:Returns the chunk size}
  1677. function TChunk.GetSize: Integer;
  1678. begin
  1679.   Result := fStream.Size;
  1680. end;
  1681. {:Saves the chunk data to the stream}
  1682. procedure TChunk.SaveToStream(Stream: TStream);
  1683. var
  1684.   ChunkLen: Cardinal;
  1685.   ChunkCRC: Cardinal;
  1686. begin
  1687.   {The chunk is not safe-to-copy}
  1688.   if ChunkType[3] = LowerCase(ChunkType[3]) then
  1689.     exit;
  1690.   {First the chunk length}
  1691.   ChunkLen := SwapLong(fStream.Size);
  1692.   Stream.Write(ChunkLen, 4);
  1693.   {Now write the chunk type}
  1694.   Stream.Write(fType, 4);
  1695.   {Write the chunk data}
  1696.   Stream.CopyFrom(fStream, 0);
  1697.   {Calculate and write the CRC}
  1698.   ChunkCRC := SwapLong(CRC(fType, fStream.Memory, fStream.Size));
  1699.   Stream.Write(ChunkCRC, 4);
  1700. end;
  1701. {Retrieve the chunk index inside the list}
  1702. function TChunk.GetIndex: Integer;
  1703. begin
  1704.   Result := Owner.IndexOfChunk(Self); { Paul }
  1705. end;
  1706. {:Called when the object is being created}
  1707. constructor TChunk.Create(AOwner: TChunkList);
  1708. var
  1709.   ClassPos: Integer;
  1710. begin
  1711.   {Create the stream containg the memory data}
  1712.   fStream := TMemoryStream.Create;
  1713.   fList := AOwner;
  1714.   {Default class name}
  1715.   ClassPos := ChunkClasses.IndexOfClass(TChunkClass(ClassType)); { Paul }
  1716.   if ClassPos <> -1 then
  1717.     fType := ChunkClasses[ClassPos].ChunkType;
  1718. end;
  1719. {:Called when the object is being destroyed}
  1720. destructor TChunk.Destroy;
  1721. begin
  1722.   {Free the stream containing the memory data}
  1723.   fStream.Free;
  1724.   inherited;
  1725. end;
  1726. {:Move one chunk position in the list}
  1727. procedure TChunkList.Move(Index1, Index2: Integer);
  1728. begin
  1729.   {Test for index}
  1730.   if (Index1 < 0) or (Index1 >= Count) then
  1731.     CallError(PNG_ERROR_INVALID_CHUNK_INDEX);
  1732.   FList.Move(Index1, Index2);
  1733. end;
  1734. {Returns the number of items in the list (Used with Count property)}
  1735. function TChunkList.GetCount: Integer;
  1736. begin
  1737.   Result := fList.Count;
  1738. end;
  1739. {Returns an item from the list (Used with Item property)}
  1740. function TChunkList.GetItem(Index: Integer): TChunk;
  1741. begin
  1742.   {Test if the chunk index is valid}
  1743.   if (Index < 0) or (Index > Count - 1) then
  1744.     CallError(PNG_ERROR_INVALID_CHUNK_INDEX);
  1745.   {If so, return the item}
  1746.   Result := fList[Index];
  1747. end;
  1748. {:Removes a chunk}
  1749. procedure TChunkList.Remove(Item: TChunk);
  1750. begin
  1751.   {Makes sure that the list contains the chunk}
  1752.   if Item.Owner <> Self then
  1753.     CallError(CHUNK_NOT_CHILD);
  1754.   {Delete the chunk}
  1755.   FList.Delete(Item.Index);
  1756.   Item.Free;
  1757. end;
  1758. {:Add a chunk to the list when the chunk object ALREADY EXISTS}
  1759. function TChunkList.AddItem(Item: TChunk): TChunk; { Paul }
  1760. begin
  1761.   {Add the item to the list}
  1762.   fList.Add(Item);
  1763.   Result := Item;
  1764. end;
  1765. {:Returns the index of the first chunk of the type in the parameter}
  1766. function TChunkList.IndexOfClass(ChunkClass: TChunkClass): Integer; { Paul }
  1767. var
  1768.   I: Integer;
  1769. begin
  1770.   {Returns -1 if none found}
  1771.   Result := -1;
  1772.   {If there are items in the list, test each item}
  1773.   if Count > 0 then
  1774.     FOR i := 0 TO Count - 1 DO
  1775.       if Item[I].ClassType = ChunkClass then
  1776.       begin
  1777.         result := i;
  1778.         break;
  1779.       end;
  1780. end;
  1781. {:Returns the position of a chunk inside the list}
  1782. function TChunkList.IndexOfChunk(Chunk: TChunk): Integer; { Paul }
  1783. begin
  1784.   Result := fList.IndexOf(Chunk);
  1785. end;
  1786. {:Add a chunk to the list when the chunk object DOES NOT EXISTS
  1787.   but it already knows which chunk class to create}
  1788. function TChunkList.AddClass(ChunkClass: TChunkClass): TChunk; { Paul }
  1789. begin
  1790.   Result := AddItem(ChunkClass.Create(Self));
  1791. end;
  1792. {:Add a chunk to the list when the chunk data needs to be readed
  1793.  from a stream.                                                }
  1794. function TChunkList.AddStream(Stream: TStream): TChunk; { Paul }
  1795. var
  1796.   CLength: Cardinal;
  1797.   CType  : TChunkType;
  1798.   CCRC   : Cardinal;
  1799.   i, p   : Integer;
  1800. begin
  1801.   {First read the chunk length}
  1802.   Stream.Read(CLength, 4);
  1803.   CLength := SwapLong(CLength);
  1804.   {Now read the chunk type}
  1805.   Stream.Read(CType, 4);
  1806.   {Look for chunk classes supporting the given chunk type}
  1807.   i := ChunkClasses.IndexOfType(CType); { Paul }
  1808.   {Test if the chunk is critical but unknown}
  1809.   if ((Byte(CType[0]) AND $20) = 0) and (i = -1) then
  1810.     CallError(PNG_ERROR_UNKOWN_CRITICAL_CHUNK);
  1811.   {If the chunk type exists in the list, then create an object  }
  1812.   {using the class found, otherwise use the generic TChunk class}
  1813.   if i <> - 1 then
  1814.     Result := ChunkClasses[I].ChunkClass.Create(Self)
  1815.   else
  1816.     Result := TChunk.Create(Self);
  1817.   {Copy the chunk type}
  1818.   Result.fType := CType;
  1819.   {Read the data if the chunk contains data}
  1820.   if CLength > 0 then
  1821.     Result.fStream.CopyFrom(Stream, CLength);
  1822.   {Read the CRC for checking}
  1823.   Stream.Read(CCRC, 4);
  1824.   CCRC := SwapLong(CCRC);
  1825.   {Test if the CRC is valid}
  1826.   if CRC(CType, Result.fStream.Memory, CLength) <> CCRC then
  1827.     CallError(PNG_ERROR_CHUNK_INVALID_CRC);
  1828.   {If there are already IDAT chunks, then mix the actual IDAT}
  1829.   {being readed with the previous IDAT}
  1830.   if (Result is TChunkIDAT) then
  1831.     p := IndexOfClass(TChunkIDAT) { Paul }
  1832.   else
  1833.     p := -1;
  1834.   if (Result is TChunkIDAT) and (p <> -1) then
  1835.   begin
  1836.     {Copy data to the old stream}
  1837.     Item[p].fStream.CopyFrom(Result.fStream, 0);
  1838.     {Free the actual IDAT stream and returns the last}
  1839.     Result.Free;
  1840.     Result := Item[p];
  1841.   end
  1842.   else {Add the item to the list}
  1843.     Result := AddItem(Result); { Paul }
  1844. end;
  1845. {:Clear all the chunks in the list}
  1846. procedure TChunkList.Clear;
  1847. var
  1848.   i: Integer;
  1849. begin
  1850.   {If there are items in the list, delete each one}
  1851.   if Count > 0 then
  1852.     FOR i := Count - 1 DOWNTO 0 DO
  1853.     BEGIN
  1854.       {Free the chunk and delete from the list}
  1855.       Item[i].Free;
  1856.       FList.Delete(I);
  1857.     END;
  1858. end;
  1859. {:Called when the object is being created}
  1860. constructor TChunkList.Create(AOwner: TPNGImage);
  1861. begin
  1862.   {Copy the TPNGImage owner pointer}
  1863.   fImage := AOwner;
  1864.   {Create the TList}
  1865.   fList := TList.Create;
  1866. end;
  1867. {:Called when the object is being destroyed}
  1868. destructor TChunkList.Destroy;
  1869. begin
  1870.   {Clear and free the TList}
  1871.   Clear;
  1872.   fList.Free;
  1873.   inherited;
  1874. end;
  1875. {:Special override for assigning other TPNGImages}
  1876. procedure TPNGImage.Assign(Source: TPersistent);
  1877. var
  1878.   SourcePNG: TPNGImage;
  1879.   i, j     : Integer;
  1880. begin
  1881.   {If the source is also a TPNGImage, copy the chunks}
  1882.   if Source is TPNGImage then
  1883.   begin
  1884.     SourcePNG := TPNGImage(Source);
  1885.     {Clear current chunks}
  1886.     Chunks.Clear;
  1887.     {Copy the chunks}
  1888.     if SourcePNG.Chunks.Count > 0 then
  1889.     FOR i := 0 TO SourcePNG.Chunks.Count - 1 DO
  1890.     begin
  1891.       j := Chunkclasses.IndexOfType(SourcePNG.Chunks[i].fType); { Paul }
  1892.       {If the class is a know class, create it using that class}
  1893.       {otherwise with the default TChunk class}
  1894.       if j <> -1 then
  1895.         Chunks.AddItem(Chunkclasses[j].ChunkClass.Create(Chunks)).Assign(SourcePNG.Chunks[i]) { Paul }
  1896.       else
  1897.         Chunks.AddItem(TChunk.Create(Chunks)).Assign(SourcePNG.Chunks[i]); { Paul }
  1898.     end;
  1899.     {Copy other info}
  1900.     Filter := SourcePNG.fEncodeFilter;
  1901.     Interlacing := SourcePNG.fInterlacing;
  1902.   end;
  1903.   inherited;
  1904. end;
  1905. {:Called when the object is being created}
  1906. constructor TPNGImage.Create;
  1907. begin
  1908.   inherited;
  1909.   fMask := nil;
  1910.   {Create the list of chunks object}
  1911.   fChunkList := TChunkList.Create(Self);
  1912.   fInterlacing := FALSE;
  1913.   Filter := [efNone, efSub, efAverage, efPaeth];
  1914.   {Create the standard chunks}
  1915.   Clear;
  1916. end;
  1917. {:Called when the object is being destroyed}
  1918. destructor TPNGImage.Destroy;
  1919. begin
  1920.   {Free the mask if assigned}
  1921.   if Assigned(fMask) then
  1922.     fMask.Free;
  1923.   {Destroy the list of chunks object}
  1924.   fChunkList.Free;
  1925.   inherited;
  1926. end;
  1927. {Set the filters that are going to be used when encoding}
  1928. procedure TPNGImage.SetFilter(Value: TEncodeFilterSet);
  1929. begin
  1930.   {efNone is the only value that the set must have}
  1931.   if not (efNone in Value) then
  1932.     Include(Value, efNone);
  1933.   fEncodeFilter := Value;
  1934. end;
  1935. {:Clears the current image}
  1936. procedure TPNGImage.Clear;
  1937. begin
  1938.   {Clear the current chunks}
  1939.   Chunks.Clear;
  1940.   with TChunkIHDR(Chunks.AddClass(TChunkIHDR)) do { Paul }
  1941.   begin
  1942.     Width := 0;
  1943.     Height := 0;
  1944.     BitDepth := 2;
  1945.     ColorType := 3;
  1946.     Compression := 0;
  1947.     Filter := 0;
  1948.     Interlaced := 0;
  1949.   end;
  1950.   {Clears the palette}
  1951.   Palette := 0;
  1952.   {Add IDAT chunk}
  1953.   Chunks.AddClass(TChunkIDAT); { Paul }
  1954.   {Add IEND chunk}
  1955.   Chunks.AddClass(TChunkIEND); { Paul }
  1956. end;
  1957. {:Saves the current PNG image to the stream}
  1958. procedure TPNGImage.SaveToStream(Stream: TStream);
  1959. var
  1960.   i: Integer;
  1961. begin
  1962.   {Do the actual writting}
  1963.   with Stream do
  1964.   begin
  1965.     {Write the valid header}
  1966.     Write(PNGHeader, 8);
  1967.     {If there are no chunks, then create the standard ones}
  1968.     if Chunks.Count = 0 then
  1969.       Clear;
  1970.     {Ensure that there is a IHDR chunk}
  1971.     if (Chunks.Count = 0) or (not (Chunks[0] is TChunkIHDR)) then
  1972.       Chunks.Move(Chunks.AddClass(TChunkIHDR).Index, 0); { Paul }
  1973.     {PLTE chunk needed}
  1974.     if ((PixelFormat = pf1bit) or (PixelFormat = pf4bit) or
  1975.       (PixelFormat = pf8bit)) and (Chunks.IndexOfClass(TChunkPLTE) = -1) then { Paul }
  1976.       Chunks.Move(Chunks.AddClass(TChunkPLTE).Index, 1); { Paul }
  1977.     {If the image needs TRNS chunk}
  1978.     if Transparent then
  1979.       Chunks.Move(Chunks.AddClass(TChunkTRNS).Index, 1) { Paul }
  1980.     {If transparency is not being used, delete the transparency chunk(s)}
  1981.     else if Chunks.IndexOfClass(TChunkTRNS) <> -1 then { Paul }
  1982.       repeat
  1983.         Chunks.Remove(Chunks[Chunks.IndexOfClass(TChunkTRNS)]); { Paul }
  1984.       until Chunks.IndexOfClass(TChunkTRNS) = -1; { Paul }
  1985.     {Make sures that there is a IEND chunk}
  1986.     if Chunks.IndexOfClass(TChunkIEND) = -1 then { Paul }
  1987.       Chunks.AddClass(TChunkIEND); { Paul }
  1988.     {Make sures that there is a IDAT chunk}
  1989.     if Chunks.IndexOfClass(TChunkIDAT) = -1 then { Paul }
  1990.       Chunks.Move(Chunks.AddClass(TChunkIDAT).Index, 1); { Paul }
  1991.     {Write each chunk}
  1992.     FOR i := 0 to Chunks.Count -1 DO
  1993.       Chunks[i].SaveToStream(Stream);
  1994.   end;
  1995. end;
  1996. {:Loads a PNG image from the stream}
  1997. procedure TPNGImage.LoadFromStream(Stream: TStream);
  1998. var
  1999.   ReadHeader: TPNGHeader;
  2000.   i         : Integer;
  2001. begin
  2002.   {Clear the current chunks}
  2003.   Clear;
  2004.   Chunks.Clear;
  2005.   {Do the actual reading}
  2006.   with Stream do
  2007.   begin
  2008.     {Read the PNG file header for checking}
  2009.     Read(ReadHeader, 8);
  2010.     if not CompareMem(@ReadHeader, @PNGHeader, 8) then
  2011.       CallError(PNG_ERROR_INVALID_HEADER);
  2012.     {Read the chunks}
  2013.     while (not (Chunks.AddStream(Stream) is TChunkIEND)) and { Paul }
  2014.       not (Stream.Position = Stream.Size) do
  2015.     begin end;
  2016.     {Test if IHDR is the first chunk}
  2017.     if (Chunks.Count = 0) or not (Chunks[0] is TChunkIHDR) then
  2018.       CallError(PNG_ERROR_IHDR_NOT_FIRST);
  2019.     {Test if there is IDAT chunk, if so, decode it}
  2020.     if Chunks.IndexOfClass(TChunkIDAT) = -1 then { Paul }
  2021.       CallError(PNG_ERROR_NO_IDAT);
  2022.     {Execute each chunks action}
  2023.       FOR i := 0 to Chunks.Count - 1 DO
  2024.         Chunks[i].DoAction;
  2025.     {Test if there is tRNS chunk, if so, apply the transparency}
  2026.     if Chunks.IndexOfClass(TChunkTRNS) <> -1 then { Paul }
  2027.     case TChunkIHDR(Chunks[0]).ColorType of
  2028.       RGB:
  2029.       begin
  2030.         TransparentColor :=
  2031.           TChunkTRNS(Chunks[Chunks.IndexOfClass(TChunkTRNS)]).GetRGBColor; { Paul }
  2032.         Transparent := TRUE;
  2033.       end;
  2034.     end;
  2035.  end; {with}
  2036. end;
  2037. {:Register a new chunk class}
  2038. class procedure TPNGImage.RegisterChunkClass(ChunkType: TChunkType;
  2039.   ChunkClass: TChunkClass);
  2040. begin
  2041.   RegisterNewChunkClass(ChunkType, ChunkClass);
  2042. end;
  2043. procedure ConvertBits(Source: array of Pointer; Target: Pointer;
  2044.   Count: Cardinal; Mask: Byte; FSourceBPS, FTargetBPS: Byte);
  2045. var
  2046.   SourceRun, TargetRun: PByte;
  2047.   Value, BitRun,TargetMask, SourceMask,  SourceShift, TargetShift, MaxInSample,
  2048.   MaxOutSample,  SourceBPS, TargetBPS: Byte;
  2049.   Done: Cardinal;
  2050. begin
  2051.   SourceRun := Source[0]; TargetRun := Target;
  2052.     BitRun := $80; SourceBPS := FSourceBPS; TargetBPS := FTargetBPS;
  2053.     SourceMask := Byte(not ((1 shl (8 - SourceBPS)) - 1));
  2054.     MaxInSample := (1 shl SourceBPS) - 1;
  2055.     TargetMask := (1 shl (8 - TargetBPS)) - 1;
  2056.     MaxOutSample := (1 shl TargetBPS) - 1;
  2057.     SourceShift := 8;  TargetShift := 8 - TargetBPS;   Done := 0;
  2058.     while Done < Count do
  2059.     begin
  2060.       if Boolean(Mask and BitRun) then
  2061.       begin
  2062.         Dec(SourceShift, SourceBPS);
  2063.         Value := (SourceRun^ and SourceMask) shr SourceShift;
  2064.         Value := MulDiv(Value, MaxOutSample, MaxInSample);
  2065.         TargetRun^ := (TargetRun^ and TargetMask) or (Value shl TargetShift);
  2066.         if SourceShift = 0 then
  2067.         begin
  2068.           SourceShift := 8;
  2069.           Inc(SourceRun);
  2070.         end;
  2071.         asm
  2072.           MOV CL, [SourceBPS]
  2073.           ROR BYTE PTR [SourceMask], CL
  2074.         end;
  2075.       end;
  2076.       asm
  2077.         ROR BYTE PTR [BitRun], 1
  2078.         MOV CL, [TargetBPS]
  2079.         ROR BYTE PTR [TargetMask], CL
  2080.       end;
  2081.       if TargetShift = 0 then
  2082.         TargetShift := 8 - TargetBPS
  2083.       else
  2084.         Dec(TargetShift, TargetBPS);
  2085.       Inc(Done);
  2086.       if (Done mod (8 div TargetBPS)) = 0 then Inc(TargetRun);
  2087.     end;
  2088. end;
  2089. var
  2090. (* Table of CRCs of all 8-bit messages. *)
  2091. crc_table: array[0..255] of Cardinal;
  2092. (* Flag: has the table been computed? Initially false. *)
  2093. crc_table_computed: Integer = 0;
  2094. (*: Make the table for a fast CRC. *)
  2095. procedure make_crc_table;
  2096. var
  2097.   c   : Cardinal;
  2098.   n, k: Integer;
  2099. begin
  2100.   for n := 0 to 255 do
  2101.   begin
  2102.      c := n;
  2103.      for k := 0 to 7 do
  2104.      begin
  2105.        if boolean(c and 1) then
  2106.          c := $edb88320 xor (c shr 1)
  2107.        else
  2108.          c := c shr 1;
  2109.      end;
  2110.      crc_table[n] := c;
  2111.   end;
  2112.   crc_table_computed := 1;
  2113. end;
  2114. (*: Update a running CRC with the bytes buf[0..len-1]--the CRC
  2115.    should be initialized to all 1's, and the transmitted value
  2116.    is the 1's complement of the final running CRC (see the
  2117.    crc() routine below)). *)
  2118. function update_crc(chunktype: tchunktype; crc: Cardinal; buf: pByteArray;
  2119.   len: Integer): Cardinal;
  2120. var
  2121.   c: Cardinal absolute crc;
  2122.   n: Integer;
  2123. begin
  2124.   if not boolean(crc_table_computed) then
  2125.     make_crc_table;
  2126.   for n := 0 to 3 do
  2127.     c := crc_table[(c XOR ord(chunktype[n])) AND $ff] XOR (c SHR 8);
  2128.   for n := 0 to Len - 1 do
  2129.     c := crc_table[(c XOR buf[n]) AND $ff] XOR (c SHR 8);
  2130.   Result := C;
  2131. end;
  2132. (*: Return the CRC of the bytes buf[0..len-1]. *)
  2133. function crc(chunktype: tchunktype; buf: pbytearray; len: Integer): Cardinal;
  2134. begin
  2135.   result := update_crc(chunktype, Cardinal($ffffffff), buf, len) xor Cardinal($ffffffff); { Paul }
  2136. end;
  2137. {When the compiled unit is being initialized}
  2138. initialization
  2139.   ChunkClasses := TChunkClasses.Create;
  2140.   {Register the chunk classes}
  2141.   RegisterNewChunkClass('IEND', TChunkIEND);
  2142.   RegisterNewChunkClass('IHDR', TChunkIHDR);
  2143.   RegisterNewChunkClass('gAMA', TChunkGAMA);
  2144.   RegisterNewChunkClass('IDAT', TChunkIDAT);
  2145.   RegisterNewChunkClass('PLTE', TChunkPLTE);
  2146.   RegisterNewChunkClass('tEXt', TChunkTEXT);
  2147.   RegisterNewChunkClass('tRNS', TChunkTRNS);
  2148.   RegisterNewChunkClass('tIME', TChunkTRNS);
  2149.   {Register the graphical class}
  2150.   TPicture.RegisterFileFormat('PNG', 'Portable Graphics Network', TPNGImage);
  2151. {When the compiled unit is being finalized}
  2152. finalization
  2153.   ChunkClasses.Free;
  2154.   {Unregister the graphical class}
  2155.   TPicture.UnregisterGraphicClass(TPNGImage);
  2156. end.