pngimage.pas
上传用户:ctlcnc
上传日期:2021-12-10
资源大小:4933k
文件大小:166k
- {Portable Network Graphics Delphi 1.5 (29 June 2005) }
- {This is the latest implementation for TPngImage component }
- {It's meant to be a full replacement for the previous one. }
- {There are lots of new improvements, including cleaner code, }
- {full partial transparency support, speed improvements, }
- {saving using ADAM 7 interlacing, better error handling, also }
- {the best compression for the final image ever. And now it's }
- {truly able to read about any png image. }
- //---------------------------------------------------------------------------
- // Notice from Lifepower (lifepower@mail333.com):
- //---------------------------------------------------------------------------
- // This file has modification - to use "asphyrezlib.pas" instead
- // of "pnglang.pas". This is used with the permission of the auhtor.
- //---------------------------------------------------------------------------
- {
- Version 1.5
- 2005-29-06 - Fixed a lot of bugs using tips from mails that I've
- being receiving for some time
- BUG 1 - Loosing palette when assigning to TBitmap. fixed
- BUG 2 - SetPixels and GetPixels worked only with
- parameters in range 0..255. fixed
- BUG 3 - Force type address off using directive
- BUG 4 - TChunkzTXt contained an error
- BUG 5 - MaxIdatSize was not working correctly (fixed thanks
- to Gabriel Corneanu
- BUG 6 - Corrected german translation (thanks to Mael Horz)
- And the following improvements:
- IMPROVE 1 - Create ImageHandleValue properties as public in
- TChunkIHDR to get access to this handle
- IMPROVE 2 - Using SetStretchBltMode to improve stretch quality
- IMPROVE 3 - Scale is now working for alpha transparent images
- IMPROVE 4 - GammaTable propery is now public to support an
- article in the help file
- Version 1.4361
- 2003-03-04 - Fixed important bug for simple transparency when using
- RGB, Grayscale color modes
- Version 1.436
- 2003-03-04 - * NEW * Property Pixels for direct access to pixels
- * IMPROVED * Palette property (TPngObject) (read only)
- Slovenian traslation for the component (Miha Petelin)
- Help file update (scanline article/png->jpg example)
- Version 1.435
- 2003-11-03 - * NEW * New chunk implementation zTXt (method AddzTXt)
- * NEW * New compiler flags to store the extra 8 bits
- from 16 bits samples (when saving it is ignored), the
- extra data may be acessed using ExtraScanline property
- * Fixed * a bug on tIMe chunk
- French translation included (Thanks to IBE Software)
- Bugs fixed
- Version 1.432
- 2002-08-24 - * NEW * A new method, CreateAlpha will transform the
- current image into partial transparency.
- Help file updated with a new article on how to handle
- partial transparency.
- Version 1.431
- 2002-08-14 - Fixed and tested to work on:
- C++ Builder 3
- C++ Builder 5
- Delphi 3
- There was an error when setting TransparentColor, fixed
- New method, RemoveTransparency to remove image
- BIT TRANSPARENCY
- Version 1.43
- 2002-08-01 - * NEW * Support for Delphi 3 and C++ Builder 3
- Implements mostly some things that were missing,
- a few tweaks and fixes.
- Version 1.428
- 2002-07-24 - More minor fixes (thanks to Ian Boyd)
- Bit transparency fixes
- * NEW * Finally support to bit transparency
- (palette / rgb / grayscale -> all)
- Version 1.427
- 2002-07-19 - Lots of bugs and leaks fixed
- * NEW * method to easy adding text comments, AddtEXt
- * NEW * property for setting bit transparency,
- TransparentColor
- Version 1.426
- 2002-07-18 - Clipboard finally fixed (hope)
- Changed UseDelphi trigger to UseDelphi
- * NEW * Support for bit transparency bitmaps
- when assigning from/to TBitmap objects
- Altough it does not support drawing transparent
- parts of bit transparency pngs (only partial)
- it is closer than ever
- Version 1.425
- 2002-07-01 - Clipboard methods implemented
- Lots of bugs fixed
- Version 1.424
- 2002-05-16 - Scanline and AlphaScanline are now working correctly.
- New methods for handling the clipboard
- Version 1.423
- 2002-05-16 - * NEW * Partial transparency for 1, 2, 4 and 8 bits is
- also supported using the tRNS chunk (for palette and
- grayscaling).
- New bug fixes (Peter Haas).
- Version 1.422
- 2002-05-14 - Fixed some critical leaks, thanks to Peter Haas tips.
- New translation for German (Peter Haas).
- Version 1.421
- 2002-05-06 - Now uses new ZLIB version, 1.1.4 with some security
- fixes.
- LoadFromResourceID and LoadFromResourceName added and
- help file updated for that.
- The resources strings are now located in pnglang.pas.
- New translation for Brazilian Portuguese.
- Bugs fixed.
- IMPORTANT: I'm currently looking for bugs on the library. If
- anyone has found one, please send me an email and
- I will fix right away. Thanks for all the help and
- ideias I'm receiving so far.}
- {My new email is: gubadaud@terra.com.br}
- {Website link : pngdelphi.sourceforge.net}
- {Gustavo Huffenbacher Daud}
- unit pngimage;
- interface
- {Triggers avaliable (edit the fields bellow)}
- {$TYPEDADDRESS OFF}
- {$DEFINE UseDelphi} //Disable fat vcl units (perfect to small apps)
- {$DEFINE ErrorOnUnknownCritical} //Error when finds an unknown critical chunk
- {$DEFINE CheckCRC} //Enables CRC checking
- {.$DEFINE RegisterGraphic} //Registers TPNGObject to use with TPicture
- {.$DEFINE PartialTransparentDraw} //Draws partial transparent images
- {.$DEFINE Store16bits} //Stores the extra 8 bits from 16bits/sample
- {.$DEFINE Debug} //For programming purposes
- {$RANGECHECKS OFF} {$J+}
- uses
- Windows {$IFDEF UseDelphi}, Classes, Graphics, SysUtils{$ENDIF} {$IFDEF Debug},
- dialogs{$ENDIF}, asphyrezlib, pnglang;
- {$IFNDEF UseDelphi}
- const
- soFromBeginning = 0;
- soFromCurrent = 1;
- soFromEnd = 2;
- {$ENDIF}
- const
- {ZLIB constants}
- ZLIBErrors: Array[-6..2] of string = ('incompatible version (-6)',
- 'buffer error (-5)', 'insufficient memory (-4)', 'data error (-3)',
- 'stream error (-2)', 'file error (-1)', '(0)', 'stream end (1)',
- 'need dictionary (2)');
- Z_NO_FLUSH = 0;
- Z_FINISH = 4;
- Z_STREAM_END = 1;
- {Avaliable PNG filters for mode 0}
- FILTER_NONE = 0;
- FILTER_SUB = 1;
- FILTER_UP = 2;
- FILTER_AVERAGE = 3;
- FILTER_PAETH = 4;
- {Avaliable color modes for PNG}
- COLOR_GRAYSCALE = 0;
- COLOR_RGB = 2;
- COLOR_PALETTE = 3;
- COLOR_GRAYSCALEALPHA = 4;
- COLOR_RGBALPHA = 6;
- type
- {$IFNDEF UseDelphi}
- {Custom exception handler}
- Exception = class(TObject)
- constructor Create(Msg: String);
- end;
- ExceptClass = class of Exception;
- TColor = ColorRef;
- {$ENDIF}
- {Error types}
- EPNGOutMemory = class(Exception);
- EPngError = class(Exception);
- EPngUnexpectedEnd = class(Exception);
- EPngInvalidCRC = class(Exception);
- EPngInvalidIHDR = class(Exception);
- EPNGMissingMultipleIDAT = class(Exception);
- EPNGZLIBError = class(Exception);
- EPNGInvalidPalette = class(Exception);
- EPNGInvalidFileHeader = class(Exception);
- EPNGIHDRNotFirst = class(Exception);
- EPNGNotExists = class(Exception);
- EPNGSizeExceeds = class(Exception);
- EPNGMissingPalette = class(Exception);
- EPNGUnknownCriticalChunk = class(Exception);
- EPNGUnknownCompression = class(Exception);
- EPNGUnknownInterlace = class(Exception);
- EPNGNoImageData = class(Exception);
- EPNGCouldNotLoadResource = class(Exception);
- EPNGCannotChangeTransparent = class(Exception);
- EPNGHeaderNotPresent = class(Exception);
- type
- {Direct access to pixels using R,G,B}
- TRGBLine = array[word] of TRGBTriple;
- pRGBLine = ^TRGBLine;
- {Same as TBitmapInfo but with allocated space for}
- {palette entries}
- TMAXBITMAPINFO = packed record
- bmiHeader: TBitmapInfoHeader;
- bmiColors: packed array[0..255] of TRGBQuad;
- end;
- {Transparency mode for pngs}
- TPNGTransparencyMode = (ptmNone, ptmBit, ptmPartial);
- {Pointer to a cardinal type}
- pCardinal = ^Cardinal;
- {Access to a rgb pixel}
- pRGBPixel = ^TRGBPixel;
- TRGBPixel = packed record
- B, G, R: Byte;
- end;
- {Pointer to an array of bytes type}
- TByteArray = Array[Word] of Byte;
- pByteArray = ^TByteArray;
- {Forward}
- TPNGObject = class;
- pPointerArray = ^TPointerArray;
- TPointerArray = Array[Word] of Pointer;
- {Contains a list of objects}
- TPNGPointerList = class
- private
- fOwner: TPNGObject;
- fCount : Cardinal;
- fMemory: pPointerArray;
- function GetItem(Index: Cardinal): Pointer;
- procedure SetItem(Index: Cardinal; const Value: Pointer);
- protected
- {Removes an item}
- function Remove(Value: Pointer): Pointer; virtual;
- {Inserts an item}
- procedure Insert(Value: Pointer; Position: Cardinal);
- {Add a new item}
- procedure Add(Value: Pointer);
- {Returns an item}
- property Item[Index: Cardinal]: Pointer read GetItem write SetItem;
- {Set the size of the list}
- procedure SetSize(const Size: Cardinal);
- {Returns owner}
- property Owner: TPNGObject read fOwner;
- public
- {Returns number of items}
- property Count: Cardinal read fCount write SetSize;
- {Object being either created or destroyed}
- constructor Create(AOwner: TPNGObject);
- destructor Destroy; override;
- end;
- {Forward declaration}
- TChunk = class;
- TChunkClass = class of TChunk;
- {Same as TPNGPointerList but providing typecasted values}
- TPNGList = class(TPNGPointerList)
- private
- {Used with property Item}
- function GetItem(Index: Cardinal): TChunk;
- public
- {Removes an item}
- procedure RemoveChunk(Chunk: TChunk); overload;
- {Add a new chunk using the class from the parameter}
- function Add(ChunkClass: TChunkClass): TChunk;
- {Returns pointer to the first chunk of class}
- function ItemFromClass(ChunkClass: TChunkClass): TChunk;
- {Returns a chunk item from the list}
- property Item[Index: Cardinal]: TChunk read GetItem;
- end;
- {$IFNDEF UseDelphi}
- {The STREAMs bellow are only needed in case delphi provided ones is not}
- {avaliable (UseDelphi trigger not set)}
- {Object becomes handles}
- TCanvas = THandle;
- TBitmap = HBitmap;
- {Trick to work}
- TPersistent = TObject;
- {Base class for all streams}
- TStream = class
- protected
- {Returning/setting size}
- function GetSize: Longint; virtual;
- procedure SetSize(const Value: Longint); virtual; abstract;
- {Returns/set position}
- function GetPosition: Longint; virtual;
- procedure SetPosition(const Value: Longint); virtual;
- public
- {Returns/sets current position}
- property Position: Longint read GetPosition write SetPosition;
- {Property returns/sets size}
- property Size: Longint read GetSize write SetSize;
- {Allows reading/writing data}
- function Read(var Buffer; Count: Longint): Cardinal; virtual; abstract;
- function Write(const Buffer; Count: Longint): Cardinal; virtual; abstract;
- {Copies from another Stream}
- function CopyFrom(Source: TStream;
- Count: Cardinal): Cardinal; virtual;
- {Seeks a stream position}
- function Seek(Offset: Longint; Origin: Word): Longint; virtual; abstract;
- end;
- {File stream modes}
- TFileStreamMode = (fsmRead, fsmWrite, fsmCreate);
- TFileStreamModeSet = set of TFileStreamMode;
- {File stream for reading from files}
- TFileStream = class(TStream)
- private
- {Opened mode}
- Filemode: TFileStreamModeSet;
- {Handle}
- fHandle: THandle;
- protected
- {Set the size of the file}
- procedure SetSize(const Value: Longint); override;
- public
- {Seeks a file position}
- function Seek(Offset: Longint; Origin: Word): Longint; override;
- {Reads/writes data from/to the file}
- function Read(var Buffer; Count: Longint): Cardinal; override;
- function Write(const Buffer; Count: Longint): Cardinal; override;
- {Stream being created and destroy}
- constructor Create(Filename: String; Mode: TFileStreamModeSet);
- destructor Destroy; override;
- end;
- {Stream for reading from resources}
- TResourceStream = class(TStream)
- constructor Create(Instance: HInst; const ResName: String; ResType:PChar);
- private
- {Variables for reading}
- Size: Integer;
- Memory: Pointer;
- Position: Integer;
- protected
- {Set the size of the file}
- procedure SetSize(const Value: Longint); override;
- public
- {Stream processing}
- function Read(var Buffer; Count: Integer): Cardinal; override;
- function Seek(Offset: Integer; Origin: Word): Longint; override;
- function Write(const Buffer; Count: Longint): Cardinal; override;
- end;
- {$ENDIF}
- {Forward}
- TChunkIHDR = class;
- {Interlace method}
- TInterlaceMethod = (imNone, imAdam7);
- {Compression level type}
- TCompressionLevel = 0..9;
- {Filters type}
- TFilter = (pfNone, pfSub, pfUp, pfAverage, pfPaeth);
- TFilters = set of TFilter;
- {Png implementation object}
- TPngObject = class{$IFDEF UseDelphi}(TGraphic){$ENDIF}
- protected
- {Inverse gamma table values}
- InverseGamma: Array[Byte] of Byte;
- procedure InitializeGamma;
- private
- {Temporary palette}
- TempPalette: HPalette;
- {Filters to test to encode}
- fFilters: TFilters;
- {Compression level for ZLIB}
- fCompressionLevel: TCompressionLevel;
- {Maximum size for IDAT chunks}
- fMaxIdatSize: Integer;
- {Returns if image is interlaced}
- fInterlaceMethod: TInterlaceMethod;
- {Chunks object}
- fChunkList: TPngList;
- {Clear all chunks in the list}
- procedure ClearChunks;
- {Returns if header is present}
- function HeaderPresent: Boolean;
- {Returns linesize and byte offset for pixels}
- procedure GetPixelInfo(var LineSize, Offset: Cardinal);
- procedure SetMaxIdatSize(const Value: Integer);
- function GetAlphaScanline(const LineIndex: Integer): pByteArray;
- function GetScanline(const LineIndex: Integer): Pointer;
- {$IFDEF Store16bits}
- function GetExtraScanline(const LineIndex: Integer): Pointer;
- {$ENDIF}
- function GetTransparencyMode: TPNGTransparencyMode;
- function GetTransparentColor: TColor;
- procedure SetTransparentColor(const Value: TColor);
- protected
- {Returns the image palette}
- function GetPalette: HPALETTE; {$IFDEF UseDelphi}override;{$ENDIF}
- {Returns/sets image width and height}
- function GetWidth: Integer; {$IFDEF UseDelphi}override;{$ENDIF}
- function GetHeight: Integer; {$IFDEF UseDelphi}override; {$ENDIF}
- procedure SetWidth(Value: Integer); {$IFDEF UseDelphi}override; {$ENDIF}
- procedure SetHeight(Value: Integer); {$IFDEF UseDelphi}override;{$ENDIF}
- {Assigns from another TPNGObject}
- procedure AssignPNG(Source: TPNGObject);
- {Returns if the image is empty}
- function GetEmpty: Boolean; {$IFDEF UseDelphi}override; {$ENDIF}
- {Used with property Header}
- function GetHeader: TChunkIHDR;
- {Draws using partial transparency}
- procedure DrawPartialTrans(DC: HDC; Rect: TRect);
- {$IFDEF UseDelphi}
- {Returns if the image is transparent}
- function GetTransparent: Boolean; override;
- {$ENDIF}
- {Returns a pixel}
- function GetPixels(const X, Y: Integer): TColor; virtual;
- procedure SetPixels(const X, Y: Integer; const Value: TColor); virtual;
- public
- {Gamma table array}
- GammaTable: Array[Byte] of Byte;
- {Generates alpha information}
- procedure CreateAlpha;
- {Removes the image transparency}
- procedure RemoveTransparency;
- {Transparent color}
- property TransparentColor: TColor read GetTransparentColor write
- SetTransparentColor;
- {Add text chunk, TChunkTEXT, TChunkzTXT}
- procedure AddtEXt(const Keyword, Text: String);
- procedure AddzTXt(const Keyword, Text: String);
- {$IFDEF UseDelphi}
- {Saves to clipboard format (thanks to Antoine Pottern)}
- procedure SaveToClipboardFormat(var AFormat: Word; var AData: THandle;
- var APalette: HPalette); override;
- procedure LoadFromClipboardFormat(AFormat: Word; AData: THandle;
- APalette: HPalette); override;
- {$ENDIF}
- {Calling errors}
- procedure RaiseError(ExceptionClass: ExceptClass; Text: String);
- {Returns a scanline from png}
- property Scanline[const Index: Integer]: Pointer read GetScanline;
- {$IFDEF Store16bits}
- property ExtraScanline[const Index: Integer]: Pointer read GetExtraScanline;
- {$ENDIF}
- property AlphaScanline[const Index: Integer]: pByteArray read GetAlphaScanline;
- {Returns pointer to the header}
- property Header: TChunkIHDR read GetHeader;
- {Returns the transparency mode used by this png}
- property TransparencyMode: TPNGTransparencyMode read GetTransparencyMode;
- {Assigns from another object}
- procedure Assign(Source: TPersistent);{$IFDEF UseDelphi}override;{$ENDIF}
- {Assigns to another object}
- procedure AssignTo(Dest: TPersistent);{$IFDEF UseDelphi}override;{$ENDIF}
- {Assigns from a windows bitmap handle}
- procedure AssignHandle(Handle: HBitmap; Transparent: Boolean;
- TransparentColor: ColorRef);
- {Draws the image into a canvas}
- procedure Draw(ACanvas: TCanvas; const Rect: TRect);
- {$IFDEF UseDelphi}override;{$ENDIF}
- {Width and height properties}
- property Width: Integer read GetWidth;
- property Height: Integer read GetHeight;
- {Returns if the image is interlaced}
- property InterlaceMethod: TInterlaceMethod read fInterlaceMethod
- write fInterlaceMethod;
- {Filters to test to encode}
- property Filters: TFilters read fFilters write fFilters;
- {Maximum size for IDAT chunks, default and minimum is 65536}
- property MaxIdatSize: Integer read fMaxIdatSize write SetMaxIdatSize;
- {Property to return if the image is empty or not}
- property Empty: Boolean read GetEmpty;
- {Compression level}
- property CompressionLevel: TCompressionLevel read fCompressionLevel
- write fCompressionLevel;
- {Access to the chunk list}
- property Chunks: TPngList read fChunkList;
- {Object being created and destroyed}
- constructor Create; {$IFDEF UseDelphi}override;{$ENDIF}
- destructor Destroy; override;
- {$IFNDEF UseDelphi}procedure LoadFromFile(const Filename: String);{$ENDIF}
- {$IFNDEF UseDelphi}procedure SaveToFile(const Filename: String);{$ENDIF}
- procedure LoadFromStream(Stream: TStream); {$IFDEF UseDelphi}override;{$ENDIF}
- procedure SaveToStream(Stream: TStream); {$IFDEF UseDelphi}override;{$ENDIF}
- {Loading the image from resources}
- procedure LoadFromResourceName(Instance: HInst; const Name: String);
- procedure LoadFromResourceID(Instance: HInst; ResID: Integer);
- {Access to the png pixels}
- property Pixels[const X, Y: Integer]: TColor read GetPixels write SetPixels;
- {Palette property}
- {$IFNDEF UseDelphi}property Palette: HPalette read GetPalette;{$ENDIF}
- end;
- {Chunk name object}
- TChunkName = Array[0..3] of Char;
- {Global chunk object}
- TChunk = class
- private
- {Contains data}
- fData: Pointer;
- fDataSize: Cardinal;
- {Stores owner}
- fOwner: TPngObject;
- {Stores the chunk name}
- fName: TChunkName;
- {Returns pointer to the TChunkIHDR}
- function GetHeader: TChunkIHDR;
- {Used with property index}
- function GetIndex: Integer;
- {Should return chunk class/name}
- class function GetName: String; virtual;
- {Returns the chunk name}
- function GetChunkName: String;
- public
- {Returns index from list}
- property Index: Integer read GetIndex;
- {Returns pointer to the TChunkIHDR}
- property Header: TChunkIHDR read GetHeader;
- {Resize the data}
- procedure ResizeData(const NewSize: Cardinal);
- {Returns data and size}
- property Data: Pointer read fData;
- property DataSize: Cardinal read fDataSize;
- {Assigns from another TChunk}
- procedure Assign(Source: TChunk); virtual;
- {Returns owner}
- property Owner: TPngObject read fOwner;
- {Being destroyed/created}
- constructor Create(Owner: TPngObject); virtual;
- destructor Destroy; override;
- {Returns chunk class/name}
- property Name: String read GetChunkName;
- {Loads the chunk from a stream}
- function LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean; virtual;
- {Saves the chunk to a stream}
- function SaveData(Stream: TStream): Boolean;
- function SaveToStream(Stream: TStream): Boolean; virtual;
- end;
- {Chunk classes}
- TChunkIEND = class(TChunk); {End chunk}
- {IHDR data}
- pIHDRData = ^TIHDRData;
- TIHDRData = packed record
- Width, Height: Cardinal;
- BitDepth,
- ColorType,
- CompressionMethod,
- FilterMethod,
- InterlaceMethod: Byte;
- end;
- {Information header chunk}
- TChunkIHDR = class(TChunk)
- private
- {Current image}
- ImageHandle: HBitmap;
- ImageDC: HDC;
- {Output windows bitmap}
- HasPalette: Boolean;
- BitmapInfo: TMaxBitmapInfo;
- BytesPerRow: Integer;
- {Stores the image bytes}
- {$IFDEF Store16bits}ExtraImageData: Pointer;{$ENDIF}
- ImageData: pointer;
- ImageAlpha: Pointer;
- {Contains all the ihdr data}
- IHDRData: TIHDRData;
- protected
- {Resizes the image data to fill the color type, bit depth, }
- {width and height parameters}
- procedure PrepareImageData;
- {Release allocated ImageData memory}
- procedure FreeImageData;
- public
- {Access to ImageHandle}
- property ImageHandleValue: HBitmap read ImageHandle;
- {Properties}
- property Width: Cardinal read IHDRData.Width write IHDRData.Width;
- property Height: Cardinal read IHDRData.Height write IHDRData.Height;
- property BitDepth: Byte read IHDRData.BitDepth write IHDRData.BitDepth;
- property ColorType: Byte read IHDRData.ColorType write IHDRData.ColorType;
- property CompressionMethod: Byte read IHDRData.CompressionMethod
- write IHDRData.CompressionMethod;
- property FilterMethod: Byte read IHDRData.FilterMethod
- write IHDRData.FilterMethod;
- property InterlaceMethod: Byte read IHDRData.InterlaceMethod
- write IHDRData.InterlaceMethod;
- {Loads the chunk from a stream}
- function LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean; override;
- {Saves the chunk to a stream}
- function SaveToStream(Stream: TStream): Boolean; override;
- {Destructor/constructor}
- constructor Create(Owner: TPngObject); override;
- destructor Destroy; override;
- {Assigns from another TChunk}
- procedure Assign(Source: TChunk); override;
- end;
- {Gamma chunk}
- TChunkgAMA = class(TChunk)
- private
- {Returns/sets the value for the gamma chunk}
- function GetValue: Cardinal;
- procedure SetValue(const Value: Cardinal);
- public
- {Returns/sets gamma value}
- property Gamma: Cardinal read GetValue write SetValue;
- {Loading the chunk from a stream}
- function LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean; override;
- {Being created}
- constructor Create(Owner: TPngObject); override;
- {Assigns from another TChunk}
- procedure Assign(Source: TChunk); override;
- end;
- {ZLIB Decompression extra information}
- TZStreamRec2 = packed record
- {From ZLIB}
- ZLIB: TZStreamRec;
- {Additional info}
- Data: Pointer;
- fStream : TStream;
- end;
- {Palette chunk}
- TChunkPLTE = class(TChunk)
- private
- {Number of items in the palette}
- fCount: Integer;
- {Contains the palette handle}
- function GetPaletteItem(Index: Byte): TRGBQuad;
- public
- {Returns the color for each item in the palette}
- property Item[Index: Byte]: TRGBQuad read GetPaletteItem;
- {Returns the number of items in the palette}
- property Count: Integer read fCount;
- {Loads the chunk from a stream}
- function LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean; override;
- {Saves the chunk to a stream}
- function SaveToStream(Stream: TStream): Boolean; override;
- {Assigns from another TChunk}
- procedure Assign(Source: TChunk); override;
- end;
- {Transparency information}
- TChunktRNS = class(TChunk)
- private
- fBitTransparency: Boolean;
- function GetTransparentColor: ColorRef;
- {Returns the transparent color}
- procedure SetTransparentColor(const Value: ColorRef);
- public
- {Palette values for transparency}
- PaletteValues: Array[Byte] of Byte;
- {Returns if it uses bit transparency}
- property BitTransparency: Boolean read fBitTransparency;
- {Returns the transparent color}
- property TransparentColor: ColorRef read GetTransparentColor write
- SetTransparentColor;
- {Loads/saves the chunk from/to a stream}
- function LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean; override;
- function SaveToStream(Stream: TStream): Boolean; override;
- {Assigns from another TChunk}
- procedure Assign(Source: TChunk); override;
- end;
- {Actual image information}
- TChunkIDAT = class(TChunk)
- private
- {Holds another pointer to the TChunkIHDR}
- Header: TChunkIHDR;
- {Stores temporary image width and height}
- ImageWidth, ImageHeight: Integer;
- {Size in bytes of each line and offset}
- Row_Bytes, Offset : Cardinal;
- {Contains data for the lines}
- Encode_Buffer: Array[0..5] of pByteArray;
- Row_Buffer: Array[Boolean] of pByteArray;
- {Variable to invert the Row_Buffer used}
- RowUsed: Boolean;
- {Ending position for the current IDAT chunk}
- EndPos: Integer;
- {Filter the current line}
- procedure FilterRow;
- {Filter to encode and returns the best filter}
- function FilterToEncode: Byte;
- {Reads ZLIB compressed data}
- function IDATZlibRead(var ZLIBStream: TZStreamRec2; Buffer: Pointer;
- Count: Integer; var EndPos: Integer; var crcfile: Cardinal): Integer;
- {Compress and writes IDAT data}
- procedure IDATZlibWrite(var ZLIBStream: TZStreamRec2; Buffer: Pointer;
- const Length: Cardinal);
- procedure FinishIDATZlib(var ZLIBStream: TZStreamRec2);
- {Prepares the palette}
- procedure PreparePalette;
- protected
- {Decode interlaced image}
- procedure DecodeInterlacedAdam7(Stream: TStream;
- var ZLIBStream: TZStreamRec2; const Size: Integer; var crcfile: Cardinal);
- {Decode non interlaced imaged}
- procedure DecodeNonInterlaced(Stream: TStream;
- var ZLIBStream: TZStreamRec2; const Size: Integer;
- var crcfile: Cardinal);
- protected
- {Encode non interlaced images}
- procedure EncodeNonInterlaced(Stream: TStream;
- var ZLIBStream: TZStreamRec2);
- {Encode interlaced images}
- procedure EncodeInterlacedAdam7(Stream: TStream;
- var ZLIBStream: TZStreamRec2);
- protected
- {Memory copy methods to decode}
- procedure CopyNonInterlacedRGB8(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyNonInterlacedRGB16(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyNonInterlacedPalette148(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyNonInterlacedPalette2(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyNonInterlacedGray2(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyNonInterlacedGrayscale16(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyNonInterlacedRGBAlpha8(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyNonInterlacedRGBAlpha16(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyNonInterlacedGrayscaleAlpha8(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyNonInterlacedGrayscaleAlpha16(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyInterlacedRGB8(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyInterlacedRGB16(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyInterlacedPalette148(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyInterlacedPalette2(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyInterlacedGray2(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyInterlacedGrayscale16(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyInterlacedRGBAlpha8(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyInterlacedRGBAlpha16(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyInterlacedGrayscaleAlpha8(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyInterlacedGrayscaleAlpha16(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- protected
- {Memory copy methods to encode}
- procedure EncodeNonInterlacedRGB8(Src, Dest, Trans: pChar);
- procedure EncodeNonInterlacedRGB16(Src, Dest, Trans: pChar);
- procedure EncodeNonInterlacedGrayscale16(Src, Dest, Trans: pChar);
- procedure EncodeNonInterlacedPalette148(Src, Dest, Trans: pChar);
- procedure EncodeNonInterlacedRGBAlpha8(Src, Dest, Trans: pChar);
- procedure EncodeNonInterlacedRGBAlpha16(Src, Dest, Trans: pChar);
- procedure EncodeNonInterlacedGrayscaleAlpha8(Src, Dest, Trans: pChar);
- procedure EncodeNonInterlacedGrayscaleAlpha16(Src, Dest, Trans: pChar);
- procedure EncodeInterlacedRGB8(const Pass: Byte; Src, Dest, Trans: pChar);
- procedure EncodeInterlacedRGB16(const Pass: Byte; Src, Dest, Trans: pChar);
- procedure EncodeInterlacedPalette148(const Pass: Byte;
- Src, Dest, Trans: pChar);
- procedure EncodeInterlacedGrayscale16(const Pass: Byte;
- Src, Dest, Trans: pChar);
- procedure EncodeInterlacedRGBAlpha8(const Pass: Byte;
- Src, Dest, Trans: pChar);
- procedure EncodeInterlacedRGBAlpha16(const Pass: Byte;
- Src, Dest, Trans: pChar);
- procedure EncodeInterlacedGrayscaleAlpha8(const Pass: Byte;
- Src, Dest, Trans: pChar);
- procedure EncodeInterlacedGrayscaleAlpha16(const Pass: Byte;
- Src, Dest, Trans: pChar);
- public
- {Loads the chunk from a stream}
- function LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean; override;
- {Saves the chunk to a stream}
- function SaveToStream(Stream: TStream): Boolean; override;
- end;
- {Image last modification chunk}
- TChunktIME = class(TChunk)
- private
- {Holds the variables}
- fYear: Word;
- fMonth, fDay, fHour, fMinute, fSecond: Byte;
- public
- {Returns/sets variables}
- property Year: Word read fYear write fYear;
- property Month: Byte read fMonth write fMonth;
- property Day: Byte read fDay write fDay;
- property Hour: Byte read fHour write fHour;
- property Minute: Byte read fMinute write fMinute;
- property Second: Byte read fSecond write fSecond;
- {Loads the chunk from a stream}
- function LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean; override;
- {Saves the chunk to a stream}
- function SaveToStream(Stream: TStream): Boolean; override;
- end;
- {Textual data}
- TChunktEXt = class(TChunk)
- private
- fKeyword, fText: String;
- public
- {Keyword and text}
- property Keyword: String read fKeyword write fKeyword;
- property Text: String read fText write fText;
- {Loads the chunk from a stream}
- function LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean; override;
- {Saves the chunk to a stream}
- function SaveToStream(Stream: TStream): Boolean; override;
- {Assigns from another TChunk}
- procedure Assign(Source: TChunk); override;
- end;
- {zTXT chunk}
- TChunkzTXt = class(TChunktEXt)
- {Loads the chunk from a stream}
- function LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean; override;
- {Saves the chunk to a stream}
- function SaveToStream(Stream: TStream): Boolean; override;
- end;
- {Here we test if it's c++ builder or delphi version 3 or less}
- {$IFDEF VER110}{$DEFINE DelphiBuilder3Less}{$ENDIF}
- {$IFDEF VER100}{$DEFINE DelphiBuilder3Less}{$ENDIF}
- {$IFDEF VER93}{$DEFINE DelphiBuilder3Less}{$ENDIF}
- {$IFDEF VER90}{$DEFINE DelphiBuilder3Less}{$ENDIF}
- {$IFDEF VER80}{$DEFINE DelphiBuilder3Less}{$ENDIF}
- {Registers a new chunk class}
- procedure RegisterChunk(ChunkClass: TChunkClass);
- {Calculates crc}
- function update_crc(crc: {$IFNDEF DelphiBuilder3Less}Cardinal{$ELSE}Integer
- {$ENDIF}; buf: pByteArray; len: Integer): Cardinal;
- {Invert bytes using assembly}
- function ByteSwap(const a: integer): integer;
- implementation
- var
- ChunkClasses: TPngPointerList;
- {Table of CRCs of all 8-bit messages}
- crc_table: Array[0..255] of Cardinal;
- {Flag: has the table been computed? Initially false}
- crc_table_computed: Boolean;
- {Draw transparent image using transparent color}
- procedure DrawTransparentBitmap(dc: HDC; srcBits: Pointer;
- var srcHeader: TBitmapInfoHeader;
- srcBitmapInfo: pBitmapInfo; Rect: TRect; cTransparentColor: COLORREF);
- var
- cColor: COLORREF;
- bmAndBack, bmAndObject, bmAndMem: HBITMAP;
- bmBackOld, bmObjectOld, bmMemOld: HBITMAP;
- hdcMem, hdcBack, hdcObject, hdcTemp: HDC;
- ptSize, orgSize: TPOINT;
- OldBitmap, DrawBitmap: HBITMAP;
- begin
- hdcTemp := CreateCompatibleDC(dc);
- // Select the bitmap
- DrawBitmap := CreateDIBitmap(dc, srcHeader, CBM_INIT, srcBits, srcBitmapInfo^,
- DIB_RGB_COLORS);
- OldBitmap := SelectObject(hdcTemp, DrawBitmap);
- // Sizes
- OrgSize.x := abs(srcHeader.biWidth);
- OrgSize.y := abs(srcHeader.biHeight);
- ptSize.x := Rect.Right - Rect.Left; // Get width of bitmap
- ptSize.y := Rect.Bottom - Rect.Top; // Get height of bitmap
- // Create some DCs to hold temporary data.
- hdcBack := CreateCompatibleDC(dc);
- hdcObject := CreateCompatibleDC(dc);
- hdcMem := CreateCompatibleDC(dc);
- // Create a bitmap for each DC. DCs are required for a number of
- // GDI functions.
- // Monochrome DCs
- bmAndBack := CreateBitmap(ptSize.x, ptSize.y, 1, 1, nil);
- bmAndObject := CreateBitmap(ptSize.x, ptSize.y, 1, 1, nil);
- bmAndMem := CreateCompatibleBitmap(dc, ptSize.x, ptSize.y);
- // Each DC must select a bitmap object to store pixel data.
- bmBackOld := SelectObject(hdcBack, bmAndBack);
- bmObjectOld := SelectObject(hdcObject, bmAndObject);
- bmMemOld := SelectObject(hdcMem, bmAndMem);
- // Set the background color of the source DC to the color.
- // contained in the parts of the bitmap that should be transparent
- cColor := SetBkColor(hdcTemp, cTransparentColor);
- // Create the object mask for the bitmap by performing a BitBlt
- // from the source bitmap to a monochrome bitmap.
- StretchBlt(hdcObject, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0,
- orgSize.x, orgSize.y, SRCCOPY);
- // Set the background color of the source DC back to the original
- // color.
- SetBkColor(hdcTemp, cColor);
- // Create the inverse of the object mask.
- BitBlt(hdcBack, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0,
- NOTSRCCOPY);
- // Copy the background of the main DC to the destination.
- BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, dc, Rect.Left, Rect.Top,
- SRCCOPY);
- // Mask out the places where the bitmap will be placed.
- BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, SRCAND);
- // Mask out the transparent colored pixels on the bitmap.
- // BitBlt(hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcBack, 0, 0, SRCAND);
- StretchBlt(hdcTemp, 0, 0, OrgSize.x, OrgSize.y, hdcBack, 0, 0,
- PtSize.x, PtSize.y, SRCAND);
- // XOR the bitmap with the background on the destination DC.
- StretchBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0,
- OrgSize.x, OrgSize.y, SRCPAINT);
- // Copy the destination to the screen.
- BitBlt(dc, Rect.Left, Rect.Top, ptSize.x, ptSize.y, hdcMem, 0, 0,
- SRCCOPY);
- // Delete the memory bitmaps.
- DeleteObject(SelectObject(hdcBack, bmBackOld));
- DeleteObject(SelectObject(hdcObject, bmObjectOld));
- DeleteObject(SelectObject(hdcMem, bmMemOld));
- DeleteObject(SelectObject(hdcTemp, OldBitmap));
- // Delete the memory DCs.
- DeleteDC(hdcMem);
- DeleteDC(hdcBack);
- DeleteDC(hdcObject);
- DeleteDC(hdcTemp);
- end;
- {Make the table for a fast CRC.}
- procedure make_crc_table;
- var
- c: Cardinal;
- n, k: Integer;
- begin
- {fill the crc table}
- for n := 0 to 255 do
- begin
- c := Cardinal(n);
- for k := 0 to 7 do
- begin
- if Boolean(c and 1) then
- c := $edb88320 xor (c shr 1)
- else
- c := c shr 1;
- end;
- crc_table[n] := c;
- end;
- {The table has already being computated}
- crc_table_computed := true;
- end;
- {Update a running CRC with the bytes buf[0..len-1]--the CRC
- should be initialized to all 1's, and the transmitted value
- is the 1's complement of the final running CRC (see the
- crc() routine below)).}
- function update_crc(crc: {$IFNDEF DelphiBuilder3Less}Cardinal{$ELSE}Integer
- {$ENDIF}; buf: pByteArray; len: Integer): Cardinal;
- var
- c: Cardinal;
- n: Integer;
- begin
- c := crc;
- {Create the crc table in case it has not being computed yet}
- if not crc_table_computed then make_crc_table;
- {Update}
- for n := 0 to len - 1 do
- c := crc_table[(c XOR buf^[n]) and $FF] XOR (c shr 8);
- {Returns}
- Result := c;
- end;
- {$IFNDEF UseDelphi}
- function FileExists(Filename: String): Boolean;
- var
- FindFile: THandle;
- FindData: TWin32FindData;
- begin
- FindFile := FindFirstFile(PChar(Filename), FindData);
- Result := FindFile <> INVALID_HANDLE_VALUE;
- if Result then Windows.FindClose(FindFile);
- end;
- {$ENDIF}
- {$IFNDEF UseDelphi}
- {Exception implementation}
- constructor Exception.Create(Msg: String);
- begin
- end;
- {$ENDIF}
- {Calculates the paeth predictor}
- function PaethPredictor(a, b, c: Byte): Byte;
- var
- pa, pb, pc: Integer;
- begin
- { a = left, b = above, c = upper left }
- pa := abs(b - c); { distances to a, b, c }
- pb := abs(a - c);
- pc := abs(a + b - c * 2);
- { return nearest of a, b, c, breaking ties in order a, b, c }
- if (pa <= pb) and (pa <= pc) then
- Result := a
- else
- if pb <= pc then
- Result := b
- else
- Result := c;
- end;
- {Invert bytes using assembly}
- function ByteSwap(const a: integer): integer;
- asm
- bswap eax
- end;
- function ByteSwap16(inp:word): word;
- asm
- bswap eax
- shr eax, 16
- end;
- {Calculates number of bytes for the number of pixels using the}
- {color mode in the paramenter}
- function BytesForPixels(const Pixels: Integer; const ColorType,
- BitDepth: Byte): Integer;
- begin
- case ColorType of
- {Palette and grayscale contains a single value, for palette}
- {an value of size 2^bitdepth pointing to the palette index}
- {and grayscale the value from 0 to 2^bitdepth with color intesity}
- COLOR_GRAYSCALE, COLOR_PALETTE:
- Result := (Pixels * BitDepth + 7) div 8;
- {RGB contains 3 values R, G, B with size 2^bitdepth each}
- COLOR_RGB:
- Result := (Pixels * BitDepth * 3) div 8;
- {Contains one value followed by alpha value booth size 2^bitdepth}
- COLOR_GRAYSCALEALPHA:
- Result := (Pixels * BitDepth * 2) div 8;
- {Contains four values size 2^bitdepth, Red, Green, Blue and alpha}
- COLOR_RGBALPHA:
- Result := (Pixels * BitDepth * 4) div 8;
- else
- Result := 0;
- end {case ColorType}
- end;
- type
- pChunkClassInfo = ^TChunkClassInfo;
- TChunkClassInfo = record
- ClassName: TChunkClass;
- end;
- {Register a chunk type}
- procedure RegisterChunk(ChunkClass: TChunkClass);
- var
- NewClass: pChunkClassInfo;
- begin
- {In case the list object has not being created yet}
- if ChunkClasses = nil then ChunkClasses := TPngPointerList.Create(nil);
- {Add this new class}
- new(NewClass);
- NewClass^.ClassName := ChunkClass;
- ChunkClasses.Add(NewClass);
- end;
- {Free chunk class list}
- procedure FreeChunkClassList;
- var
- i: Integer;
- begin
- if (ChunkClasses <> nil) then
- begin
- FOR i := 0 TO ChunkClasses.Count - 1 do
- Dispose(pChunkClassInfo(ChunkClasses.Item[i]));
- ChunkClasses.Free;
- end;
- end;
- {Registering of common chunk classes}
- procedure RegisterCommonChunks;
- begin
- {Important chunks}
- RegisterChunk(TChunkIEND);
- RegisterChunk(TChunkIHDR);
- RegisterChunk(TChunkIDAT);
- RegisterChunk(TChunkPLTE);
- RegisterChunk(TChunkgAMA);
- RegisterChunk(TChunktRNS);
- {Not so important chunks}
- RegisterChunk(TChunktIME);
- RegisterChunk(TChunktEXt);
- RegisterChunk(TChunkzTXt);
- end;
- {Creates a new chunk of this class}
- function CreateClassChunk(Owner: TPngObject; Name: TChunkName): TChunk;
- var
- i : Integer;
- NewChunk: TChunkClass;
- begin
- {Looks for this chunk}
- NewChunk := TChunk; {In case there is no registered class for this}
- {Looks for this class in all registered chunks}
- if Assigned(ChunkClasses) then
- FOR i := 0 TO ChunkClasses.Count - 1 DO
- begin
- if pChunkClassInfo(ChunkClasses.Item[i])^.ClassName.GetName = Name then
- begin
- NewChunk := pChunkClassInfo(ChunkClasses.Item[i])^.ClassName;
- break;
- end;
- end;
- {Returns chunk class}
- Result := NewChunk.Create(Owner);
- Result.fName := Name;
- end;
- {ZLIB support}
- const
- ZLIBAllocate = High(Word);
- {Initializes ZLIB for decompression}
- function ZLIBInitInflate(Stream: TStream): TZStreamRec2;
- begin
- {Fill record}
- Fillchar(Result, SIZEOF(TZStreamRec2), #0);
- {Set internal record information}
- with Result do
- begin
- GetMem(Data, ZLIBAllocate);
- fStream := Stream;
- end;
- {Init decompression}
- InflateInit_(Result.zlib, zlib_version, SIZEOF(TZStreamRec));
- end;
- {Initializes ZLIB for compression}
- function ZLIBInitDeflate(Stream: TStream;
- Level: TCompressionlevel; Size: Cardinal): TZStreamRec2;
- begin
- {Fill record}
- Fillchar(Result, SIZEOF(TZStreamRec2), #0);
- {Set internal record information}
- with Result, ZLIB do
- begin
- GetMem(Data, Size);
- fStream := Stream;
- next_out := Data;
- avail_out := Size;
- end;
- {Inits compression}
- deflateInit_(Result.zlib, Level, zlib_version, sizeof(TZStreamRec));
- end;
- {Terminates ZLIB for compression}
- procedure ZLIBTerminateDeflate(var ZLIBStream: TZStreamRec2);
- begin
- {Terminates decompression}
- DeflateEnd(ZLIBStream.zlib);
- {Free internal record}
- FreeMem(ZLIBStream.Data, ZLIBAllocate);
- end;
- {Terminates ZLIB for decompression}
- procedure ZLIBTerminateInflate(var ZLIBStream: TZStreamRec2);
- begin
- {Terminates decompression}
- InflateEnd(ZLIBStream.zlib);
- {Free internal record}
- FreeMem(ZLIBStream.Data, ZLIBAllocate);
- end;
- {Decompresses ZLIB into a memory address}
- function DecompressZLIB(const Input: Pointer; InputSize: Integer;
- var Output: Pointer; var OutputSize: Integer;
- var ErrorOutput: String): Boolean;
- var
- StreamRec : TZStreamRec;
- Buffer : Array[Byte] of Byte;
- InflateRet: Integer;
- begin
- with StreamRec do
- begin
- {Initializes}
- Result := True;
- OutputSize := 0;
- {Prepares the data to decompress}
- FillChar(StreamRec, SizeOf(TZStreamRec), #0);
- InflateInit_(StreamRec, zlib_version, SIZEOF(TZStreamRec));
- next_in := Input;
- avail_in := InputSize;
- {Decodes data}
- repeat
- {In case it needs an output buffer}
- if (avail_out = 0) then
- begin
- next_out := @Buffer;
- avail_out := SizeOf(Buffer);
- end {if (avail_out = 0)};
- {Decompress and put in output}
- InflateRet := inflate(StreamRec, 0);
- if (InflateRet = Z_STREAM_END) or (InflateRet = 0) then
- begin
- {Reallocates output buffer}
- inc(OutputSize, total_out);
- if Output = nil then
- GetMem(Output, OutputSize) else ReallocMem(Output, OutputSize);
- {Copies the new data}
- CopyMemory(Ptr(Longint(Output) + OutputSize - total_out),
- @Buffer, total_out);
- end {if (InflateRet = Z_STREAM_END) or (InflateRet = 0)}
- {Now tests for errors}
- else if InflateRet < 0 then
- begin
- Result := False;
- ErrorOutput := StreamRec.msg;
- InflateEnd(StreamRec);
- Exit;
- end {if InflateRet < 0}
- until InflateRet = Z_STREAM_END;
- {Terminates decompression}
- InflateEnd(StreamRec);
- end {with StreamRec}
- end;
- {Compresses ZLIB into a memory address}
- function CompressZLIB(Input: Pointer; InputSize, CompressionLevel: Integer;
- var Output: Pointer; var OutputSize: Integer;
- var ErrorOutput: String): Boolean;
- var
- StreamRec : TZStreamRec;
- Buffer : Array[Byte] of Byte;
- DeflateRet: Integer;
- begin
- with StreamRec do
- begin
- Result := True; {By default returns TRUE as everything might have gone ok}
- OutputSize := 0; {Initialize}
- {Prepares the data to compress}
- FillChar(StreamRec, SizeOf(TZStreamRec), #0);
- DeflateInit_(StreamRec, CompressionLevel,zlib_version, SIZEOF(TZStreamRec));
- next_in := Input;
- avail_in := InputSize;
- while avail_in > 0 do
- begin
- {When it needs new buffer to stores the compressed data}
- if avail_out = 0 then
- begin
- {Restore buffer}
- next_out := @Buffer;
- avail_out := SizeOf(Buffer);
- end {if avail_out = 0};
- {Compresses}
- DeflateRet := deflate(StreamRec, Z_FINISH);
- if (DeflateRet = Z_STREAM_END) or (DeflateRet = 0) then
- begin
- {Updates the output memory}
- inc(OutputSize, total_out);
- if Output = nil then
- GetMem(Output, OutputSize) else ReallocMem(Output, OutputSize);
- {Copies the new data}
- CopyMemory(Ptr(Longint(Output) + OutputSize - total_out),
- @Buffer, total_out);
- end {if (InflateRet = Z_STREAM_END) or (InflateRet = 0)}
- {Now tests for errors}
- else if DeflateRet < 0 then
- begin
- Result := False;
- ErrorOutput := StreamRec.msg;
- DeflateEnd(StreamRec);
- Exit;
- end {if InflateRet < 0}
- end {while avail_in > 0};
- {Finishes compressing}
- DeflateEnd(StreamRec);
- end {with StreamRec}
- end;
- {TPngPointerList implementation}
- {Object being created}
- constructor TPngPointerList.Create(AOwner: TPNGObject);
- begin
- inherited Create; {Let ancestor work}
- {Holds owner}
- fOwner := AOwner;
- {Memory pointer not being used yet}
- fMemory := nil;
- {No items yet}
- fCount := 0;
- end;
- {Removes value from the list}
- function TPngPointerList.Remove(Value: Pointer): Pointer;
- var
- I, Position: Integer;
- begin
- {Gets item position}
- Position := -1;
- FOR I := 0 TO Count - 1 DO
- if Value = Item[I] then Position := I;
- {In case a match was found}
- if Position >= 0 then
- begin
- Result := Item[Position]; {Returns pointer}
- {Remove item and move memory}
- Dec(fCount);
- if Position < Integer(FCount) then
- System.Move(fMemory^[Position + 1], fMemory^[Position],
- (Integer(fCount) - Position) * SizeOf(Pointer));
- end {if Position >= 0} else Result := nil
- end;
- {Add a new value in the list}
- procedure TPngPointerList.Add(Value: Pointer);
- begin
- Count := Count + 1;
- Item[Count - 1] := Value;
- end;
- {Object being destroyed}
- destructor TPngPointerList.Destroy;
- begin
- {Release memory if needed}
- if fMemory <> nil then
- FreeMem(fMemory, fCount * sizeof(Pointer));
- {Free things}
- inherited Destroy;
- end;
- {Returns one item from the list}
- function TPngPointerList.GetItem(Index: Cardinal): Pointer;
- begin
- if (Index <= Count - 1) then
- Result := fMemory[Index]
- else
- {In case it's out of bounds}
- Result := nil;
- end;
- {Inserts a new item in the list}
- procedure TPngPointerList.Insert(Value: Pointer; Position: Cardinal);
- begin
- if (Position < Count) then
- begin
- {Increase item count}
- SetSize(Count + 1);
- {Move other pointers}
- if Position < Count then
- System.Move(fMemory^[Position], fMemory^[Position + 1],
- (Count - Position - 1) * SizeOf(Pointer));
- {Sets item}
- Item[Position] := Value;
- end;
- end;
- {Sets one item from the list}
- procedure TPngPointerList.SetItem(Index: Cardinal; const Value: Pointer);
- begin
- {If index is in bounds, set value}
- if (Index <= Count - 1) then
- fMemory[Index] := Value
- end;
- {This method resizes the list}
- procedure TPngPointerList.SetSize(const Size: Cardinal);
- begin
- {Sets the size}
- if (fMemory = nil) and (Size > 0) then
- GetMem(fMemory, Size * SIZEOF(Pointer))
- else
- if Size > 0 then {Only realloc if the new size is greater than 0}
- ReallocMem(fMemory, Size * SIZEOF(Pointer))
- else
- {In case user is resize to 0 items}
- begin
- FreeMem(fMemory);
- fMemory := nil;
- end;
- {Update count}
- fCount := Size;
- end;
- {TPNGList implementation}
- {Removes an item}
- procedure TPNGList.RemoveChunk(Chunk: TChunk);
- begin
- Remove(Chunk);
- Chunk.Free
- end;
- {Add a new item}
- function TPNGList.Add(ChunkClass: TChunkClass): TChunk;
- var
- IHDR: TChunkIHDR;
- IEND: TChunkIEND;
- IDAT: TChunkIDAT;
- PLTE: TChunkPLTE;
- begin
- Result := nil; {Default result}
- {Adding these is not allowed}
- if (ChunkClass = TChunkIHDR) or (ChunkClass = TChunkIDAT) or
- (ChunkClass = TChunkPLTE) or (ChunkClass = TChunkIEND) then
- fOwner.RaiseError(EPngError, EPNGCannotAddChunkText)
- {Two of these is not allowed}
- else if ((ChunkClass = TChunkgAMA) and (ItemFromClass(TChunkgAMA) <> nil)) or
- ((ChunkClass = TChunktRNS) and (ItemFromClass(TChunktRNS) <> nil)) then
- fOwner.RaiseError(EPngError, EPNGCannotAddChunkText)
- {There must have an IEND and IHDR chunk}
- else if (ItemFromClass(TChunkIEND) = nil) or
- (ItemFromClass(TChunkIHDR) = nil) then
- fOwner.RaiseError(EPngError, EPNGCannotAddInvalidImageText)
- else
- begin
- {Get common chunks}
- IHDR := ItemFromClass(TChunkIHDR) as TChunkIHDR;
- IEND := ItemFromClass(TChunkIEND) as TChunkIEND;
- {Create new chunk}
- Result := ChunkClass.Create(Owner);
- {Add to the list}
- if (ChunkClass = TChunkgAMA) then
- Insert(Result, IHDR.Index + 1)
- {Transparency chunk (fix by Ian Boyd)}
- else if (ChunkClass = TChunktRNS) then
- begin
- {Transparecy chunk must be after PLTE; before IDAT}
- IDAT := ItemFromClass(TChunkIDAT) as TChunkIDAT;
- PLTE := ItemFromClass(TChunkPLTE) as TChunkPLTE;
- if Assigned(PLTE) then
- Insert(Result, PLTE.Index + 1)
- else if Assigned(IDAT) then
- Insert(Result, IDAT.Index)
- else
- Insert(Result, IHDR.Index + 1)
- end
- else {All other chunks}
- Insert(Result, IEND.Index);
- end {if}
- end;
- {Returns item from the list}
- function TPNGList.GetItem(Index: Cardinal): TChunk;
- begin
- Result := inherited GetItem(Index);
- end;
- {Returns first item from the list using the class from parameter}
- function TPNGList.ItemFromClass(ChunkClass: TChunkClass): TChunk;
- var
- i: Integer;
- begin
- Result := nil; {Initial result}
- FOR i := 0 TO Count - 1 DO
- {Test if this item has the same class}
- if Item[i] is ChunkClass then
- begin
- {Returns this item and exit}
- Result := Item[i];
- break;
- end {if}
- end;
- {$IFNDEF UseDelphi}
- {TStream implementation}
- {Copies all from another stream}
- function TStream.CopyFrom(Source: TStream; Count: Cardinal): Cardinal;
- const
- MaxBytes = $f000;
- var
- Buffer: PChar;
- BufSize, N: Cardinal;
- begin
- {If count is zero, copy everything from Source}
- if Count = 0 then
- begin
- Source.Seek(0, soFromBeginning);
- Count := Source.Size;
- end;
- Result := Count; {Returns the number of bytes readed}
- {Allocates memory}
- if Count > MaxBytes then BufSize := MaxBytes else BufSize := Count;
- GetMem(Buffer, BufSize);
- {Copy memory}
- while Count > 0 do
- begin
- if Count > BufSize then N := BufSize else N := Count;
- Source.Read(Buffer^, N);
- Write(Buffer^, N);
- dec(Count, N);
- end;
- {Deallocates memory}
- FreeMem(Buffer, BufSize);
- end;
- {Set current stream position}
- procedure TStream.SetPosition(const Value: Longint);
- begin
- Seek(Value, soFromBeginning);
- end;
- {Returns position}
- function TStream.GetPosition: Longint;
- begin
- Result := Seek(0, soFromCurrent);
- end;
- {Returns stream size}
- function TStream.GetSize: Longint;
- var
- Pos: Cardinal;
- begin
- Pos := Seek(0, soFromCurrent);
- Result := Seek(0, soFromEnd);
- Seek(Pos, soFromCurrent);
- end;
- {TFileStream implementation}
- {Filestream object being created}
- constructor TFileStream.Create(Filename: String; Mode: TFileStreamModeSet);
- {Makes file mode}
- function OpenMode: DWORD;
- begin
- Result := 0;
- if fsmRead in Mode then Result := GENERIC_READ;
- if (fsmWrite in Mode) or (fsmCreate in Mode) then
- Result := Result OR GENERIC_WRITE;
- end;
- const
- IsCreate: Array[Boolean] of Integer = (OPEN_ALWAYS, CREATE_ALWAYS);
- begin
- {Call ancestor}
- inherited Create;
- {Create handle}
- fHandle := CreateFile(PChar(Filename), OpenMode, FILE_SHARE_READ or
- FILE_SHARE_WRITE, nil, IsCreate[fsmCreate in Mode], 0, 0);
- {Store mode}
- FileMode := Mode;
- end;
- {Filestream object being destroyed}
- destructor TFileStream.Destroy;
- begin
- {Terminates file and close}
- if FileMode = [fsmWrite] then
- SetEndOfFile(fHandle);
- CloseHandle(fHandle);
- {Call ancestor}
- inherited Destroy;
- end;
- {Writes data to the file}
- function TFileStream.Write(const Buffer; Count: Longint): Cardinal;
- begin
- if not WriteFile(fHandle, Buffer, Count, Result, nil) then
- Result := 0;
- end;
- {Reads data from the file}
- function TFileStream.Read(var Buffer; Count: Longint): Cardinal;
- begin
- if not ReadFile(fHandle, Buffer, Count, Result, nil) then
- Result := 0;
- end;
- {Seeks the file position}
- function TFileStream.Seek(Offset: Integer; Origin: Word): Longint;
- begin
- Result := SetFilePointer(fHandle, Offset, nil, Origin);
- end;
- {Sets the size of the file}
- procedure TFileStream.SetSize(const Value: Longint);
- begin
- Seek(Value, soFromBeginning);
- SetEndOfFile(fHandle);
- end;
- {TResourceStream implementation}
- {Creates the resource stream}
- constructor TResourceStream.Create(Instance: HInst; const ResName: String;
- ResType: PChar);
- var
- ResID: HRSRC;
- ResGlobal: HGlobal;
- begin
- {Obtains the resource ID}
- ResID := FindResource(hInstance, PChar(ResName), RT_RCDATA);
- if ResID = 0 then raise EPNGError.Create('');
- {Obtains memory and size}
- ResGlobal := LoadResource(hInstance, ResID);
- Size := SizeOfResource(hInstance, ResID);
- Memory := LockResource(ResGlobal);
- if (ResGlobal = 0) or (Memory = nil) then EPNGError.Create('');
- end;
- {Setting resource stream size is not supported}
- procedure TResourceStream.SetSize(const Value: Integer);
- begin
- end;
- {Writing into a resource stream is not supported}
- function TResourceStream.Write(const Buffer; Count: Integer): Cardinal;
- begin
- Result := 0;
- end;
- {Reads data from the stream}
- function TResourceStream.Read(var Buffer; Count: Integer): Cardinal;
- begin
- //Returns data
- CopyMemory(@Buffer, Ptr(Longint(Memory) + Position), Count);
- //Update position
- inc(Position, Count);
- //Returns
- Result := Count;
- end;
- {Seeks data}
- function TResourceStream.Seek(Offset: Integer; Origin: Word): Longint;
- begin
- {Move depending on the origin}
- case Origin of
- soFromBeginning: Position := Offset;
- soFromCurrent: inc(Position, Offset);
- soFromEnd: Position := Size + Offset;
- end;
- {Returns the current position}
- Result := Position;
- end;
- {$ENDIF}
- {TChunk implementation}
- {Resizes the data}
- procedure TChunk.ResizeData(const NewSize: Cardinal);
- begin
- fDataSize := NewSize;
- ReallocMem(fData, NewSize + 1);
- end;
- {Returns index from list}
- function TChunk.GetIndex: Integer;
- var
- i: Integer;
- begin
- Result := -1; {Avoiding warnings}
- {Searches in the list}
- FOR i := 0 TO Owner.Chunks.Count - 1 DO
- if Owner.Chunks.Item[i] = Self then
- begin
- {Found match}
- Result := i;
- exit;
- end {for i}
- end;
- {Returns pointer to the TChunkIHDR}
- function TChunk.GetHeader: TChunkIHDR;
- begin
- Result := Owner.Chunks.Item[0] as TChunkIHDR;
- end;
- {Assigns from another TChunk}
- procedure TChunk.Assign(Source: TChunk);
- begin
- {Copy properties}
- fName := Source.fName;
- {Set data size and realloc}
- ResizeData(Source.fDataSize);
- {Copy data (if there's any)}
- if fDataSize > 0 then CopyMemory(fData, Source.fData, fDataSize);
- end;
- {Chunk being created}
- constructor TChunk.Create(Owner: TPngObject);
- var
- ChunkName: String;
- begin
- {Ancestor create}
- inherited Create;
- {If it's a registered class, set the chunk name based on the class}
- {name. For instance, if the class name is TChunkgAMA, the GAMA part}
- {will become the chunk name}
- ChunkName := Copy(ClassName, Length('TChunk') + 1, Length(ClassName));
- if Length(ChunkName) = 4 then CopyMemory(@fName[0], @ChunkName[1], 4);
- {Initialize data holder}
- GetMem(fData, 1);
- fDataSize := 0;
- {Record owner}
- fOwner := Owner;
- end;
- {Chunk being destroyed}
- destructor TChunk.Destroy;
- begin
- {Free data holder}
- FreeMem(fData, fDataSize + 1);
- {Let ancestor destroy}
- inherited Destroy;
- end;
- {Returns the chunk name 1}
- function TChunk.GetChunkName: String;
- begin
- Result := fName
- end;
- {Returns the chunk name 2}
- class function TChunk.GetName: String;
- begin
- {For avoid writing GetName for each TChunk descendent, by default for}
- {classes which don't declare GetName, it will look for the class name}
- {to extract the chunk kind. Example, if the class name is TChunkIEND }
- {this method extracts and returns IEND}
- Result := Copy(ClassName, Length('TChunk') + 1, Length(ClassName));
- end;
- {Saves the data to the stream}
- function TChunk.SaveData(Stream: TStream): Boolean;
- var
- ChunkSize, ChunkCRC: Cardinal;
- begin
- {First, write the size for the following data in the chunk}
- ChunkSize := ByteSwap(DataSize);
- Stream.Write(ChunkSize, 4);
- {The chunk name}
- Stream.Write(fName, 4);
- {If there is data for the chunk, write it}
- if DataSize > 0 then Stream.Write(Data^, DataSize);
- {Calculates and write CRC}
- ChunkCRC := update_crc($ffffffff, @fName[0], 4);
- ChunkCRC := Byteswap(update_crc(ChunkCRC, Data, DataSize) xor $ffffffff);
- Stream.Write(ChunkCRC, 4);
- {Returns that everything went ok}
- Result := TRUE;
- end;
- {Saves the chunk to the stream}
- function TChunk.SaveToStream(Stream: TStream): Boolean;
- begin
- Result := SaveData(Stream)
- end;
- {Loads the chunk from a stream}
- function TChunk.LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean;
- var
- CheckCRC: Cardinal;
- {$IFDEF CheckCRC}RightCRC: Cardinal;{$ENDIF}
- begin
- {Copies data from source}
- ResizeData(Size);
- if Size > 0 then Stream.Read(fData^, Size);
- {Reads CRC}
- Stream.Read(CheckCRC, 4);
- CheckCrc := ByteSwap(CheckCRC);
- {Check if crc readed is valid}
- {$IFDEF CheckCRC}
- RightCRC := update_crc($ffffffff, @ChunkName[0], 4);
- RightCRC := update_crc(RightCRC, fData, Size) xor $ffffffff;
- Result := RightCRC = CheckCrc;
- {Handle CRC error}
- if not Result then
- begin
- {In case it coult not load chunk}
- Owner.RaiseError(EPngInvalidCRC, EPngInvalidCRCText);
- exit;
- end
- {$ELSE}Result := TRUE; {$ENDIF}
- end;
- {TChunktIME implementation}
- {Chunk being loaded from a stream}
- function TChunktIME.LoadFromStream(Stream: TStream;
- const ChunkName: TChunkName; Size: Integer): Boolean;
- begin
- {Let ancestor load the data}
- Result := inherited LoadFromStream(Stream, ChunkName, Size);
- if not Result or (Size <> 7) then exit; {Size must be 7}
- {Reads data}
- fYear := ((pByte(Longint(Data) )^) * 256)+ (pByte(Longint(Data) + 1)^);
- fMonth := pByte(Longint(Data) + 2)^;
- fDay := pByte(Longint(Data) + 3)^;
- fHour := pByte(Longint(Data) + 4)^;
- fMinute := pByte(Longint(Data) + 5)^;
- fSecond := pByte(Longint(Data) + 6)^;
- end;
- {Saving the chunk to a stream}
- function TChunktIME.SaveToStream(Stream: TStream): Boolean;
- begin
- {Update data}
- ResizeData(7); {Make sure the size is 7}
- pWord(Data)^ := Year;
- pByte(Longint(Data) + 2)^ := Month;
- pByte(Longint(Data) + 3)^ := Day;
- pByte(Longint(Data) + 4)^ := Hour;
- pByte(Longint(Data) + 5)^ := Minute;
- pByte(Longint(Data) + 6)^ := Second;
- {Let inherited save data}
- Result := inherited SaveToStream(Stream);
- end;
- {TChunkztXt implementation}
- {Loading the chunk from a stream}
- function TChunkzTXt.LoadFromStream(Stream: TStream;
- const ChunkName: TChunkName; Size: Integer): Boolean;
- var
- ErrorOutput: String;
- CompressionMethod: Byte;
- Output: Pointer;
- OutputSize: Integer;
- begin
- {Load data from stream and validate}
- Result := inherited LoadFromStream(Stream, ChunkName, Size);
- if not Result or (Size < 4) then exit;
- fKeyword := PChar(Data); {Get keyword and compression method bellow}
- CompressionMethod := pByte(Longint(fKeyword) + Length(fKeyword))^;
- fText := '';
- {In case the compression is 0 (only one accepted by specs), reads it}
- if CompressionMethod = 0 then
- begin
- Output := nil;
- if DecompressZLIB(PChar(Longint(Data) + Length(fKeyword) + 2),
- Size - Length(fKeyword) - 2, Output, OutputSize, ErrorOutput) then
- begin
- SetLength(fText, OutputSize);
- CopyMemory(@fText[1], Output, OutputSize);
- end {if DecompressZLIB(...};
- FreeMem(Output);
- end {if CompressionMethod = 0}
- end;
- {Saving the chunk to a stream}
- function TChunkztXt.SaveToStream(Stream: TStream): Boolean;
- var
- Output: Pointer;
- OutputSize: Integer;
- ErrorOutput: String;
- begin
- Output := nil; {Initializes output}
- if fText = '' then fText := ' ';
- {Compresses the data}
- if CompressZLIB(@fText[1], Length(fText), Owner.CompressionLevel, Output,
- OutputSize, ErrorOutput) then
- begin
- {Size is length from keyword, plus a null character to divide}
- {plus the compression method, plus the length of the text (zlib compressed)}
- ResizeData(Length(fKeyword) + 2 + OutputSize);
- Fillchar(Data^, DataSize, #0);
- {Copies the keyword data}
- if Keyword <> '' then
- CopyMemory(Data, @fKeyword[1], Length(Keyword));
- {Compression method 0 (inflate/deflate)}
- pByte(Ptr(Longint(Data) + Length(Keyword) + 1))^ := 0;
- if OutputSize > 0 then
- CopyMemory(Ptr(Longint(Data) + Length(Keyword) + 2), Output, OutputSize);
- {Let ancestor calculate crc and save}
- Result := SaveData(Stream);
- end {if CompressZLIB(...} else Result := False;
- {Frees output}
- if Output <> nil then FreeMem(Output)
- end;
- {TChunktEXt implementation}
- {Assigns from another text chunk}
- procedure TChunktEXt.Assign(Source: TChunk);
- begin
- fKeyword := TChunktEXt(Source).fKeyword;
- fText := TChunktEXt(Source).fText;
- end;
- {Loading the chunk from a stream}
- function TChunktEXt.LoadFromStream(Stream: TStream;
- const ChunkName: TChunkName; Size: Integer): Boolean;
- begin
- {Load data from stream and validate}
- Result := inherited LoadFromStream(Stream, ChunkName, Size);
- if not Result or (Size < 3) then exit;
- {Get text}
- fKeyword := PChar(Data);
- SetLength(fText, Size - Length(fKeyword) - 1);
- CopyMemory(@fText[1], Ptr(Longint(Data) + Length(fKeyword) + 1),
- Length(fText));
- end;
- {Saving the chunk to a stream}
- function TChunktEXt.SaveToStream(Stream: TStream): Boolean;
- begin
- {Size is length from keyword, plus a null character to divide}
- {plus the length of the text}
- ResizeData(Length(fKeyword) + 1 + Length(fText));
- Fillchar(Data^, DataSize, #0);
- {Copy data}
- if Keyword <> '' then
- CopyMemory(Data, @fKeyword[1], Length(Keyword));
- if Text <> '' then
- CopyMemory(Ptr(Longint(Data) + Length(Keyword) + 1), @fText[1],
- Length(Text));
- {Let ancestor calculate crc and save}
- Result := inherited SaveToStream(Stream);
- end;
- {TChunkIHDR implementation}
- {Chunk being created}
- constructor TChunkIHDR.Create(Owner: TPngObject);
- begin
- {Call inherited}
- inherited Create(Owner);
- {Prepare pointers}
- ImageHandle := 0;
- ImageDC := 0;
- end;
- {Chunk being destroyed}
- destructor TChunkIHDR.Destroy;
- begin
- {Free memory}
- FreeImageData();
- {Calls TChunk destroy}
- inherited Destroy;
- end;
- {Assigns from another IHDR chunk}
- procedure TChunkIHDR.Assign(Source: TChunk);
- begin
- {Copy the IHDR data}
- if Source is TChunkIHDR then
- begin
- {Copy IHDR values}
- IHDRData := TChunkIHDR(Source).IHDRData;
- {Prepare to hold data by filling BitmapInfo structure and}
- {resizing ImageData and ImageAlpha memory allocations}
- PrepareImageData();
- {Copy image data}
- CopyMemory(ImageData, TChunkIHDR(Source).ImageData,
- BytesPerRow * Integer(Height));
- CopyMemory(ImageAlpha, TChunkIHDR(Source).ImageAlpha,
- Integer(Width) * Integer(Height));
- {Copy palette colors}
- BitmapInfo.bmiColors := TChunkIHDR(Source).BitmapInfo.bmiColors;
- end
- else
- Owner.RaiseError(EPNGError, EPNGCannotAssignChunkText);
- end;
- {Release allocated image data}
- procedure TChunkIHDR.FreeImageData;
- begin
- {Free old image data}
- if ImageHandle <> 0 then DeleteObject(ImageHandle);
- if ImageDC <> 0 then DeleteDC(ImageDC);
- if ImageAlpha <> nil then FreeMem(ImageAlpha);
- {$IFDEF Store16bits}
- if ExtraImageData <> nil then FreeMem(ExtraImageData);
- {$ENDIF}
- ImageHandle := 0; ImageDC := 0; ImageAlpha := nil; ImageData := nil;
- end;
- {Chunk being loaded from a stream}
- function TChunkIHDR.LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean;
- begin
- {Let TChunk load it}
- Result := inherited LoadFromStream(Stream, ChunkName, Size);
- if not Result then Exit;
- {Now check values}
- {Note: It's recommended by png specification to make sure that the size}
- {must be 13 bytes to be valid, but some images with 14 bytes were found}
- {which could be loaded by internet explorer and other tools}
- if (fDataSize < SIZEOF(TIHdrData)) then
- begin
- {Ihdr must always have at least 13 bytes}
- Result := False;
- Owner.RaiseError(EPNGInvalidIHDR, EPNGInvalidIHDRText);
- exit;
- end;
- {Everything ok, reads IHDR}
- IHDRData := pIHDRData(fData)^;
- IHDRData.Width := ByteSwap(IHDRData.Width);
- IHDRData.Height := ByteSwap(IHDRData.Height);
- {The width and height must not be larger than 65535 pixels}
- if (IHDRData.Width > High(Word)) or (IHDRData.Height > High(Word)) then
- begin
- Result := False;
- Owner.RaiseError(EPNGSizeExceeds, EPNGSizeExceedsText);
- exit;
- end {if IHDRData.Width > High(Word)};
- {Compression method must be 0 (inflate/deflate)}
- if (IHDRData.CompressionMethod <> 0) then
- begin
- Result := False;
- Owner.RaiseError(EPNGUnknownCompression, EPNGUnknownCompressionText);
- exit;
- end;
- {Interlace must be either 0 (none) or 7 (adam7)}
- if (IHDRData.InterlaceMethod <> 0) and (IHDRData.InterlaceMethod <> 1) then
- begin
- Result := False;
- Owner.RaiseError(EPNGUnknownInterlace, EPNGUnknownInterlaceText);
- exit;
- end;
- {Updates owner properties}
- Owner.InterlaceMethod := TInterlaceMethod(IHDRData.InterlaceMethod);
- {Prepares data to hold image}
- PrepareImageData();
- end;
- {Saving the IHDR chunk to a stream}
- function TChunkIHDR.SaveToStream(Stream: TStream): Boolean;
- begin
- {Ignore 2 bits images}
- if BitDepth = 2 then BitDepth := 4;
- {It needs to do is update the data with the IHDR data}
- {structure containing the write values}
- ResizeData(SizeOf(TIHDRData));
- pIHDRData(fData)^ := IHDRData;
- {..byteswap 4 byte types}
- pIHDRData(fData)^.Width := ByteSwap(pIHDRData(fData)^.Width);
- pIHDRData(fData)^.Height := ByteSwap(pIHDRData(fData)^.Height);
- {..update interlace method}
- pIHDRData(fData)^.InterlaceMethod := Byte(Owner.InterlaceMethod);
- {..and then let the ancestor SaveToStream do the hard work}
- Result := inherited SaveToStream(Stream);
- end;
- {Resizes the image data to fill the color type, bit depth, }
- {width and height parameters}
- procedure TChunkIHDR.PrepareImageData();
- {Set the bitmap info}
- procedure SetInfo(const Bitdepth: Integer; const Palette: Boolean);
- begin
- {Copy if the bitmap contain palette entries}
- HasPalette := Palette;
- {Initialize the structure with zeros}
- fillchar(BitmapInfo, sizeof(BitmapInfo), #0);
- {Fill the strucutre}
- with BitmapInfo.bmiHeader do
- begin
- biSize := sizeof(TBitmapInfoHeader);
- biHeight := Height;
- biWidth := Width;
- biPlanes := 1;
- biBitCount := BitDepth;
- biCompression := BI_RGB;
- end {with BitmapInfo.bmiHeader}
- end;
- begin
- {Prepare bitmap info header}
- Fillchar(BitmapInfo, sizeof(TMaxBitmapInfo), #0);
- {Release old image data}
- FreeImageData();
- {Obtain number of bits for each pixel}
- case ColorType of
- COLOR_GRAYSCALE, COLOR_PALETTE, COLOR_GRAYSCALEALPHA:
- case BitDepth of
- {These are supported by windows}
- 1, 4, 8: SetInfo(BitDepth, TRUE);
- {2 bits for each pixel is not supported by windows bitmap}
- 2 : SetInfo(4, TRUE);
- {Also 16 bits (2 bytes) for each pixel is not supported}
- {and should be transormed into a 8 bit grayscale}
- 16 : SetInfo(8, TRUE);
- end;
- {Only 1 byte (8 bits) is supported}
- COLOR_RGB, COLOR_RGBALPHA: SetInfo(24, FALSE);
- end {case ColorType};
- {Number of bytes for each scanline}
- BytesPerRow := (((BitmapInfo.bmiHeader.biBitCount * Width) + 31)
- and not 31) div 8;
- {Build array for alpha information, if necessary}
- if (ColorType = COLOR_RGBALPHA) or (ColorType = COLOR_GRAYSCALEALPHA) then
- begin
- GetMem(ImageAlpha, Integer(Width) * Integer(Height));
- FillChar(ImageAlpha^, Integer(Width) * Integer(Height), #0);
- end;
- {Build array for extra byte information}
- {$IFDEF Store16bits}
- if (BitDepth = 16) then
- begin
- GetMem(ExtraImageData, BytesPerRow * Integer(Height));
- FillChar(ExtraImageData^, BytesPerRow * Integer(Height), #0);
- end;
- {$ENDIF}
- {Creates the image to hold the data, CreateDIBSection does a better}
- {work in allocating necessary memory}
- ImageDC := CreateCompatibleDC(0);
- ImageHandle := CreateDIBSection(ImageDC, pBitmapInfo(@BitmapInfo)^,
- DIB_RGB_COLORS, ImageData, 0, 0);
- {Clears the old palette (if any)}
- with Owner do
- if TempPalette <> 0 then
- begin
- DeleteObject(TempPalette);
- TempPalette := 0;
- end {with Owner, if TempPalette <> 0};
- {Build array and allocate bytes for each row}
- zeromemory(ImageData, BytesPerRow * Integer(Height));
- end;
- {TChunktRNS implementation}
- {$IFNDEF UseDelphi}
- function CompareMem(P1, P2: pByte; const Size: Integer): Boolean;
- var i: Integer;
- begin
- Result := True;
- for i := 1 to Size do
- begin
- if P1^ <> P2^ then Result := False;
- inc(P1); inc(P2);
- end {for i}
- end;
- {$ENDIF}
- {Sets the transpararent color}
- procedure TChunktRNS.SetTransparentColor(const Value: ColorRef);
- var
- i: Byte;
- LookColor: TRGBQuad;
- begin
- {Clears the palette values}
- Fillchar(PaletteValues, SizeOf(PaletteValues), #0);
- {Sets that it uses bit transparency}
- fBitTransparency := True;
- {Depends on the color type}
- with Header do
- case ColorType of
- COLOR_GRAYSCALE:
- begin
- Self.ResizeData(2);
- pWord(@PaletteValues[0])^ := ByteSwap16(GetRValue(Value));
- end;
- COLOR_RGB:
- begin
- Self.ResizeData(6);
- pWord(@PaletteValues[0])^ := ByteSwap16(GetRValue(Value));
- pWord(@PaletteValues[2])^ := ByteSwap16(GetGValue(Value));
- pWord(@PaletteValues[4])^ := ByteSwap16(GetBValue(Value));
- end;
- COLOR_PALETTE:
- begin
- {Creates a RGBQuad to search for the color}
- LookColor.rgbRed := GetRValue(Value);
- LookColor.rgbGreen := GetGValue(Value);
- LookColor.rgbBlue := GetBValue(Value);
- {Look in the table for the entry}
- for i := 0 to 255 do
- if CompareMem(@BitmapInfo.bmiColors[i], @LookColor, 3) then
- Break;
- {Fill the transparency table}
- Fillchar(PaletteValues, i, 255);
- Self.ResizeData(i + 1)
- end
- end {case / with};
- end;
- {Returns the transparent color for the image}
- function TChunktRNS.GetTransparentColor: ColorRef;
- var
- PaletteChunk: TChunkPLTE;
- i: Integer;
- begin
- Result := 0; {Default: Unknown transparent color}
- {Depends on the color type}
- with Header do
- case ColorType of
- COLOR_GRAYSCALE:
- Result := RGB(PaletteValues[0], PaletteValues[0],
- PaletteValues[0]);
- COLOR_RGB:
- Result := RGB(PaletteValues[1], PaletteValues[3], PaletteValues[5]);
- COLOR_PALETTE:
- begin
- {Obtains the palette chunk}
- PaletteChunk := Owner.Chunks.ItemFromClass(TChunkPLTE) as TChunkPLTE;
- {Looks for an entry with 0 transparency meaning that it is the}
- {full transparent entry}
- for i := 0 to Self.DataSize - 1 do
- if PaletteValues[i] = 0 then
- with PaletteChunk.GetPaletteItem(i) do
- begin
- Result := RGB(rgbRed, rgbGreen, rgbBlue);
- break
- end
- end {COLOR_PALETTE}
- end {case Header.ColorType};
- end;
- {Saving the chunk to a stream}
- function TChunktRNS.SaveToStream(Stream: TStream): Boolean;
- begin
- {Copy palette into data buffer}
- if DataSize <= 256 then
- CopyMemory(fData, @PaletteValues[0], DataSize);
- Result := inherited SaveToStream(Stream);
- end;
- {Assigns from another chunk}
- procedure TChunktRNS.Assign(Source: TChunk);
- begin
- CopyMemory(@PaletteValues[0], @TChunkTrns(Source).PaletteValues[0], 256);
- fBitTransparency := TChunkTrns(Source).fBitTransparency;
- inherited Assign(Source);
- end;
- {Loads the chunk from a stream}
- function TChunktRNS.LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean;
- var
- i, Differ255: Integer;
- begin
- {Let inherited load}
- Result := inherited LoadFromStream(Stream, ChunkName, Size);
- if not Result then Exit;
- {Make sure size is correct}
- if Size > 256 then Owner.RaiseError(EPNGInvalidPalette,
- EPNGInvalidPaletteText);
- {The unset items should have value 255}
- Fillchar(PaletteValues[0], 256, 255);
- {Copy the other values}
- CopyMemory(@PaletteValues[0], fData, Size);
- {Create the mask if needed}
- case Header.ColorType of
- {Mask for grayscale and RGB}
- COLOR_RGB, COLOR_GRAYSCALE: fBitTransparency := True;
- COLOR_PALETTE:
- begin
- Differ255 := 0; {Count the entries with a value different from 255}
- {Tests if it uses bit transparency}
- for i := 0 to Size - 1 do
- if PaletteValues[i] <> 255 then inc(Differ255);
- {If it has one value different from 255 it is a bit transparency}
- fBitTransparency := (Differ255 = 1);
- end {COLOR_PALETTE}
- end {case Header.ColorType};
- end;
- {Prepares the image palette}
- procedure TChunkIDAT.PreparePalette;
- var
- Entries: Word;
- j : Integer;
- begin
- {In case the image uses grayscale, build a grayscale palette}
- with Header do
- if (ColorType = COLOR_GRAYSCALE) or (ColorType = COLOR_GRAYSCALEALPHA) then
- begin
- {Calculate total number of palette entries}
- Entries := (1 shl Byte(BitmapInfo.bmiHeader.biBitCount));
- FOR j := 0 TO Entries - 1 DO
- with BitmapInfo.bmiColors[j] do
- begin
- {Calculate each palette entry}
- rgbRed := fOwner.GammaTable[MulDiv(j, 255, Entries - 1)];
- rgbGreen := rgbRed;
- rgbBlue := rgbRed;
- end {with BitmapInfo.bmiColors[j]}
- end {if ColorType = COLOR_GRAYSCALE..., with Header}
- end;
- {Reads from ZLIB}
- function TChunkIDAT.IDATZlibRead(var ZLIBStream: TZStreamRec2;
- Buffer: Pointer; Count: Integer; var EndPos: Integer;
- var crcfile: Cardinal): Integer;
- var
- ProcResult : Integer;
- IDATHeader : Array[0..3] of char;
- IDATCRC : Cardinal;
- begin
- {Uses internal record pointed by ZLIBStream to gather information}
- with ZLIBStream, ZLIBStream.zlib do
- begin
- {Set the buffer the zlib will read into}
- next_out := Buffer;
- avail_out := Count;
- {Decode until it reach the Count variable}
- while avail_out > 0 do
- begin
- {In case it needs more data and it's in the end of a IDAT chunk,}
- {it means that there are more IDAT chunks}
- if (fStream.Position = EndPos) and (avail_out > 0) and
- (avail_in = 0) then
- begin
- {End this chunk by reading and testing the crc value}
- fStream.Read(IDATCRC, 4);
- {$IFDEF CheckCRC}
- if crcfile xor $ffffffff <> Cardinal(ByteSwap(IDATCRC)) then
- begin
- Result := -1;
- Owner.RaiseError(EPNGInvalidCRC, EPNGInvalidCRCText);
- exit;
- end;
- {$ENDIF}
- {Start reading the next chunk}
- fStream.Read(EndPos, 4); {Reads next chunk size}
- fStream.Read(IDATHeader[0], 4); {Next chunk header}
- {It must be a IDAT chunk since image data is required and PNG}
- {specification says that multiple IDAT chunks must be consecutive}
- if IDATHeader <> 'IDAT' then
- begin
- Owner.RaiseError(EPNGMissingMultipleIDAT, EPNGMissingMultipleIDATText);
- result := -1;
- exit;
- end;
- {Calculate chunk name part of the crc}
- {$IFDEF CheckCRC}
- crcfile := update_crc($ffffffff, @IDATHeader[0], 4);
- {$ENDIF}
- EndPos := fStream.Position + ByteSwap(EndPos);
- end;
- {In case it needs compressed data to read from}
- if avail_in = 0 then
- begin
- {In case it's trying to read more than it is avaliable}
- if fStream.Position + ZLIBAllocate > EndPos then
- avail_in := fStream.Read(Data^, EndPos - fStream.Position)
- else
- avail_in := fStream.Read(Data^, ZLIBAllocate);
- {Update crc}
- {$IFDEF CheckCRC}
- crcfile := update_crc(crcfile, Data, avail_in);
- {$ENDIF}
- {In case there is no more compressed data to read from}
- if avail_in = 0 then
- begin
- Result := Count - avail_out;
- Exit;
- end;
- {Set next buffer to read and record current position}
- next_in := Data;
- end {if avail_in = 0};
- ProcResult := inflate(zlib, 0);
- {In case the result was not sucessfull}
- if (ProcResult < 0) then
- begin
- Result := -1;
- Owner.RaiseError(EPNGZLIBError,
- EPNGZLIBErrorText + zliberrors[procresult]);
- exit;
- end;
- end {while avail_out > 0};
- end {with};
- {If everything gone ok, it returns the count bytes}
- Result := Count;
- end;
- {TChunkIDAT implementation}
- const
- {Adam 7 interlacing values}
- RowStart: array[0..6] of Integer = (0, 0, 4, 0, 2, 0, 1);
- ColumnStart: array[0..6] of Integer = (0, 4, 0, 2, 0, 1, 0);
- RowIncrement: array[0..6] of Integer = (8, 8, 8, 4, 4, 2, 2);
- ColumnIncrement: array[0..6] of Integer = (8, 8, 4, 4, 2, 2, 1);
- {Copy interlaced images with 1 byte for R, G, B}
- procedure TChunkIDAT.CopyInterlacedRGB8(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- Col: Integer;
- begin
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- Dest := pChar(Longint(Dest) + Col * 3);
- repeat
- {Copy this row}
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest);
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Dest);
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) )^]; inc(Dest);
- {Move to next column}
- inc(Src, 3);
- inc(Dest, ColumnIncrement[Pass] * 3 - 3);
- inc(Col, ColumnIncrement[Pass]);
- until Col >= ImageWidth;
- end;
- {Copy interlaced images with 2 bytes for R, G, B}
- procedure TChunkIDAT.CopyInterlacedRGB16(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- Col: Integer;
- begin
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- Dest := pChar(Longint(Dest) + Col * 3);
- repeat
- {Copy this row}
- Byte(Dest^) := Owner.GammaTable[pByte(Longint(Src) + 4)^]; inc(Dest);
- Byte(Dest^) := Owner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest);
- Byte(Dest^) := Owner.GammaTable[pByte(Longint(Src) )^]; inc(Dest);
- {$IFDEF Store16bits}
- {Copy extra pixel values}
- Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 5)^]; inc(Extra);
- Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 3)^]; inc(Extra);
- Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Extra);
- {$ENDIF}
- {Move to next column}
- inc(Src, 6);
- inc(Dest, ColumnIncrement[Pass] * 3 - 3);
- inc(Col, ColumnIncrement[Pass]);
- until Col >= ImageWidth;
- end;
- {Copy 韒ages with palette using bit depths 1, 4 or 8}
- procedure TChunkIDAT.CopyInterlacedPalette148(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- const
- BitTable: Array[1..8] of Integer = ($1, $3, 0, $F, 0, 0, 0, $FF);
- StartBit: Array[1..8] of Integer = (7 , 0 , 0, 4, 0, 0, 0, 0);
- var
- CurBit, Col: Integer;
- Dest2: PChar;
- begin
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- repeat
- {Copy data}
- CurBit := StartBit[Header.BitDepth];
- repeat
- {Adjust pointer to pixel byte bounds}
- Dest2 := pChar(Longint(Dest) + (Header.BitDepth * Col) div 8);
- {Copy data}
- Byte(Dest2^) := Byte(Dest2^) or
- ( ((Byte(Src^) shr CurBit) and BitTable[Header.BitDepth])
- shl (StartBit[Header.BitDepth] - (Col * Header.BitDepth mod 8)));
- {Move to next column}
- inc(Col, ColumnIncrement[Pass]);
- {Will read next bits}
- dec(CurBit, Header.BitDepth);
- until CurBit < 0;
- {Move to next byte in source}
- inc(Src);
- until Col >= ImageWidth;
- end;
- {Copy 韒ages with palette using bit depth 2}
- procedure TChunkIDAT.CopyInterlacedPalette2(const Pass: Byte; Src, Dest,
- Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- CurBit, Col: Integer;
- Dest2: PChar;
- begin
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- repeat
- {Copy data}
- CurBit := 6;
- repeat
- {Adjust pointer to pixel byte bounds}
- Dest2 := pChar(Longint(Dest) + Col div 2);
- {Copy data}
- Byte(Dest2^) := Byte(Dest2^) or (((Byte(Src^) shr CurBit) and $3)
- shl (4 - (4 * Col) mod 8));
- {Move to next column}
- inc(Col, ColumnIncrement[Pass]);
- {Will read next bits}
- dec(CurBit, 2);
- until CurBit < 0;
- {Move to next byte in source}
- inc(Src);
- until Col >= ImageWidth;
- end;
- {Copy 韒ages with grayscale using bit depth 2}
- procedure TChunkIDAT.CopyInterlacedGray2(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- CurBit, Col: Integer;
- Dest2: PChar;
- begin
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- repeat
- {Copy data}
- CurBit := 6;
- repeat
- {Adjust pointer to pixel byte bounds}