| [453] | 1 | {Portable Network Graphics Delphi 1.5       (29 June 2005)    } | 
|---|
|  | 2 |  | 
|---|
|  | 3 | {This is the latest implementation for TPngImage component    } | 
|---|
|  | 4 | {It's meant to be a full replacement for the previous one.    } | 
|---|
|  | 5 | {There are lots of new improvements, including cleaner code,  } | 
|---|
|  | 6 | {full partial transparency support, speed improvements,       } | 
|---|
|  | 7 | {saving using ADAM 7 interlacing, better error handling, also } | 
|---|
|  | 8 | {the best compression for the final image ever. And now it's  } | 
|---|
|  | 9 | {truly able to read about any png image.                      } | 
|---|
|  | 10 |  | 
|---|
|  | 11 | { | 
|---|
|  | 12 | Version 1.5 | 
|---|
|  | 13 | 2005-29-06 - Fixed a lot of bugs using tips from mails that I´ve | 
|---|
|  | 14 | being receiving for some time | 
|---|
|  | 15 | BUG 1 - Loosing palette when assigning to TBitmap. fixed | 
|---|
|  | 16 | BUG 2 - SetPixels and GetPixels worked only with | 
|---|
|  | 17 | parameters in range 0..255. fixed | 
|---|
|  | 18 | BUG 3 - Force type address off using directive | 
|---|
|  | 19 | BUG 4 - TChunkzTXt contained an error | 
|---|
|  | 20 | BUG 5 - MaxIdatSize was not working correctly (fixed thanks | 
|---|
|  | 21 | to Gabriel Corneanu | 
|---|
|  | 22 | BUG 6 - Corrected german translation (thanks to Mael Horz) | 
|---|
|  | 23 | And the following improvements: | 
|---|
|  | 24 | IMPROVE 1 - Create ImageHandleValue properties as public in | 
|---|
|  | 25 | TChunkIHDR to get access to this handle | 
|---|
|  | 26 | IMPROVE 2 - Using SetStretchBltMode to improve stretch quality | 
|---|
|  | 27 | IMPROVE 3 - Scale is now working for alpha transparent images | 
|---|
|  | 28 | IMPROVE 4 - GammaTable propery is now public to support an | 
|---|
|  | 29 | article in the help file | 
|---|
|  | 30 |  | 
|---|
|  | 31 | Version 1.4361 | 
|---|
|  | 32 | 2003-03-04 - Fixed important bug for simple transparency when using | 
|---|
|  | 33 | RGB, Grayscale color modes | 
|---|
|  | 34 |  | 
|---|
|  | 35 | Version 1.436 | 
|---|
|  | 36 | 2003-03-04 - * NEW * Property Pixels for direct access to pixels | 
|---|
|  | 37 | * IMPROVED * Palette property (TPngObject) (read only) | 
|---|
|  | 38 | Slovenian traslation for the component (Miha Petelin) | 
|---|
|  | 39 | Help file update (scanline article/png->jpg example) | 
|---|
|  | 40 |  | 
|---|
|  | 41 | Version 1.435 | 
|---|
|  | 42 | 2003-11-03 - * NEW * New chunk implementation zTXt (method AddzTXt) | 
|---|
|  | 43 | * NEW * New compiler flags to store the extra 8 bits | 
|---|
|  | 44 | from 16 bits samples (when saving it is ignored), the | 
|---|
|  | 45 | extra data may be acessed using ExtraScanline property | 
|---|
|  | 46 | * Fixed * a bug on tIMe chunk | 
|---|
|  | 47 | French translation included (Thanks to IBE Software) | 
|---|
|  | 48 | Bugs fixed | 
|---|
|  | 49 |  | 
|---|
|  | 50 | Version 1.432 | 
|---|
|  | 51 | 2002-08-24 - * NEW *  A new method, CreateAlpha will transform the | 
|---|
|  | 52 | current image into partial transparency. | 
|---|
|  | 53 | Help file updated with a new article on how to handle | 
|---|
|  | 54 | partial transparency. | 
|---|
|  | 55 |  | 
|---|
|  | 56 | Version 1.431 | 
|---|
|  | 57 | 2002-08-14 - Fixed and tested to work on: | 
|---|
|  | 58 | C++ Builder 3 | 
|---|
|  | 59 | C++ Builder 5 | 
|---|
|  | 60 | Delphi 3 | 
|---|
|  | 61 | There was an error when setting TransparentColor, fixed | 
|---|
|  | 62 | New method, RemoveTransparency to remove image | 
|---|
|  | 63 | BIT TRANSPARENCY | 
|---|
|  | 64 |  | 
|---|
|  | 65 | Version 1.43 | 
|---|
|  | 66 | 2002-08-01 - * NEW * Support for Delphi 3 and C++ Builder 3 | 
|---|
|  | 67 | Implements mostly some things that were missing, | 
|---|
|  | 68 | a few tweaks and fixes. | 
|---|
|  | 69 |  | 
|---|
|  | 70 | Version 1.428 | 
|---|
|  | 71 | 2002-07-24 - More minor fixes (thanks to Ian Boyd) | 
|---|
|  | 72 | Bit transparency fixes | 
|---|
|  | 73 | * NEW * Finally support to bit transparency | 
|---|
|  | 74 | (palette / rgb / grayscale -> all) | 
|---|
|  | 75 |  | 
|---|
|  | 76 | Version 1.427 | 
|---|
|  | 77 | 2002-07-19 - Lots of bugs and leaks fixed | 
|---|
|  | 78 | * NEW * method to easy adding text comments, AddtEXt | 
|---|
|  | 79 | * NEW * property for setting bit transparency, | 
|---|
|  | 80 | TransparentColor | 
|---|
|  | 81 |  | 
|---|
|  | 82 | Version 1.426 | 
|---|
|  | 83 | 2002-07-18 - Clipboard finally fixed (hope) | 
|---|
|  | 84 | Changed UseDelphi trigger to UseDelphi | 
|---|
|  | 85 | * NEW * Support for bit transparency bitmaps | 
|---|
|  | 86 | when assigning from/to TBitmap objects | 
|---|
|  | 87 | Altough it does not support drawing transparent | 
|---|
|  | 88 | parts of bit transparency pngs (only partial) | 
|---|
|  | 89 | it is closer than ever | 
|---|
|  | 90 |  | 
|---|
|  | 91 | Version 1.425 | 
|---|
|  | 92 | 2002-07-01 - Clipboard methods implemented | 
|---|
|  | 93 | Lots of bugs fixed | 
|---|
|  | 94 |  | 
|---|
|  | 95 | Version 1.424 | 
|---|
|  | 96 | 2002-05-16 - Scanline and AlphaScanline are now working correctly. | 
|---|
|  | 97 | New methods for handling the clipboard | 
|---|
|  | 98 |  | 
|---|
|  | 99 | Version 1.423 | 
|---|
|  | 100 | 2002-05-16 - * NEW * Partial transparency for 1, 2, 4 and 8 bits is | 
|---|
|  | 101 | also supported using the tRNS chunk (for palette and | 
|---|
|  | 102 | grayscaling). | 
|---|
|  | 103 | New bug fixes (Peter Haas). | 
|---|
|  | 104 |  | 
|---|
|  | 105 | Version 1.422 | 
|---|
|  | 106 | 2002-05-14 - Fixed some critical leaks, thanks to Peter Haas tips. | 
|---|
|  | 107 | New translation for German (Peter Haas). | 
|---|
|  | 108 |  | 
|---|
|  | 109 | Version 1.421 | 
|---|
|  | 110 | 2002-05-06 - Now uses new ZLIB version, 1.1.4 with some security | 
|---|
|  | 111 | fixes. | 
|---|
|  | 112 | LoadFromResourceID and LoadFromResourceName added and | 
|---|
|  | 113 | help file updated for that. | 
|---|
|  | 114 | The resources strings are now located in pnglang.pas. | 
|---|
|  | 115 | New translation for Brazilian Portuguese. | 
|---|
|  | 116 | Bugs fixed. | 
|---|
|  | 117 |  | 
|---|
|  | 118 | IMPORTANT: I'm currently looking for bugs on the library. If | 
|---|
|  | 119 | anyone has found one, please send me an email and | 
|---|
|  | 120 | I will fix right away. Thanks for all the help and | 
|---|
|  | 121 | ideias I'm receiving so far.} | 
|---|
|  | 122 |  | 
|---|
|  | 123 | {My new email is: gubadaud@terra.com.br} | 
|---|
|  | 124 | {Website link   : pngdelphi.sourceforge.net} | 
|---|
|  | 125 | {Gustavo Huffenbacher Daud} | 
|---|
|  | 126 |  | 
|---|
|  | 127 | unit pngimage; | 
|---|
|  | 128 |  | 
|---|
|  | 129 | interface | 
|---|
|  | 130 |  | 
|---|
|  | 131 | {Triggers avaliable (edit the fields bellow)} | 
|---|
|  | 132 | {$TYPEDADDRESS OFF} | 
|---|
|  | 133 | {$DEFINE UseDelphi}              //Disable fat vcl units (perfect to small apps) | 
|---|
|  | 134 | {$DEFINE ErrorOnUnknownCritical} //Error when finds an unknown critical chunk | 
|---|
|  | 135 | {$DEFINE CheckCRC}               //Enables CRC checking | 
|---|
|  | 136 | {$DEFINE RegisterGraphic}        //Registers TPNGObject to use with TPicture | 
|---|
|  | 137 | {$DEFINE PartialTransparentDraw} //Draws partial transparent images | 
|---|
|  | 138 | {.$DEFINE Store16bits}           //Stores the extra 8 bits from 16bits/sample | 
|---|
|  | 139 | {.$DEFINE Debug}                 //For programming purposes | 
|---|
|  | 140 | {$RANGECHECKS OFF} {$J+} | 
|---|
|  | 141 |  | 
|---|
|  | 142 |  | 
|---|
|  | 143 |  | 
|---|
|  | 144 | uses | 
|---|
|  | 145 | Windows {$IFDEF UseDelphi}, Classes, Graphics, SysUtils{$ENDIF} {$IFDEF Debug}, | 
|---|
|  | 146 | dialogs{$ENDIF}, pngzlib, pnglang; | 
|---|
|  | 147 |  | 
|---|
|  | 148 | {$IFNDEF UseDelphi} | 
|---|
|  | 149 | const | 
|---|
|  | 150 | soFromBeginning = 0; | 
|---|
|  | 151 | soFromCurrent = 1; | 
|---|
|  | 152 | soFromEnd = 2; | 
|---|
|  | 153 | {$ENDIF} | 
|---|
|  | 154 |  | 
|---|
|  | 155 | const | 
|---|
|  | 156 | {ZLIB constants} | 
|---|
|  | 157 | ZLIBErrors: Array[-6..2] of string = ('incompatible version (-6)', | 
|---|
|  | 158 | 'buffer error (-5)', 'insufficient memory (-4)', 'data error (-3)', | 
|---|
|  | 159 | 'stream error (-2)', 'file error (-1)', '(0)', 'stream end (1)', | 
|---|
|  | 160 | 'need dictionary (2)'); | 
|---|
|  | 161 | Z_NO_FLUSH      = 0; | 
|---|
|  | 162 | Z_FINISH        = 4; | 
|---|
|  | 163 | Z_STREAM_END    = 1; | 
|---|
|  | 164 |  | 
|---|
|  | 165 | {Avaliable PNG filters for mode 0} | 
|---|
|  | 166 | FILTER_NONE    = 0; | 
|---|
|  | 167 | FILTER_SUB     = 1; | 
|---|
|  | 168 | FILTER_UP      = 2; | 
|---|
|  | 169 | FILTER_AVERAGE = 3; | 
|---|
|  | 170 | FILTER_PAETH   = 4; | 
|---|
|  | 171 |  | 
|---|
|  | 172 | {Avaliable color modes for PNG} | 
|---|
|  | 173 | COLOR_GRAYSCALE      = 0; | 
|---|
|  | 174 | COLOR_RGB            = 2; | 
|---|
|  | 175 | COLOR_PALETTE        = 3; | 
|---|
|  | 176 | COLOR_GRAYSCALEALPHA = 4; | 
|---|
|  | 177 | COLOR_RGBALPHA       = 6; | 
|---|
|  | 178 |  | 
|---|
|  | 179 |  | 
|---|
|  | 180 | type | 
|---|
|  | 181 | {$IFNDEF UseDelphi} | 
|---|
|  | 182 | {Custom exception handler} | 
|---|
|  | 183 | Exception = class(TObject) | 
|---|
|  | 184 | constructor Create(Msg: String); | 
|---|
|  | 185 | end; | 
|---|
|  | 186 | ExceptClass = class of Exception; | 
|---|
|  | 187 | TColor = ColorRef; | 
|---|
|  | 188 | {$ENDIF} | 
|---|
|  | 189 |  | 
|---|
|  | 190 | {Error types} | 
|---|
|  | 191 | EPNGOutMemory = class(Exception); | 
|---|
|  | 192 | EPngError = class(Exception); | 
|---|
|  | 193 | EPngUnexpectedEnd = class(Exception); | 
|---|
|  | 194 | EPngInvalidCRC = class(Exception); | 
|---|
|  | 195 | EPngInvalidIHDR = class(Exception); | 
|---|
|  | 196 | EPNGMissingMultipleIDAT = class(Exception); | 
|---|
|  | 197 | EPNGZLIBError = class(Exception); | 
|---|
|  | 198 | EPNGInvalidPalette = class(Exception); | 
|---|
|  | 199 | EPNGInvalidFileHeader = class(Exception); | 
|---|
|  | 200 | EPNGIHDRNotFirst = class(Exception); | 
|---|
|  | 201 | EPNGNotExists = class(Exception); | 
|---|
|  | 202 | EPNGSizeExceeds = class(Exception); | 
|---|
|  | 203 | EPNGMissingPalette = class(Exception); | 
|---|
|  | 204 | EPNGUnknownCriticalChunk = class(Exception); | 
|---|
|  | 205 | EPNGUnknownCompression = class(Exception); | 
|---|
|  | 206 | EPNGUnknownInterlace = class(Exception); | 
|---|
|  | 207 | EPNGNoImageData = class(Exception); | 
|---|
|  | 208 | EPNGCouldNotLoadResource = class(Exception); | 
|---|
|  | 209 | EPNGCannotChangeTransparent = class(Exception); | 
|---|
|  | 210 | EPNGHeaderNotPresent = class(Exception); | 
|---|
|  | 211 |  | 
|---|
|  | 212 | type | 
|---|
|  | 213 | {Direct access to pixels using R,G,B} | 
|---|
|  | 214 | TRGBLine = array[word] of TRGBTriple; | 
|---|
|  | 215 | pRGBLine = ^TRGBLine; | 
|---|
|  | 216 |  | 
|---|
|  | 217 | {Same as TBitmapInfo but with allocated space for} | 
|---|
|  | 218 | {palette entries} | 
|---|
|  | 219 | TMAXBITMAPINFO = packed record | 
|---|
|  | 220 | bmiHeader: TBitmapInfoHeader; | 
|---|
|  | 221 | bmiColors: packed array[0..255] of TRGBQuad; | 
|---|
|  | 222 | end; | 
|---|
|  | 223 |  | 
|---|
|  | 224 | {Transparency mode for pngs} | 
|---|
|  | 225 | TPNGTransparencyMode = (ptmNone, ptmBit, ptmPartial); | 
|---|
|  | 226 | {Pointer to a cardinal type} | 
|---|
|  | 227 | pCardinal = ^Cardinal; | 
|---|
|  | 228 | {Access to a rgb pixel} | 
|---|
|  | 229 | pRGBPixel = ^TRGBPixel; | 
|---|
|  | 230 | TRGBPixel = packed record | 
|---|
|  | 231 | B, G, R: Byte; | 
|---|
|  | 232 | end; | 
|---|
|  | 233 |  | 
|---|
|  | 234 | {Pointer to an array of bytes type} | 
|---|
|  | 235 | TByteArray = Array[Word] of Byte; | 
|---|
|  | 236 | pByteArray = ^TByteArray; | 
|---|
|  | 237 |  | 
|---|
|  | 238 | {Forward} | 
|---|
|  | 239 | TPNGObject = class; | 
|---|
|  | 240 | pPointerArray = ^TPointerArray; | 
|---|
|  | 241 | TPointerArray = Array[Word] of Pointer; | 
|---|
|  | 242 |  | 
|---|
|  | 243 | {Contains a list of objects} | 
|---|
|  | 244 | TPNGPointerList = class | 
|---|
|  | 245 | private | 
|---|
|  | 246 | fOwner: TPNGObject; | 
|---|
|  | 247 | fCount : Cardinal; | 
|---|
|  | 248 | fMemory: pPointerArray; | 
|---|
|  | 249 | function GetItem(Index: Cardinal): Pointer; | 
|---|
|  | 250 | procedure SetItem(Index: Cardinal; const Value: Pointer); | 
|---|
|  | 251 | protected | 
|---|
|  | 252 | {Removes an item} | 
|---|
|  | 253 | function Remove(Value: Pointer): Pointer; virtual; | 
|---|
|  | 254 | {Inserts an item} | 
|---|
|  | 255 | procedure Insert(Value: Pointer; Position: Cardinal); | 
|---|
|  | 256 | {Add a new item} | 
|---|
|  | 257 | procedure Add(Value: Pointer); | 
|---|
|  | 258 | {Returns an item} | 
|---|
|  | 259 | property Item[Index: Cardinal]: Pointer read GetItem write SetItem; | 
|---|
|  | 260 | {Set the size of the list} | 
|---|
|  | 261 | procedure SetSize(const Size: Cardinal); | 
|---|
|  | 262 | {Returns owner} | 
|---|
|  | 263 | property Owner: TPNGObject read fOwner; | 
|---|
|  | 264 | public | 
|---|
|  | 265 | {Returns number of items} | 
|---|
|  | 266 | property Count: Cardinal read fCount write SetSize; | 
|---|
|  | 267 | {Object being either created or destroyed} | 
|---|
|  | 268 | constructor Create(AOwner: TPNGObject); | 
|---|
|  | 269 | destructor Destroy; override; | 
|---|
|  | 270 | end; | 
|---|
|  | 271 |  | 
|---|
|  | 272 | {Forward declaration} | 
|---|
|  | 273 | TChunk = class; | 
|---|
|  | 274 | TChunkClass = class of TChunk; | 
|---|
|  | 275 |  | 
|---|
|  | 276 | {Same as TPNGPointerList but providing typecasted values} | 
|---|
|  | 277 | TPNGList = class(TPNGPointerList) | 
|---|
|  | 278 | private | 
|---|
|  | 279 | {Used with property Item} | 
|---|
|  | 280 | function GetItem(Index: Cardinal): TChunk; | 
|---|
|  | 281 | public | 
|---|
|  | 282 | {Removes an item} | 
|---|
|  | 283 | procedure RemoveChunk(Chunk: TChunk); overload; | 
|---|
|  | 284 | {Add a new chunk using the class from the parameter} | 
|---|
|  | 285 | function Add(ChunkClass: TChunkClass): TChunk; | 
|---|
|  | 286 | {Returns pointer to the first chunk of class} | 
|---|
|  | 287 | function ItemFromClass(ChunkClass: TChunkClass): TChunk; | 
|---|
|  | 288 | {Returns a chunk item from the list} | 
|---|
|  | 289 | property Item[Index: Cardinal]: TChunk read GetItem; | 
|---|
|  | 290 | end; | 
|---|
|  | 291 |  | 
|---|
|  | 292 | {$IFNDEF UseDelphi} | 
|---|
|  | 293 | {The STREAMs bellow are only needed in case delphi provided ones is not} | 
|---|
|  | 294 | {avaliable (UseDelphi trigger not set)} | 
|---|
|  | 295 | {Object becomes handles} | 
|---|
|  | 296 | TCanvas = THandle; | 
|---|
|  | 297 | TBitmap = HBitmap; | 
|---|
|  | 298 | {Trick to work} | 
|---|
|  | 299 | TPersistent = TObject; | 
|---|
|  | 300 |  | 
|---|
|  | 301 | {Base class for all streams} | 
|---|
|  | 302 | TStream = class | 
|---|
|  | 303 | protected | 
|---|
|  | 304 | {Returning/setting size} | 
|---|
|  | 305 | function GetSize: Longint; virtual; | 
|---|
|  | 306 | procedure SetSize(const Value: Longint); virtual; abstract; | 
|---|
|  | 307 | {Returns/set position} | 
|---|
|  | 308 | function GetPosition: Longint; virtual; | 
|---|
|  | 309 | procedure SetPosition(const Value: Longint); virtual; | 
|---|
|  | 310 | public | 
|---|
|  | 311 | {Returns/sets current position} | 
|---|
|  | 312 | property Position: Longint read GetPosition write SetPosition; | 
|---|
|  | 313 | {Property returns/sets size} | 
|---|
|  | 314 | property Size: Longint read GetSize write SetSize; | 
|---|
|  | 315 | {Allows reading/writing data} | 
|---|
|  | 316 | function Read(var Buffer; Count: Longint): Cardinal; virtual; abstract; | 
|---|
|  | 317 | function Write(const Buffer; Count: Longint): Cardinal; virtual; abstract; | 
|---|
|  | 318 | {Copies from another Stream} | 
|---|
|  | 319 | function CopyFrom(Source: TStream; | 
|---|
|  | 320 | Count: Cardinal): Cardinal; virtual; | 
|---|
|  | 321 | {Seeks a stream position} | 
|---|
|  | 322 | function Seek(Offset: Longint; Origin: Word): Longint; virtual; abstract; | 
|---|
|  | 323 | end; | 
|---|
|  | 324 |  | 
|---|
|  | 325 | {File stream modes} | 
|---|
|  | 326 | TFileStreamMode = (fsmRead, fsmWrite, fsmCreate); | 
|---|
|  | 327 | TFileStreamModeSet = set of TFileStreamMode; | 
|---|
|  | 328 |  | 
|---|
|  | 329 | {File stream for reading from files} | 
|---|
|  | 330 | TFileStream = class(TStream) | 
|---|
|  | 331 | private | 
|---|
|  | 332 | {Opened mode} | 
|---|
|  | 333 | Filemode: TFileStreamModeSet; | 
|---|
|  | 334 | {Handle} | 
|---|
|  | 335 | fHandle: THandle; | 
|---|
|  | 336 | protected | 
|---|
|  | 337 | {Set the size of the file} | 
|---|
|  | 338 | procedure SetSize(const Value: Longint); override; | 
|---|
|  | 339 | public | 
|---|
|  | 340 | {Seeks a file position} | 
|---|
|  | 341 | function Seek(Offset: Longint; Origin: Word): Longint; override; | 
|---|
|  | 342 | {Reads/writes data from/to the file} | 
|---|
|  | 343 | function Read(var Buffer; Count: Longint): Cardinal; override; | 
|---|
|  | 344 | function Write(const Buffer; Count: Longint): Cardinal; override; | 
|---|
|  | 345 | {Stream being created and destroy} | 
|---|
|  | 346 | constructor Create(Filename: String; Mode: TFileStreamModeSet); | 
|---|
|  | 347 | destructor Destroy; override; | 
|---|
|  | 348 | end; | 
|---|
|  | 349 |  | 
|---|
|  | 350 | {Stream for reading from resources} | 
|---|
|  | 351 | TResourceStream = class(TStream) | 
|---|
|  | 352 | constructor Create(Instance: HInst; const ResName: String; ResType:PChar); | 
|---|
|  | 353 | private | 
|---|
|  | 354 | {Variables for reading} | 
|---|
|  | 355 | Size: Integer; | 
|---|
|  | 356 | Memory: Pointer; | 
|---|
|  | 357 | Position: Integer; | 
|---|
|  | 358 | protected | 
|---|
|  | 359 | {Set the size of the file} | 
|---|
|  | 360 | procedure SetSize(const Value: Longint); override; | 
|---|
|  | 361 | public | 
|---|
|  | 362 | {Stream processing} | 
|---|
|  | 363 | function Read(var Buffer; Count: Integer): Cardinal; override; | 
|---|
|  | 364 | function Seek(Offset: Integer; Origin: Word): Longint; override; | 
|---|
|  | 365 | function Write(const Buffer; Count: Longint): Cardinal; override; | 
|---|
|  | 366 | end; | 
|---|
|  | 367 | {$ENDIF} | 
|---|
|  | 368 |  | 
|---|
|  | 369 | {Forward} | 
|---|
|  | 370 | TChunkIHDR = class; | 
|---|
|  | 371 | {Interlace method} | 
|---|
|  | 372 | TInterlaceMethod = (imNone, imAdam7); | 
|---|
|  | 373 | {Compression level type} | 
|---|
|  | 374 | TCompressionLevel = 0..9; | 
|---|
|  | 375 | {Filters type} | 
|---|
|  | 376 | TFilter = (pfNone, pfSub, pfUp, pfAverage, pfPaeth); | 
|---|
|  | 377 | TFilters = set of TFilter; | 
|---|
|  | 378 |  | 
|---|
|  | 379 | {Png implementation object} | 
|---|
|  | 380 | TPngObject = class{$IFDEF UseDelphi}(TGraphic){$ENDIF} | 
|---|
|  | 381 | protected | 
|---|
|  | 382 | {Inverse gamma table values} | 
|---|
|  | 383 | InverseGamma: Array[Byte] of Byte; | 
|---|
|  | 384 | procedure InitializeGamma; | 
|---|
|  | 385 | private | 
|---|
|  | 386 | {Temporary palette} | 
|---|
|  | 387 | TempPalette: HPalette; | 
|---|
|  | 388 | {Filters to test to encode} | 
|---|
|  | 389 | fFilters: TFilters; | 
|---|
|  | 390 | {Compression level for ZLIB} | 
|---|
|  | 391 | fCompressionLevel: TCompressionLevel; | 
|---|
|  | 392 | {Maximum size for IDAT chunks} | 
|---|
|  | 393 | fMaxIdatSize: Integer; | 
|---|
|  | 394 | {Returns if image is interlaced} | 
|---|
|  | 395 | fInterlaceMethod: TInterlaceMethod; | 
|---|
|  | 396 | {Chunks object} | 
|---|
|  | 397 | fChunkList: TPngList; | 
|---|
|  | 398 | {Clear all chunks in the list} | 
|---|
|  | 399 | procedure ClearChunks; | 
|---|
|  | 400 | {Returns if header is present} | 
|---|
|  | 401 | function HeaderPresent: Boolean; | 
|---|
|  | 402 | {Returns linesize and byte offset for pixels} | 
|---|
|  | 403 | procedure GetPixelInfo(var LineSize, Offset: Cardinal); | 
|---|
|  | 404 | procedure SetMaxIdatSize(const Value: Integer); | 
|---|
|  | 405 | function GetAlphaScanline(const LineIndex: Integer): pByteArray; | 
|---|
|  | 406 | function GetScanline(const LineIndex: Integer): Pointer; | 
|---|
|  | 407 | {$IFDEF Store16bits} | 
|---|
|  | 408 | function GetExtraScanline(const LineIndex: Integer): Pointer; | 
|---|
|  | 409 | {$ENDIF} | 
|---|
|  | 410 | function GetTransparencyMode: TPNGTransparencyMode; | 
|---|
|  | 411 | function GetTransparentColor: TColor; | 
|---|
|  | 412 | procedure SetTransparentColor(const Value: TColor); | 
|---|
|  | 413 | protected | 
|---|
|  | 414 | {Returns the image palette} | 
|---|
|  | 415 | function GetPalette: HPALETTE; {$IFDEF UseDelphi}override;{$ENDIF} | 
|---|
|  | 416 | {Returns/sets image width and height} | 
|---|
|  | 417 | function GetWidth: Integer; {$IFDEF UseDelphi}override;{$ENDIF} | 
|---|
|  | 418 | function GetHeight: Integer; {$IFDEF UseDelphi}override; {$ENDIF} | 
|---|
|  | 419 | procedure SetWidth(Value: Integer);  {$IFDEF UseDelphi}override; {$ENDIF} | 
|---|
|  | 420 | procedure SetHeight(Value: Integer);  {$IFDEF UseDelphi}override;{$ENDIF} | 
|---|
|  | 421 | {Assigns from another TPNGObject} | 
|---|
|  | 422 | procedure AssignPNG(Source: TPNGObject); | 
|---|
|  | 423 | {Returns if the image is empty} | 
|---|
|  | 424 | function GetEmpty: Boolean; {$IFDEF UseDelphi}override; {$ENDIF} | 
|---|
|  | 425 | {Used with property Header} | 
|---|
|  | 426 | function GetHeader: TChunkIHDR; | 
|---|
|  | 427 | {Draws using partial transparency} | 
|---|
|  | 428 | procedure DrawPartialTrans(DC: HDC; Rect: TRect); | 
|---|
|  | 429 | {$IFDEF UseDelphi} | 
|---|
|  | 430 | {Returns if the image is transparent} | 
|---|
|  | 431 | function GetTransparent: Boolean; override; | 
|---|
|  | 432 | {$ENDIF} | 
|---|
|  | 433 | {Returns a pixel} | 
|---|
|  | 434 | function GetPixels(const X, Y: Integer): TColor; virtual; | 
|---|
|  | 435 | procedure SetPixels(const X, Y: Integer; const Value: TColor); virtual; | 
|---|
|  | 436 | public | 
|---|
|  | 437 | {Gamma table array} | 
|---|
|  | 438 | GammaTable: Array[Byte] of Byte; | 
|---|
|  | 439 | {Generates alpha information} | 
|---|
|  | 440 | procedure CreateAlpha; | 
|---|
|  | 441 | {Removes the image transparency} | 
|---|
|  | 442 | procedure RemoveTransparency; | 
|---|
|  | 443 | {Transparent color} | 
|---|
|  | 444 | property TransparentColor: TColor read GetTransparentColor write | 
|---|
|  | 445 | SetTransparentColor; | 
|---|
|  | 446 | {Add text chunk, TChunkTEXT, TChunkzTXT} | 
|---|
|  | 447 | procedure AddtEXt(const Keyword, Text: String); | 
|---|
|  | 448 | procedure AddzTXt(const Keyword, Text: String); | 
|---|
|  | 449 | {$IFDEF UseDelphi} | 
|---|
|  | 450 | {Saves to clipboard format (thanks to Antoine Pottern)} | 
|---|
|  | 451 | procedure SaveToClipboardFormat(var AFormat: Word; var AData: THandle; | 
|---|
|  | 452 | var APalette: HPalette); override; | 
|---|
|  | 453 | procedure LoadFromClipboardFormat(AFormat: Word; AData: THandle; | 
|---|
|  | 454 | APalette: HPalette); override; | 
|---|
|  | 455 | {$ENDIF} | 
|---|
|  | 456 | {Calling errors} | 
|---|
|  | 457 | procedure RaiseError(ExceptionClass: ExceptClass; Text: String); | 
|---|
|  | 458 | {Returns a scanline from png} | 
|---|
|  | 459 | property Scanline[const Index: Integer]: Pointer read GetScanline; | 
|---|
|  | 460 | {$IFDEF Store16bits} | 
|---|
|  | 461 | property ExtraScanline[const Index: Integer]: Pointer read GetExtraScanline; | 
|---|
|  | 462 | {$ENDIF} | 
|---|
|  | 463 | property AlphaScanline[const Index: Integer]: pByteArray read GetAlphaScanline; | 
|---|
|  | 464 | {Returns pointer to the header} | 
|---|
|  | 465 | property Header: TChunkIHDR read GetHeader; | 
|---|
|  | 466 | {Returns the transparency mode used by this png} | 
|---|
|  | 467 | property TransparencyMode: TPNGTransparencyMode read GetTransparencyMode; | 
|---|
|  | 468 | {Assigns from another object} | 
|---|
|  | 469 | procedure Assign(Source: TPersistent);{$IFDEF UseDelphi}override;{$ENDIF} | 
|---|
|  | 470 | {Assigns to another object} | 
|---|
|  | 471 | procedure AssignTo(Dest: TPersistent);{$IFDEF UseDelphi}override;{$ENDIF} | 
|---|
|  | 472 | {Assigns from a windows bitmap handle} | 
|---|
|  | 473 | procedure AssignHandle(Handle: HBitmap; Transparent: Boolean; | 
|---|
|  | 474 | TransparentColor: ColorRef); | 
|---|
|  | 475 | {Draws the image into a canvas} | 
|---|
|  | 476 | procedure Draw(ACanvas: TCanvas; const Rect: TRect); | 
|---|
|  | 477 | {$IFDEF UseDelphi}override;{$ENDIF} | 
|---|
|  | 478 | {Width and height properties} | 
|---|
|  | 479 | property Width: Integer read GetWidth; | 
|---|
|  | 480 | property Height: Integer read GetHeight; | 
|---|
|  | 481 | {Returns if the image is interlaced} | 
|---|
|  | 482 | property InterlaceMethod: TInterlaceMethod read fInterlaceMethod | 
|---|
|  | 483 | write fInterlaceMethod; | 
|---|
|  | 484 | {Filters to test to encode} | 
|---|
|  | 485 | property Filters: TFilters read fFilters write fFilters; | 
|---|
|  | 486 | {Maximum size for IDAT chunks, default and minimum is 65536} | 
|---|
|  | 487 | property MaxIdatSize: Integer read fMaxIdatSize write SetMaxIdatSize; | 
|---|
|  | 488 | {Property to return if the image is empty or not} | 
|---|
|  | 489 | property Empty: Boolean read GetEmpty; | 
|---|
|  | 490 | {Compression level} | 
|---|
|  | 491 | property CompressionLevel: TCompressionLevel read fCompressionLevel | 
|---|
|  | 492 | write fCompressionLevel; | 
|---|
|  | 493 | {Access to the chunk list} | 
|---|
|  | 494 | property Chunks: TPngList read fChunkList; | 
|---|
|  | 495 | {Object being created and destroyed} | 
|---|
|  | 496 | constructor Create; {$IFDEF UseDelphi}override;{$ENDIF} | 
|---|
|  | 497 | destructor Destroy; override; | 
|---|
|  | 498 | {$IFNDEF UseDelphi}procedure LoadFromFile(const Filename: String);{$ENDIF} | 
|---|
|  | 499 | {$IFNDEF UseDelphi}procedure SaveToFile(const Filename: String);{$ENDIF} | 
|---|
|  | 500 | procedure LoadFromStream(Stream: TStream); {$IFDEF UseDelphi}override;{$ENDIF} | 
|---|
|  | 501 | procedure SaveToStream(Stream: TStream); {$IFDEF UseDelphi}override;{$ENDIF} | 
|---|
|  | 502 | {Loading the image from resources} | 
|---|
|  | 503 | procedure LoadFromResourceName(Instance: HInst; const Name: String); | 
|---|
|  | 504 | procedure LoadFromResourceID(Instance: HInst; ResID: Integer); | 
|---|
|  | 505 | {Access to the png pixels} | 
|---|
|  | 506 | property Pixels[const X, Y: Integer]: TColor read GetPixels write SetPixels; | 
|---|
|  | 507 | {Palette property} | 
|---|
|  | 508 | {$IFNDEF UseDelphi}property Palette: HPalette read GetPalette;{$ENDIF} | 
|---|
|  | 509 | end; | 
|---|
|  | 510 |  | 
|---|
|  | 511 | {Chunk name object} | 
|---|
|  | 512 | TChunkName = Array[0..3] of Char; | 
|---|
|  | 513 |  | 
|---|
|  | 514 | {Global chunk object} | 
|---|
|  | 515 | TChunk = class | 
|---|
|  | 516 | private | 
|---|
|  | 517 | {Contains data} | 
|---|
|  | 518 | fData: Pointer; | 
|---|
|  | 519 | fDataSize: Cardinal; | 
|---|
|  | 520 | {Stores owner} | 
|---|
|  | 521 | fOwner: TPngObject; | 
|---|
|  | 522 | {Stores the chunk name} | 
|---|
|  | 523 | fName: TChunkName; | 
|---|
|  | 524 | {Returns pointer to the TChunkIHDR} | 
|---|
|  | 525 | function GetHeader: TChunkIHDR; | 
|---|
|  | 526 | {Used with property index} | 
|---|
|  | 527 | function GetIndex: Integer; | 
|---|
|  | 528 | {Should return chunk class/name} | 
|---|
|  | 529 | class function GetName: String; virtual; | 
|---|
|  | 530 | {Returns the chunk name} | 
|---|
|  | 531 | function GetChunkName: String; | 
|---|
|  | 532 | public | 
|---|
|  | 533 | {Returns index from list} | 
|---|
|  | 534 | property Index: Integer read GetIndex; | 
|---|
|  | 535 | {Returns pointer to the TChunkIHDR} | 
|---|
|  | 536 | property Header: TChunkIHDR read GetHeader; | 
|---|
|  | 537 | {Resize the data} | 
|---|
|  | 538 | procedure ResizeData(const NewSize: Cardinal); | 
|---|
|  | 539 | {Returns data and size} | 
|---|
|  | 540 | property Data: Pointer read fData; | 
|---|
|  | 541 | property DataSize: Cardinal read fDataSize; | 
|---|
|  | 542 | {Assigns from another TChunk} | 
|---|
|  | 543 | procedure Assign(Source: TChunk); virtual; | 
|---|
|  | 544 | {Returns owner} | 
|---|
|  | 545 | property Owner: TPngObject read fOwner; | 
|---|
|  | 546 | {Being destroyed/created} | 
|---|
|  | 547 | constructor Create(Owner: TPngObject); virtual; | 
|---|
|  | 548 | destructor Destroy; override; | 
|---|
|  | 549 | {Returns chunk class/name} | 
|---|
|  | 550 | property Name: String read GetChunkName; | 
|---|
|  | 551 | {Loads the chunk from a stream} | 
|---|
|  | 552 | function LoadFromStream(Stream: TStream; const ChunkName: TChunkName; | 
|---|
|  | 553 | Size: Integer): Boolean; virtual; | 
|---|
|  | 554 | {Saves the chunk to a stream} | 
|---|
|  | 555 | function SaveData(Stream: TStream): Boolean; | 
|---|
|  | 556 | function SaveToStream(Stream: TStream): Boolean; virtual; | 
|---|
|  | 557 | end; | 
|---|
|  | 558 |  | 
|---|
|  | 559 | {Chunk classes} | 
|---|
|  | 560 | TChunkIEND = class(TChunk);     {End chunk} | 
|---|
|  | 561 |  | 
|---|
|  | 562 | {IHDR data} | 
|---|
|  | 563 | pIHDRData = ^TIHDRData; | 
|---|
|  | 564 | TIHDRData = packed record | 
|---|
|  | 565 | Width, Height: Cardinal; | 
|---|
|  | 566 | BitDepth, | 
|---|
|  | 567 | ColorType, | 
|---|
|  | 568 | CompressionMethod, | 
|---|
|  | 569 | FilterMethod, | 
|---|
|  | 570 | InterlaceMethod: Byte; | 
|---|
|  | 571 | end; | 
|---|
|  | 572 |  | 
|---|
|  | 573 | {Information header chunk} | 
|---|
|  | 574 | TChunkIHDR = class(TChunk) | 
|---|
|  | 575 | private | 
|---|
|  | 576 | {Current image} | 
|---|
|  | 577 | ImageHandle: HBitmap; | 
|---|
|  | 578 | ImageDC: HDC; | 
|---|
|  | 579 | {Output windows bitmap} | 
|---|
|  | 580 | HasPalette: Boolean; | 
|---|
|  | 581 | BitmapInfo: TMaxBitmapInfo; | 
|---|
|  | 582 | BytesPerRow: Integer; | 
|---|
|  | 583 | {Stores the image bytes} | 
|---|
|  | 584 | {$IFDEF Store16bits}ExtraImageData: Pointer;{$ENDIF} | 
|---|
|  | 585 | ImageData: pointer; | 
|---|
|  | 586 | ImageAlpha: Pointer; | 
|---|
|  | 587 |  | 
|---|
|  | 588 | {Contains all the ihdr data} | 
|---|
|  | 589 | IHDRData: TIHDRData; | 
|---|
|  | 590 | protected | 
|---|
|  | 591 | {Resizes the image data to fill the color type, bit depth, } | 
|---|
|  | 592 | {width and height parameters} | 
|---|
|  | 593 | procedure PrepareImageData; | 
|---|
|  | 594 | {Release allocated ImageData memory} | 
|---|
|  | 595 | procedure FreeImageData; | 
|---|
|  | 596 | public | 
|---|
|  | 597 | {Access to ImageHandle} | 
|---|
|  | 598 | property ImageHandleValue: HBitmap read ImageHandle; | 
|---|
|  | 599 | {Properties} | 
|---|
|  | 600 | property Width: Cardinal read IHDRData.Width write IHDRData.Width; | 
|---|
|  | 601 | property Height: Cardinal read IHDRData.Height write IHDRData.Height; | 
|---|
|  | 602 | property BitDepth: Byte read IHDRData.BitDepth write IHDRData.BitDepth; | 
|---|
|  | 603 | property ColorType: Byte read IHDRData.ColorType write IHDRData.ColorType; | 
|---|
|  | 604 | property CompressionMethod: Byte read IHDRData.CompressionMethod | 
|---|
|  | 605 | write IHDRData.CompressionMethod; | 
|---|
|  | 606 | property FilterMethod: Byte read IHDRData.FilterMethod | 
|---|
|  | 607 | write IHDRData.FilterMethod; | 
|---|
|  | 608 | property InterlaceMethod: Byte read IHDRData.InterlaceMethod | 
|---|
|  | 609 | write IHDRData.InterlaceMethod; | 
|---|
|  | 610 | {Loads the chunk from a stream} | 
|---|
|  | 611 | function LoadFromStream(Stream: TStream; const ChunkName: TChunkName; | 
|---|
|  | 612 | Size: Integer): Boolean; override; | 
|---|
|  | 613 | {Saves the chunk to a stream} | 
|---|
|  | 614 | function SaveToStream(Stream: TStream): Boolean; override; | 
|---|
|  | 615 | {Destructor/constructor} | 
|---|
|  | 616 | constructor Create(Owner: TPngObject); override; | 
|---|
|  | 617 | destructor Destroy; override; | 
|---|
|  | 618 | {Assigns from another TChunk} | 
|---|
|  | 619 | procedure Assign(Source: TChunk); override; | 
|---|
|  | 620 | end; | 
|---|
|  | 621 |  | 
|---|
|  | 622 | {Gamma chunk} | 
|---|
|  | 623 | TChunkgAMA = class(TChunk) | 
|---|
|  | 624 | private | 
|---|
|  | 625 | {Returns/sets the value for the gamma chunk} | 
|---|
|  | 626 | function GetValue: Cardinal; | 
|---|
|  | 627 | procedure SetValue(const Value: Cardinal); | 
|---|
|  | 628 | public | 
|---|
|  | 629 | {Returns/sets gamma value} | 
|---|
|  | 630 | property Gamma: Cardinal read GetValue write SetValue; | 
|---|
|  | 631 | {Loading the chunk from a stream} | 
|---|
|  | 632 | function LoadFromStream(Stream: TStream; const ChunkName: TChunkName; | 
|---|
|  | 633 | Size: Integer): Boolean; override; | 
|---|
|  | 634 | {Being created} | 
|---|
|  | 635 | constructor Create(Owner: TPngObject); override; | 
|---|
|  | 636 | {Assigns from another TChunk} | 
|---|
|  | 637 | procedure Assign(Source: TChunk); override; | 
|---|
|  | 638 | end; | 
|---|
|  | 639 |  | 
|---|
|  | 640 | {ZLIB Decompression extra information} | 
|---|
|  | 641 | TZStreamRec2 = packed record | 
|---|
|  | 642 | {From ZLIB} | 
|---|
|  | 643 | ZLIB: TZStreamRec; | 
|---|
|  | 644 | {Additional info} | 
|---|
|  | 645 | Data: Pointer; | 
|---|
|  | 646 | fStream   : TStream; | 
|---|
|  | 647 | end; | 
|---|
|  | 648 |  | 
|---|
|  | 649 | {Palette chunk} | 
|---|
|  | 650 | TChunkPLTE = class(TChunk) | 
|---|
|  | 651 | private | 
|---|
|  | 652 | {Number of items in the palette} | 
|---|
|  | 653 | fCount: Integer; | 
|---|
|  | 654 | {Contains the palette handle} | 
|---|
|  | 655 | function GetPaletteItem(Index: Byte): TRGBQuad; | 
|---|
|  | 656 | public | 
|---|
|  | 657 | {Returns the color for each item in the palette} | 
|---|
|  | 658 | property Item[Index: Byte]: TRGBQuad read GetPaletteItem; | 
|---|
|  | 659 | {Returns the number of items in the palette} | 
|---|
|  | 660 | property Count: Integer read fCount; | 
|---|
|  | 661 | {Loads the chunk from a stream} | 
|---|
|  | 662 | function LoadFromStream(Stream: TStream; const ChunkName: TChunkName; | 
|---|
|  | 663 | Size: Integer): Boolean; override; | 
|---|
|  | 664 | {Saves the chunk to a stream} | 
|---|
|  | 665 | function SaveToStream(Stream: TStream): Boolean; override; | 
|---|
|  | 666 | {Assigns from another TChunk} | 
|---|
|  | 667 | procedure Assign(Source: TChunk); override; | 
|---|
|  | 668 | end; | 
|---|
|  | 669 |  | 
|---|
|  | 670 | {Transparency information} | 
|---|
|  | 671 | TChunktRNS = class(TChunk) | 
|---|
|  | 672 | private | 
|---|
|  | 673 | fBitTransparency: Boolean; | 
|---|
|  | 674 | function GetTransparentColor: ColorRef; | 
|---|
|  | 675 | {Returns the transparent color} | 
|---|
|  | 676 | procedure SetTransparentColor(const Value: ColorRef); | 
|---|
|  | 677 | public | 
|---|
|  | 678 | {Palette values for transparency} | 
|---|
|  | 679 | PaletteValues: Array[Byte] of Byte; | 
|---|
|  | 680 | {Returns if it uses bit transparency} | 
|---|
|  | 681 | property BitTransparency: Boolean read fBitTransparency; | 
|---|
|  | 682 | {Returns the transparent color} | 
|---|
|  | 683 | property TransparentColor: ColorRef read GetTransparentColor write | 
|---|
|  | 684 | SetTransparentColor; | 
|---|
|  | 685 | {Loads/saves the chunk from/to a stream} | 
|---|
|  | 686 | function LoadFromStream(Stream: TStream; const ChunkName: TChunkName; | 
|---|
|  | 687 | Size: Integer): Boolean; override; | 
|---|
|  | 688 | function SaveToStream(Stream: TStream): Boolean; override; | 
|---|
|  | 689 | {Assigns from another TChunk} | 
|---|
|  | 690 | procedure Assign(Source: TChunk); override; | 
|---|
|  | 691 | end; | 
|---|
|  | 692 |  | 
|---|
|  | 693 | {Actual image information} | 
|---|
|  | 694 | TChunkIDAT = class(TChunk) | 
|---|
|  | 695 | private | 
|---|
|  | 696 | {Holds another pointer to the TChunkIHDR} | 
|---|
|  | 697 | Header: TChunkIHDR; | 
|---|
|  | 698 | {Stores temporary image width and height} | 
|---|
|  | 699 | ImageWidth, ImageHeight: Integer; | 
|---|
|  | 700 | {Size in bytes of each line and offset} | 
|---|
|  | 701 | Row_Bytes, Offset : Cardinal; | 
|---|
|  | 702 | {Contains data for the lines} | 
|---|
|  | 703 | Encode_Buffer: Array[0..5] of pByteArray; | 
|---|
|  | 704 | Row_Buffer: Array[Boolean] of pByteArray; | 
|---|
|  | 705 | {Variable to invert the Row_Buffer used} | 
|---|
|  | 706 | RowUsed: Boolean; | 
|---|
|  | 707 | {Ending position for the current IDAT chunk} | 
|---|
|  | 708 | EndPos: Integer; | 
|---|
|  | 709 | {Filter the current line} | 
|---|
|  | 710 | procedure FilterRow; | 
|---|
|  | 711 | {Filter to encode and returns the best filter} | 
|---|
|  | 712 | function FilterToEncode: Byte; | 
|---|
|  | 713 | {Reads ZLIB compressed data} | 
|---|
|  | 714 | function IDATZlibRead(var ZLIBStream: TZStreamRec2; Buffer: Pointer; | 
|---|
|  | 715 | Count: Integer; var EndPos: Integer; var crcfile: Cardinal): Integer; | 
|---|
|  | 716 | {Compress and writes IDAT data} | 
|---|
|  | 717 | procedure IDATZlibWrite(var ZLIBStream: TZStreamRec2; Buffer: Pointer; | 
|---|
|  | 718 | const Length: Cardinal); | 
|---|
|  | 719 | procedure FinishIDATZlib(var ZLIBStream: TZStreamRec2); | 
|---|
|  | 720 | {Prepares the palette} | 
|---|
|  | 721 | procedure PreparePalette; | 
|---|
|  | 722 | protected | 
|---|
|  | 723 | {Decode interlaced image} | 
|---|
|  | 724 | procedure DecodeInterlacedAdam7(Stream: TStream; | 
|---|
|  | 725 | var ZLIBStream: TZStreamRec2; const Size: Integer; var crcfile: Cardinal); | 
|---|
|  | 726 | {Decode non interlaced imaged} | 
|---|
|  | 727 | procedure DecodeNonInterlaced(Stream: TStream; | 
|---|
|  | 728 | var ZLIBStream: TZStreamRec2; const Size: Integer; | 
|---|
|  | 729 | var crcfile: Cardinal); | 
|---|
|  | 730 | protected | 
|---|
|  | 731 | {Encode non interlaced images} | 
|---|
|  | 732 | procedure EncodeNonInterlaced(Stream: TStream; | 
|---|
|  | 733 | var ZLIBStream: TZStreamRec2); | 
|---|
|  | 734 | {Encode interlaced images} | 
|---|
|  | 735 | procedure EncodeInterlacedAdam7(Stream: TStream; | 
|---|
|  | 736 | var ZLIBStream: TZStreamRec2); | 
|---|
|  | 737 | protected | 
|---|
|  | 738 | {Memory copy methods to decode} | 
|---|
|  | 739 | procedure CopyNonInterlacedRGB8( | 
|---|
|  | 740 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 741 | procedure CopyNonInterlacedRGB16( | 
|---|
|  | 742 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 743 | procedure CopyNonInterlacedPalette148( | 
|---|
|  | 744 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 745 | procedure CopyNonInterlacedPalette2( | 
|---|
|  | 746 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 747 | procedure CopyNonInterlacedGray2( | 
|---|
|  | 748 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 749 | procedure CopyNonInterlacedGrayscale16( | 
|---|
|  | 750 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 751 | procedure CopyNonInterlacedRGBAlpha8( | 
|---|
|  | 752 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 753 | procedure CopyNonInterlacedRGBAlpha16( | 
|---|
|  | 754 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 755 | procedure CopyNonInterlacedGrayscaleAlpha8( | 
|---|
|  | 756 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 757 | procedure CopyNonInterlacedGrayscaleAlpha16( | 
|---|
|  | 758 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 759 | procedure CopyInterlacedRGB8(const Pass: Byte; | 
|---|
|  | 760 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 761 | procedure CopyInterlacedRGB16(const Pass: Byte; | 
|---|
|  | 762 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 763 | procedure CopyInterlacedPalette148(const Pass: Byte; | 
|---|
|  | 764 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 765 | procedure CopyInterlacedPalette2(const Pass: Byte; | 
|---|
|  | 766 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 767 | procedure CopyInterlacedGray2(const Pass: Byte; | 
|---|
|  | 768 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 769 | procedure CopyInterlacedGrayscale16(const Pass: Byte; | 
|---|
|  | 770 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 771 | procedure CopyInterlacedRGBAlpha8(const Pass: Byte; | 
|---|
|  | 772 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 773 | procedure CopyInterlacedRGBAlpha16(const Pass: Byte; | 
|---|
|  | 774 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 775 | procedure CopyInterlacedGrayscaleAlpha8(const Pass: Byte; | 
|---|
|  | 776 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 777 | procedure CopyInterlacedGrayscaleAlpha16(const Pass: Byte; | 
|---|
|  | 778 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 779 | protected | 
|---|
|  | 780 | {Memory copy methods to encode} | 
|---|
|  | 781 | procedure EncodeNonInterlacedRGB8(Src, Dest, Trans: pChar); | 
|---|
|  | 782 | procedure EncodeNonInterlacedRGB16(Src, Dest, Trans: pChar); | 
|---|
|  | 783 | procedure EncodeNonInterlacedGrayscale16(Src, Dest, Trans: pChar); | 
|---|
|  | 784 | procedure EncodeNonInterlacedPalette148(Src, Dest, Trans: pChar); | 
|---|
|  | 785 | procedure EncodeNonInterlacedRGBAlpha8(Src, Dest, Trans: pChar); | 
|---|
|  | 786 | procedure EncodeNonInterlacedRGBAlpha16(Src, Dest, Trans: pChar); | 
|---|
|  | 787 | procedure EncodeNonInterlacedGrayscaleAlpha8(Src, Dest, Trans: pChar); | 
|---|
|  | 788 | procedure EncodeNonInterlacedGrayscaleAlpha16(Src, Dest, Trans: pChar); | 
|---|
|  | 789 | procedure EncodeInterlacedRGB8(const Pass: Byte; Src, Dest, Trans: pChar); | 
|---|
|  | 790 | procedure EncodeInterlacedRGB16(const Pass: Byte; Src, Dest, Trans: pChar); | 
|---|
|  | 791 | procedure EncodeInterlacedPalette148(const Pass: Byte; | 
|---|
|  | 792 | Src, Dest, Trans: pChar); | 
|---|
|  | 793 | procedure EncodeInterlacedGrayscale16(const Pass: Byte; | 
|---|
|  | 794 | Src, Dest, Trans: pChar); | 
|---|
|  | 795 | procedure EncodeInterlacedRGBAlpha8(const Pass: Byte; | 
|---|
|  | 796 | Src, Dest, Trans: pChar); | 
|---|
|  | 797 | procedure EncodeInterlacedRGBAlpha16(const Pass: Byte; | 
|---|
|  | 798 | Src, Dest, Trans: pChar); | 
|---|
|  | 799 | procedure EncodeInterlacedGrayscaleAlpha8(const Pass: Byte; | 
|---|
|  | 800 | Src, Dest, Trans: pChar); | 
|---|
|  | 801 | procedure EncodeInterlacedGrayscaleAlpha16(const Pass: Byte; | 
|---|
|  | 802 | Src, Dest, Trans: pChar); | 
|---|
|  | 803 | public | 
|---|
|  | 804 | {Loads the chunk from a stream} | 
|---|
|  | 805 | function LoadFromStream(Stream: TStream; const ChunkName: TChunkName; | 
|---|
|  | 806 | Size: Integer): Boolean; override; | 
|---|
|  | 807 | {Saves the chunk to a stream} | 
|---|
|  | 808 | function SaveToStream(Stream: TStream): Boolean; override; | 
|---|
|  | 809 | end; | 
|---|
|  | 810 |  | 
|---|
|  | 811 | {Image last modification chunk} | 
|---|
|  | 812 | TChunktIME = class(TChunk) | 
|---|
|  | 813 | private | 
|---|
|  | 814 | {Holds the variables} | 
|---|
|  | 815 | fYear: Word; | 
|---|
|  | 816 | fMonth, fDay, fHour, fMinute, fSecond: Byte; | 
|---|
|  | 817 | public | 
|---|
|  | 818 | {Returns/sets variables} | 
|---|
|  | 819 | property Year: Word read fYear write fYear; | 
|---|
|  | 820 | property Month: Byte read fMonth write fMonth; | 
|---|
|  | 821 | property Day: Byte read fDay write fDay; | 
|---|
|  | 822 | property Hour: Byte read fHour write fHour; | 
|---|
|  | 823 | property Minute: Byte read fMinute write fMinute; | 
|---|
|  | 824 | property Second: Byte read fSecond write fSecond; | 
|---|
|  | 825 | {Loads the chunk from a stream} | 
|---|
|  | 826 | function LoadFromStream(Stream: TStream; const ChunkName: TChunkName; | 
|---|
|  | 827 | Size: Integer): Boolean; override; | 
|---|
|  | 828 | {Saves the chunk to a stream} | 
|---|
|  | 829 | function SaveToStream(Stream: TStream): Boolean; override; | 
|---|
|  | 830 | end; | 
|---|
|  | 831 |  | 
|---|
|  | 832 | {Textual data} | 
|---|
|  | 833 | TChunktEXt = class(TChunk) | 
|---|
|  | 834 | private | 
|---|
|  | 835 | fKeyword, fText: String; | 
|---|
|  | 836 | public | 
|---|
|  | 837 | {Keyword and text} | 
|---|
|  | 838 | property Keyword: String read fKeyword write fKeyword; | 
|---|
|  | 839 | property Text: String read fText write fText; | 
|---|
|  | 840 | {Loads the chunk from a stream} | 
|---|
|  | 841 | function LoadFromStream(Stream: TStream; const ChunkName: TChunkName; | 
|---|
|  | 842 | Size: Integer): Boolean; override; | 
|---|
|  | 843 | {Saves the chunk to a stream} | 
|---|
|  | 844 | function SaveToStream(Stream: TStream): Boolean; override; | 
|---|
|  | 845 | {Assigns from another TChunk} | 
|---|
|  | 846 | procedure Assign(Source: TChunk); override; | 
|---|
|  | 847 | end; | 
|---|
|  | 848 |  | 
|---|
|  | 849 | {zTXT chunk} | 
|---|
|  | 850 | TChunkzTXt = class(TChunktEXt) | 
|---|
|  | 851 | {Loads the chunk from a stream} | 
|---|
|  | 852 | function LoadFromStream(Stream: TStream; const ChunkName: TChunkName; | 
|---|
|  | 853 | Size: Integer): Boolean; override; | 
|---|
|  | 854 | {Saves the chunk to a stream} | 
|---|
|  | 855 | function SaveToStream(Stream: TStream): Boolean; override; | 
|---|
|  | 856 | end; | 
|---|
|  | 857 |  | 
|---|
|  | 858 | {Here we test if it's c++ builder or delphi version 3 or less} | 
|---|
|  | 859 | {$IFDEF VER110}{$DEFINE DelphiBuilder3Less}{$ENDIF} | 
|---|
|  | 860 | {$IFDEF VER100}{$DEFINE DelphiBuilder3Less}{$ENDIF} | 
|---|
|  | 861 | {$IFDEF VER93}{$DEFINE DelphiBuilder3Less}{$ENDIF} | 
|---|
|  | 862 | {$IFDEF VER90}{$DEFINE DelphiBuilder3Less}{$ENDIF} | 
|---|
|  | 863 | {$IFDEF VER80}{$DEFINE DelphiBuilder3Less}{$ENDIF} | 
|---|
|  | 864 |  | 
|---|
|  | 865 |  | 
|---|
|  | 866 | {Registers a new chunk class} | 
|---|
|  | 867 | procedure RegisterChunk(ChunkClass: TChunkClass); | 
|---|
|  | 868 | {Calculates crc} | 
|---|
|  | 869 | function update_crc(crc: {$IFNDEF DelphiBuilder3Less}Cardinal{$ELSE}Integer | 
|---|
|  | 870 | {$ENDIF}; buf: pByteArray; len: Integer): Cardinal; | 
|---|
|  | 871 | {Invert bytes using assembly} | 
|---|
|  | 872 | function ByteSwap(const a: integer): integer; | 
|---|
|  | 873 |  | 
|---|
|  | 874 | implementation | 
|---|
|  | 875 |  | 
|---|
|  | 876 | var | 
|---|
|  | 877 | ChunkClasses: TPngPointerList; | 
|---|
|  | 878 | {Table of CRCs of all 8-bit messages} | 
|---|
|  | 879 | crc_table: Array[0..255] of Cardinal; | 
|---|
|  | 880 | {Flag: has the table been computed? Initially false} | 
|---|
|  | 881 | crc_table_computed: Boolean; | 
|---|
|  | 882 |  | 
|---|
|  | 883 | {Draw transparent image using transparent color} | 
|---|
|  | 884 | procedure DrawTransparentBitmap(dc: HDC; srcBits: Pointer; | 
|---|
|  | 885 | var srcHeader: TBitmapInfoHeader; | 
|---|
|  | 886 | srcBitmapInfo: pBitmapInfo; Rect: TRect; cTransparentColor: COLORREF); | 
|---|
|  | 887 | var | 
|---|
|  | 888 | cColor:   COLORREF; | 
|---|
|  | 889 | bmAndBack, bmAndObject, bmAndMem: HBITMAP; | 
|---|
|  | 890 | bmBackOld, bmObjectOld, bmMemOld: HBITMAP; | 
|---|
|  | 891 | hdcMem, hdcBack, hdcObject, hdcTemp: HDC; | 
|---|
|  | 892 | ptSize, orgSize: TPOINT; | 
|---|
|  | 893 | OldBitmap, DrawBitmap: HBITMAP; | 
|---|
|  | 894 | begin | 
|---|
|  | 895 | hdcTemp := CreateCompatibleDC(dc); | 
|---|
|  | 896 | // Select the bitmap | 
|---|
|  | 897 | DrawBitmap := CreateDIBitmap(dc, srcHeader, CBM_INIT, srcBits, srcBitmapInfo^, | 
|---|
|  | 898 | DIB_RGB_COLORS); | 
|---|
|  | 899 | OldBitmap := SelectObject(hdcTemp, DrawBitmap); | 
|---|
|  | 900 |  | 
|---|
|  | 901 | // Sizes | 
|---|
|  | 902 | OrgSize.x := abs(srcHeader.biWidth); | 
|---|
|  | 903 | OrgSize.y := abs(srcHeader.biHeight); | 
|---|
|  | 904 | ptSize.x := Rect.Right - Rect.Left;        // Get width of bitmap | 
|---|
|  | 905 | ptSize.y := Rect.Bottom - Rect.Top;        // Get height of bitmap | 
|---|
|  | 906 |  | 
|---|
|  | 907 | // Create some DCs to hold temporary data. | 
|---|
|  | 908 | hdcBack  := CreateCompatibleDC(dc); | 
|---|
|  | 909 | hdcObject := CreateCompatibleDC(dc); | 
|---|
|  | 910 | hdcMem   := CreateCompatibleDC(dc); | 
|---|
|  | 911 |  | 
|---|
|  | 912 | // Create a bitmap for each DC. DCs are required for a number of | 
|---|
|  | 913 | // GDI functions. | 
|---|
|  | 914 |  | 
|---|
|  | 915 | // Monochrome DCs | 
|---|
|  | 916 | bmAndBack  := CreateBitmap(ptSize.x, ptSize.y, 1, 1, nil); | 
|---|
|  | 917 | bmAndObject := CreateBitmap(ptSize.x, ptSize.y, 1, 1, nil); | 
|---|
|  | 918 |  | 
|---|
|  | 919 | bmAndMem   := CreateCompatibleBitmap(dc, ptSize.x, ptSize.y); | 
|---|
|  | 920 |  | 
|---|
|  | 921 | // Each DC must select a bitmap object to store pixel data. | 
|---|
|  | 922 | bmBackOld  := SelectObject(hdcBack, bmAndBack); | 
|---|
|  | 923 | bmObjectOld := SelectObject(hdcObject, bmAndObject); | 
|---|
|  | 924 | bmMemOld   := SelectObject(hdcMem, bmAndMem); | 
|---|
|  | 925 |  | 
|---|
|  | 926 | // Set the background color of the source DC to the color. | 
|---|
|  | 927 | // contained in the parts of the bitmap that should be transparent | 
|---|
|  | 928 | cColor := SetBkColor(hdcTemp, cTransparentColor); | 
|---|
|  | 929 |  | 
|---|
|  | 930 | // Create the object mask for the bitmap by performing a BitBlt | 
|---|
|  | 931 | // from the source bitmap to a monochrome bitmap. | 
|---|
|  | 932 | StretchBlt(hdcObject, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, | 
|---|
|  | 933 | orgSize.x, orgSize.y, SRCCOPY); | 
|---|
|  | 934 |  | 
|---|
|  | 935 | // Set the background color of the source DC back to the original | 
|---|
|  | 936 | // color. | 
|---|
|  | 937 | SetBkColor(hdcTemp, cColor); | 
|---|
|  | 938 |  | 
|---|
|  | 939 | // Create the inverse of the object mask. | 
|---|
|  | 940 | BitBlt(hdcBack, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, | 
|---|
|  | 941 | NOTSRCCOPY); | 
|---|
|  | 942 |  | 
|---|
|  | 943 | // Copy the background of the main DC to the destination. | 
|---|
|  | 944 | BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, dc, Rect.Left, Rect.Top, | 
|---|
|  | 945 | SRCCOPY); | 
|---|
|  | 946 |  | 
|---|
|  | 947 | // Mask out the places where the bitmap will be placed. | 
|---|
|  | 948 | BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, SRCAND); | 
|---|
|  | 949 |  | 
|---|
|  | 950 | // Mask out the transparent colored pixels on the bitmap. | 
|---|
|  | 951 | //  BitBlt(hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcBack, 0, 0, SRCAND); | 
|---|
|  | 952 | StretchBlt(hdcTemp, 0, 0, OrgSize.x, OrgSize.y, hdcBack, 0, 0, | 
|---|
|  | 953 | PtSize.x, PtSize.y, SRCAND); | 
|---|
|  | 954 |  | 
|---|
|  | 955 | // XOR the bitmap with the background on the destination DC. | 
|---|
|  | 956 | StretchBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, | 
|---|
|  | 957 | OrgSize.x, OrgSize.y, SRCPAINT); | 
|---|
|  | 958 |  | 
|---|
|  | 959 | // Copy the destination to the screen. | 
|---|
|  | 960 | BitBlt(dc, Rect.Left, Rect.Top, ptSize.x, ptSize.y, hdcMem, 0, 0, | 
|---|
|  | 961 | SRCCOPY); | 
|---|
|  | 962 |  | 
|---|
|  | 963 | // Delete the memory bitmaps. | 
|---|
|  | 964 | DeleteObject(SelectObject(hdcBack, bmBackOld)); | 
|---|
|  | 965 | DeleteObject(SelectObject(hdcObject, bmObjectOld)); | 
|---|
|  | 966 | DeleteObject(SelectObject(hdcMem, bmMemOld)); | 
|---|
|  | 967 | DeleteObject(SelectObject(hdcTemp, OldBitmap)); | 
|---|
|  | 968 |  | 
|---|
|  | 969 | // Delete the memory DCs. | 
|---|
|  | 970 | DeleteDC(hdcMem); | 
|---|
|  | 971 | DeleteDC(hdcBack); | 
|---|
|  | 972 | DeleteDC(hdcObject); | 
|---|
|  | 973 | DeleteDC(hdcTemp); | 
|---|
|  | 974 | end; | 
|---|
|  | 975 |  | 
|---|
|  | 976 | {Make the table for a fast CRC.} | 
|---|
|  | 977 | procedure make_crc_table; | 
|---|
|  | 978 | var | 
|---|
|  | 979 | c: Cardinal; | 
|---|
|  | 980 | n, k: Integer; | 
|---|
|  | 981 | begin | 
|---|
|  | 982 |  | 
|---|
|  | 983 | {fill the crc table} | 
|---|
|  | 984 | for n := 0 to 255 do | 
|---|
|  | 985 | begin | 
|---|
|  | 986 | c := Cardinal(n); | 
|---|
|  | 987 | for k := 0 to 7 do | 
|---|
|  | 988 | begin | 
|---|
|  | 989 | if Boolean(c and 1) then | 
|---|
|  | 990 | c := $edb88320 xor (c shr 1) | 
|---|
|  | 991 | else | 
|---|
|  | 992 | c := c shr 1; | 
|---|
|  | 993 | end; | 
|---|
|  | 994 | crc_table[n] := c; | 
|---|
|  | 995 | end; | 
|---|
|  | 996 |  | 
|---|
|  | 997 | {The table has already being computated} | 
|---|
|  | 998 | crc_table_computed := true; | 
|---|
|  | 999 | end; | 
|---|
|  | 1000 |  | 
|---|
|  | 1001 | {Update a running CRC with the bytes buf[0..len-1]--the CRC | 
|---|
|  | 1002 | should be initialized to all 1's, and the transmitted value | 
|---|
|  | 1003 | is the 1's complement of the final running CRC (see the | 
|---|
|  | 1004 | crc() routine below)).} | 
|---|
|  | 1005 | function update_crc(crc: {$IFNDEF DelphiBuilder3Less}Cardinal{$ELSE}Integer | 
|---|
|  | 1006 | {$ENDIF}; buf: pByteArray; len: Integer): Cardinal; | 
|---|
|  | 1007 | var | 
|---|
|  | 1008 | c: Cardinal; | 
|---|
|  | 1009 | n: Integer; | 
|---|
|  | 1010 | begin | 
|---|
|  | 1011 | c := crc; | 
|---|
|  | 1012 |  | 
|---|
|  | 1013 | {Create the crc table in case it has not being computed yet} | 
|---|
|  | 1014 | if not crc_table_computed then make_crc_table; | 
|---|
|  | 1015 |  | 
|---|
|  | 1016 | {Update} | 
|---|
|  | 1017 | for n := 0 to len - 1 do | 
|---|
|  | 1018 | c := crc_table[(c XOR buf^[n]) and $FF] XOR (c shr 8); | 
|---|
|  | 1019 |  | 
|---|
|  | 1020 | {Returns} | 
|---|
|  | 1021 | Result := c; | 
|---|
|  | 1022 | end; | 
|---|
|  | 1023 |  | 
|---|
|  | 1024 | {$IFNDEF UseDelphi} | 
|---|
|  | 1025 | function FileExists(Filename: String): Boolean; | 
|---|
|  | 1026 | var | 
|---|
|  | 1027 | FindFile: THandle; | 
|---|
|  | 1028 | FindData: TWin32FindData; | 
|---|
|  | 1029 | begin | 
|---|
|  | 1030 | FindFile := FindFirstFile(PChar(Filename), FindData); | 
|---|
|  | 1031 | Result := FindFile <> INVALID_HANDLE_VALUE; | 
|---|
|  | 1032 | if Result then Windows.FindClose(FindFile); | 
|---|
|  | 1033 | end; | 
|---|
|  | 1034 |  | 
|---|
|  | 1035 |  | 
|---|
|  | 1036 | {$ENDIF} | 
|---|
|  | 1037 |  | 
|---|
|  | 1038 | {$IFNDEF UseDelphi} | 
|---|
|  | 1039 | {Exception implementation} | 
|---|
|  | 1040 | constructor Exception.Create(Msg: String); | 
|---|
|  | 1041 | begin | 
|---|
|  | 1042 | end; | 
|---|
|  | 1043 | {$ENDIF} | 
|---|
|  | 1044 |  | 
|---|
|  | 1045 | {Calculates the paeth predictor} | 
|---|
|  | 1046 | function PaethPredictor(a, b, c: Byte): Byte; | 
|---|
|  | 1047 | var | 
|---|
|  | 1048 | pa, pb, pc: Integer; | 
|---|
|  | 1049 | begin | 
|---|
|  | 1050 | { a = left, b = above, c = upper left } | 
|---|
|  | 1051 | pa := abs(b - c);      { distances to a, b, c } | 
|---|
|  | 1052 | pb := abs(a - c); | 
|---|
|  | 1053 | pc := abs(a + b - c * 2); | 
|---|
|  | 1054 |  | 
|---|
|  | 1055 | { return nearest of a, b, c, breaking ties in order a, b, c } | 
|---|
|  | 1056 | if (pa <= pb) and (pa <= pc) then | 
|---|
|  | 1057 | Result := a | 
|---|
|  | 1058 | else | 
|---|
|  | 1059 | if pb <= pc then | 
|---|
|  | 1060 | Result := b | 
|---|
|  | 1061 | else | 
|---|
|  | 1062 | Result := c; | 
|---|
|  | 1063 | end; | 
|---|
|  | 1064 |  | 
|---|
|  | 1065 | {Invert bytes using assembly} | 
|---|
|  | 1066 | function ByteSwap(const a: integer): integer; | 
|---|
|  | 1067 | asm | 
|---|
|  | 1068 | bswap eax | 
|---|
|  | 1069 | end; | 
|---|
|  | 1070 | function ByteSwap16(inp:word): word; | 
|---|
|  | 1071 | asm | 
|---|
|  | 1072 | bswap eax | 
|---|
|  | 1073 | shr   eax, 16 | 
|---|
|  | 1074 | end; | 
|---|
|  | 1075 |  | 
|---|
|  | 1076 | {Calculates number of bytes for the number of pixels using the} | 
|---|
|  | 1077 | {color mode in the paramenter} | 
|---|
|  | 1078 | function BytesForPixels(const Pixels: Integer; const ColorType, | 
|---|
|  | 1079 | BitDepth: Byte): Integer; | 
|---|
|  | 1080 | begin | 
|---|
|  | 1081 | case ColorType of | 
|---|
|  | 1082 | {Palette and grayscale contains a single value, for palette} | 
|---|
|  | 1083 | {an value of size 2^bitdepth pointing to the palette index} | 
|---|
|  | 1084 | {and grayscale the value from 0 to 2^bitdepth with color intesity} | 
|---|
|  | 1085 | COLOR_GRAYSCALE, COLOR_PALETTE: | 
|---|
|  | 1086 | Result := (Pixels * BitDepth + 7) div 8; | 
|---|
|  | 1087 | {RGB contains 3 values R, G, B with size 2^bitdepth each} | 
|---|
|  | 1088 | COLOR_RGB: | 
|---|
|  | 1089 | Result := (Pixels * BitDepth * 3) div 8; | 
|---|
|  | 1090 | {Contains one value followed by alpha value booth size 2^bitdepth} | 
|---|
|  | 1091 | COLOR_GRAYSCALEALPHA: | 
|---|
|  | 1092 | Result := (Pixels * BitDepth * 2) div 8; | 
|---|
|  | 1093 | {Contains four values size 2^bitdepth, Red, Green, Blue and alpha} | 
|---|
|  | 1094 | COLOR_RGBALPHA: | 
|---|
|  | 1095 | Result := (Pixels * BitDepth * 4) div 8; | 
|---|
|  | 1096 | else | 
|---|
|  | 1097 | Result := 0; | 
|---|
|  | 1098 | end {case ColorType} | 
|---|
|  | 1099 | end; | 
|---|
|  | 1100 |  | 
|---|
|  | 1101 | type | 
|---|
|  | 1102 | pChunkClassInfo = ^TChunkClassInfo; | 
|---|
|  | 1103 | TChunkClassInfo = record | 
|---|
|  | 1104 | ClassName: TChunkClass; | 
|---|
|  | 1105 | end; | 
|---|
|  | 1106 |  | 
|---|
|  | 1107 | {Register a chunk type} | 
|---|
|  | 1108 | procedure RegisterChunk(ChunkClass: TChunkClass); | 
|---|
|  | 1109 | var | 
|---|
|  | 1110 | NewClass: pChunkClassInfo; | 
|---|
|  | 1111 | begin | 
|---|
|  | 1112 | {In case the list object has not being created yet} | 
|---|
|  | 1113 | if ChunkClasses = nil then ChunkClasses := TPngPointerList.Create(nil); | 
|---|
|  | 1114 |  | 
|---|
|  | 1115 | {Add this new class} | 
|---|
|  | 1116 | new(NewClass); | 
|---|
|  | 1117 | NewClass^.ClassName := ChunkClass; | 
|---|
|  | 1118 | ChunkClasses.Add(NewClass); | 
|---|
|  | 1119 | end; | 
|---|
|  | 1120 |  | 
|---|
|  | 1121 | {Free chunk class list} | 
|---|
|  | 1122 | procedure FreeChunkClassList; | 
|---|
|  | 1123 | var | 
|---|
|  | 1124 | i: Integer; | 
|---|
|  | 1125 | begin | 
|---|
|  | 1126 | if (ChunkClasses <> nil) then | 
|---|
|  | 1127 | begin | 
|---|
|  | 1128 | FOR i := 0 TO ChunkClasses.Count - 1 do | 
|---|
|  | 1129 | Dispose(pChunkClassInfo(ChunkClasses.Item[i])); | 
|---|
|  | 1130 | ChunkClasses.Free; | 
|---|
|  | 1131 | end; | 
|---|
|  | 1132 | end; | 
|---|
|  | 1133 |  | 
|---|
|  | 1134 | {Registering of common chunk classes} | 
|---|
|  | 1135 | procedure RegisterCommonChunks; | 
|---|
|  | 1136 | begin | 
|---|
|  | 1137 | {Important chunks} | 
|---|
|  | 1138 | RegisterChunk(TChunkIEND); | 
|---|
|  | 1139 | RegisterChunk(TChunkIHDR); | 
|---|
|  | 1140 | RegisterChunk(TChunkIDAT); | 
|---|
|  | 1141 | RegisterChunk(TChunkPLTE); | 
|---|
|  | 1142 | RegisterChunk(TChunkgAMA); | 
|---|
|  | 1143 | RegisterChunk(TChunktRNS); | 
|---|
|  | 1144 |  | 
|---|
|  | 1145 | {Not so important chunks} | 
|---|
|  | 1146 | RegisterChunk(TChunktIME); | 
|---|
|  | 1147 | RegisterChunk(TChunktEXt); | 
|---|
|  | 1148 | RegisterChunk(TChunkzTXt); | 
|---|
|  | 1149 | end; | 
|---|
|  | 1150 |  | 
|---|
|  | 1151 | {Creates a new chunk of this class} | 
|---|
|  | 1152 | function CreateClassChunk(Owner: TPngObject; Name: TChunkName): TChunk; | 
|---|
|  | 1153 | var | 
|---|
|  | 1154 | i       : Integer; | 
|---|
|  | 1155 | NewChunk: TChunkClass; | 
|---|
|  | 1156 | begin | 
|---|
|  | 1157 | {Looks for this chunk} | 
|---|
|  | 1158 | NewChunk := TChunk;  {In case there is no registered class for this} | 
|---|
|  | 1159 |  | 
|---|
|  | 1160 | {Looks for this class in all registered chunks} | 
|---|
|  | 1161 | if Assigned(ChunkClasses) then | 
|---|
|  | 1162 | FOR i := 0 TO ChunkClasses.Count - 1 DO | 
|---|
|  | 1163 | begin | 
|---|
|  | 1164 | if pChunkClassInfo(ChunkClasses.Item[i])^.ClassName.GetName = Name then | 
|---|
|  | 1165 | begin | 
|---|
|  | 1166 | NewChunk := pChunkClassInfo(ChunkClasses.Item[i])^.ClassName; | 
|---|
|  | 1167 | break; | 
|---|
|  | 1168 | end; | 
|---|
|  | 1169 | end; | 
|---|
|  | 1170 |  | 
|---|
|  | 1171 | {Returns chunk class} | 
|---|
|  | 1172 | Result := NewChunk.Create(Owner); | 
|---|
|  | 1173 | Result.fName := Name; | 
|---|
|  | 1174 | end; | 
|---|
|  | 1175 |  | 
|---|
|  | 1176 | {ZLIB support} | 
|---|
|  | 1177 |  | 
|---|
|  | 1178 | const | 
|---|
|  | 1179 | ZLIBAllocate = High(Word); | 
|---|
|  | 1180 |  | 
|---|
|  | 1181 | {Initializes ZLIB for decompression} | 
|---|
|  | 1182 | function ZLIBInitInflate(Stream: TStream): TZStreamRec2; | 
|---|
|  | 1183 | begin | 
|---|
|  | 1184 | {Fill record} | 
|---|
|  | 1185 | Fillchar(Result, SIZEOF(TZStreamRec2), #0); | 
|---|
|  | 1186 |  | 
|---|
|  | 1187 | {Set internal record information} | 
|---|
|  | 1188 | with Result do | 
|---|
|  | 1189 | begin | 
|---|
|  | 1190 | GetMem(Data, ZLIBAllocate); | 
|---|
|  | 1191 | fStream := Stream; | 
|---|
|  | 1192 | end; | 
|---|
|  | 1193 |  | 
|---|
|  | 1194 | {Init decompression} | 
|---|
|  | 1195 | InflateInit_(Result.zlib, zlib_version, SIZEOF(TZStreamRec)); | 
|---|
|  | 1196 | end; | 
|---|
|  | 1197 |  | 
|---|
|  | 1198 | {Initializes ZLIB for compression} | 
|---|
|  | 1199 | function ZLIBInitDeflate(Stream: TStream; | 
|---|
|  | 1200 | Level: TCompressionlevel; Size: Cardinal): TZStreamRec2; | 
|---|
|  | 1201 | begin | 
|---|
|  | 1202 | {Fill record} | 
|---|
|  | 1203 | Fillchar(Result, SIZEOF(TZStreamRec2), #0); | 
|---|
|  | 1204 |  | 
|---|
|  | 1205 | {Set internal record information} | 
|---|
|  | 1206 | with Result, ZLIB do | 
|---|
|  | 1207 | begin | 
|---|
|  | 1208 | GetMem(Data, Size); | 
|---|
|  | 1209 | fStream := Stream; | 
|---|
|  | 1210 | next_out := Data; | 
|---|
|  | 1211 | avail_out := Size; | 
|---|
|  | 1212 | end; | 
|---|
|  | 1213 |  | 
|---|
|  | 1214 | {Inits compression} | 
|---|
|  | 1215 | deflateInit_(Result.zlib, Level, zlib_version, sizeof(TZStreamRec)); | 
|---|
|  | 1216 | end; | 
|---|
|  | 1217 |  | 
|---|
|  | 1218 | {Terminates ZLIB for compression} | 
|---|
|  | 1219 | procedure ZLIBTerminateDeflate(var ZLIBStream: TZStreamRec2); | 
|---|
|  | 1220 | begin | 
|---|
|  | 1221 | {Terminates decompression} | 
|---|
|  | 1222 | DeflateEnd(ZLIBStream.zlib); | 
|---|
|  | 1223 | {Free internal record} | 
|---|
|  | 1224 | FreeMem(ZLIBStream.Data, ZLIBAllocate); | 
|---|
|  | 1225 | end; | 
|---|
|  | 1226 |  | 
|---|
|  | 1227 | {Terminates ZLIB for decompression} | 
|---|
|  | 1228 | procedure ZLIBTerminateInflate(var ZLIBStream: TZStreamRec2); | 
|---|
|  | 1229 | begin | 
|---|
|  | 1230 | {Terminates decompression} | 
|---|
|  | 1231 | InflateEnd(ZLIBStream.zlib); | 
|---|
|  | 1232 | {Free internal record} | 
|---|
|  | 1233 | FreeMem(ZLIBStream.Data, ZLIBAllocate); | 
|---|
|  | 1234 | end; | 
|---|
|  | 1235 |  | 
|---|
|  | 1236 | {Decompresses ZLIB into a memory address} | 
|---|
|  | 1237 | function DecompressZLIB(const Input: Pointer; InputSize: Integer; | 
|---|
|  | 1238 | var Output: Pointer; var OutputSize: Integer; | 
|---|
|  | 1239 | var ErrorOutput: String): Boolean; | 
|---|
|  | 1240 | var | 
|---|
|  | 1241 | StreamRec : TZStreamRec; | 
|---|
|  | 1242 | Buffer    : Array[Byte] of Byte; | 
|---|
|  | 1243 | InflateRet: Integer; | 
|---|
|  | 1244 | begin | 
|---|
|  | 1245 | with StreamRec do | 
|---|
|  | 1246 | begin | 
|---|
|  | 1247 | {Initializes} | 
|---|
|  | 1248 | Result := True; | 
|---|
|  | 1249 | OutputSize := 0; | 
|---|
|  | 1250 |  | 
|---|
|  | 1251 | {Prepares the data to decompress} | 
|---|
|  | 1252 | FillChar(StreamRec, SizeOf(TZStreamRec), #0); | 
|---|
|  | 1253 | InflateInit_(StreamRec, zlib_version, SIZEOF(TZStreamRec)); | 
|---|
|  | 1254 | next_in := Input; | 
|---|
|  | 1255 | avail_in := InputSize; | 
|---|
|  | 1256 |  | 
|---|
|  | 1257 | {Decodes data} | 
|---|
|  | 1258 | repeat | 
|---|
|  | 1259 | {In case it needs an output buffer} | 
|---|
|  | 1260 | if (avail_out = 0) then | 
|---|
|  | 1261 | begin | 
|---|
|  | 1262 | next_out := @Buffer; | 
|---|
|  | 1263 | avail_out := SizeOf(Buffer); | 
|---|
|  | 1264 | end {if (avail_out = 0)}; | 
|---|
|  | 1265 |  | 
|---|
|  | 1266 | {Decompress and put in output} | 
|---|
|  | 1267 | InflateRet := inflate(StreamRec, 0); | 
|---|
|  | 1268 | if (InflateRet = Z_STREAM_END) or (InflateRet = 0) then | 
|---|
|  | 1269 | begin | 
|---|
|  | 1270 | {Reallocates output buffer} | 
|---|
|  | 1271 | inc(OutputSize, total_out); | 
|---|
|  | 1272 | if Output = nil then | 
|---|
|  | 1273 | GetMem(Output, OutputSize) else ReallocMem(Output, OutputSize); | 
|---|
|  | 1274 | {Copies the new data} | 
|---|
|  | 1275 | CopyMemory(Ptr(Longint(Output) + OutputSize - total_out), | 
|---|
|  | 1276 | @Buffer, total_out); | 
|---|
|  | 1277 | end {if (InflateRet = Z_STREAM_END) or (InflateRet = 0)} | 
|---|
|  | 1278 | {Now tests for errors} | 
|---|
|  | 1279 | else if InflateRet < 0 then | 
|---|
|  | 1280 | begin | 
|---|
|  | 1281 | Result := False; | 
|---|
|  | 1282 | ErrorOutput := StreamRec.msg; | 
|---|
|  | 1283 | InflateEnd(StreamRec); | 
|---|
|  | 1284 | Exit; | 
|---|
|  | 1285 | end {if InflateRet < 0} | 
|---|
|  | 1286 | until InflateRet = Z_STREAM_END; | 
|---|
|  | 1287 |  | 
|---|
|  | 1288 | {Terminates decompression} | 
|---|
|  | 1289 | InflateEnd(StreamRec); | 
|---|
|  | 1290 | end {with StreamRec} | 
|---|
|  | 1291 |  | 
|---|
|  | 1292 | end; | 
|---|
|  | 1293 |  | 
|---|
|  | 1294 | {Compresses ZLIB into a memory address} | 
|---|
|  | 1295 | function CompressZLIB(Input: Pointer; InputSize, CompressionLevel: Integer; | 
|---|
|  | 1296 | var Output: Pointer; var OutputSize: Integer; | 
|---|
|  | 1297 | var ErrorOutput: String): Boolean; | 
|---|
|  | 1298 | var | 
|---|
|  | 1299 | StreamRec : TZStreamRec; | 
|---|
|  | 1300 | Buffer    : Array[Byte] of Byte; | 
|---|
|  | 1301 | DeflateRet: Integer; | 
|---|
|  | 1302 | begin | 
|---|
|  | 1303 | with StreamRec do | 
|---|
|  | 1304 | begin | 
|---|
|  | 1305 | Result := True; {By default returns TRUE as everything might have gone ok} | 
|---|
|  | 1306 | OutputSize := 0; {Initialize} | 
|---|
|  | 1307 | {Prepares the data to compress} | 
|---|
|  | 1308 | FillChar(StreamRec, SizeOf(TZStreamRec), #0); | 
|---|
|  | 1309 | DeflateInit_(StreamRec, CompressionLevel,zlib_version, SIZEOF(TZStreamRec)); | 
|---|
|  | 1310 |  | 
|---|
|  | 1311 | next_in := Input; | 
|---|
|  | 1312 | avail_in := InputSize; | 
|---|
|  | 1313 |  | 
|---|
|  | 1314 | while avail_in > 0 do | 
|---|
|  | 1315 | begin | 
|---|
|  | 1316 | {When it needs new buffer to stores the compressed data} | 
|---|
|  | 1317 | if avail_out = 0 then | 
|---|
|  | 1318 | begin | 
|---|
|  | 1319 | {Restore buffer} | 
|---|
|  | 1320 | next_out := @Buffer; | 
|---|
|  | 1321 | avail_out := SizeOf(Buffer); | 
|---|
|  | 1322 | end {if avail_out = 0}; | 
|---|
|  | 1323 |  | 
|---|
|  | 1324 | {Compresses} | 
|---|
|  | 1325 | DeflateRet := deflate(StreamRec, Z_FINISH); | 
|---|
|  | 1326 |  | 
|---|
|  | 1327 | if (DeflateRet = Z_STREAM_END) or (DeflateRet = 0) then | 
|---|
|  | 1328 | begin | 
|---|
|  | 1329 | {Updates the output memory} | 
|---|
|  | 1330 | inc(OutputSize, total_out); | 
|---|
|  | 1331 | if Output = nil then | 
|---|
|  | 1332 | GetMem(Output, OutputSize) else ReallocMem(Output, OutputSize); | 
|---|
|  | 1333 |  | 
|---|
|  | 1334 | {Copies the new data} | 
|---|
|  | 1335 | CopyMemory(Ptr(Longint(Output) + OutputSize - total_out), | 
|---|
|  | 1336 | @Buffer, total_out); | 
|---|
|  | 1337 | end {if (InflateRet = Z_STREAM_END) or (InflateRet = 0)} | 
|---|
|  | 1338 | {Now tests for errors} | 
|---|
|  | 1339 | else if DeflateRet < 0 then | 
|---|
|  | 1340 | begin | 
|---|
|  | 1341 | Result := False; | 
|---|
|  | 1342 | ErrorOutput := StreamRec.msg; | 
|---|
|  | 1343 | DeflateEnd(StreamRec); | 
|---|
|  | 1344 | Exit; | 
|---|
|  | 1345 | end {if InflateRet < 0} | 
|---|
|  | 1346 |  | 
|---|
|  | 1347 | end {while avail_in > 0}; | 
|---|
|  | 1348 |  | 
|---|
|  | 1349 | {Finishes compressing} | 
|---|
|  | 1350 | DeflateEnd(StreamRec); | 
|---|
|  | 1351 | end {with StreamRec} | 
|---|
|  | 1352 |  | 
|---|
|  | 1353 | end; | 
|---|
|  | 1354 |  | 
|---|
|  | 1355 | {TPngPointerList implementation} | 
|---|
|  | 1356 |  | 
|---|
|  | 1357 | {Object being created} | 
|---|
|  | 1358 | constructor TPngPointerList.Create(AOwner: TPNGObject); | 
|---|
|  | 1359 | begin | 
|---|
|  | 1360 | inherited Create; {Let ancestor work} | 
|---|
|  | 1361 | {Holds owner} | 
|---|
|  | 1362 | fOwner := AOwner; | 
|---|
|  | 1363 | {Memory pointer not being used yet} | 
|---|
|  | 1364 | fMemory := nil; | 
|---|
|  | 1365 | {No items yet} | 
|---|
|  | 1366 | fCount := 0; | 
|---|
|  | 1367 | end; | 
|---|
|  | 1368 |  | 
|---|
|  | 1369 | {Removes value from the list} | 
|---|
|  | 1370 | function TPngPointerList.Remove(Value: Pointer): Pointer; | 
|---|
|  | 1371 | var | 
|---|
|  | 1372 | I, Position: Integer; | 
|---|
|  | 1373 | begin | 
|---|
|  | 1374 | {Gets item position} | 
|---|
|  | 1375 | Position := -1; | 
|---|
|  | 1376 | FOR I := 0 TO Count - 1 DO | 
|---|
|  | 1377 | if Value = Item[I] then Position := I; | 
|---|
|  | 1378 | {In case a match was found} | 
|---|
|  | 1379 | if Position >= 0 then | 
|---|
|  | 1380 | begin | 
|---|
|  | 1381 | Result := Item[Position]; {Returns pointer} | 
|---|
|  | 1382 | {Remove item and move memory} | 
|---|
|  | 1383 | Dec(fCount); | 
|---|
|  | 1384 | if Position < Integer(FCount) then | 
|---|
|  | 1385 | System.Move(fMemory^[Position + 1], fMemory^[Position], | 
|---|
|  | 1386 | (Integer(fCount) - Position) * SizeOf(Pointer)); | 
|---|
|  | 1387 | end {if Position >= 0} else Result := nil | 
|---|
|  | 1388 | end; | 
|---|
|  | 1389 |  | 
|---|
|  | 1390 | {Add a new value in the list} | 
|---|
|  | 1391 | procedure TPngPointerList.Add(Value: Pointer); | 
|---|
|  | 1392 | begin | 
|---|
|  | 1393 | Count := Count + 1; | 
|---|
|  | 1394 | Item[Count - 1] := Value; | 
|---|
|  | 1395 | end; | 
|---|
|  | 1396 |  | 
|---|
|  | 1397 |  | 
|---|
|  | 1398 | {Object being destroyed} | 
|---|
|  | 1399 | destructor TPngPointerList.Destroy; | 
|---|
|  | 1400 | begin | 
|---|
|  | 1401 | {Release memory if needed} | 
|---|
|  | 1402 | if fMemory <> nil then | 
|---|
|  | 1403 | FreeMem(fMemory, fCount * sizeof(Pointer)); | 
|---|
|  | 1404 |  | 
|---|
|  | 1405 | {Free things} | 
|---|
|  | 1406 | inherited Destroy; | 
|---|
|  | 1407 | end; | 
|---|
|  | 1408 |  | 
|---|
|  | 1409 | {Returns one item from the list} | 
|---|
|  | 1410 | function TPngPointerList.GetItem(Index: Cardinal): Pointer; | 
|---|
|  | 1411 | begin | 
|---|
|  | 1412 | if (Index <= Count - 1) then | 
|---|
|  | 1413 | Result := fMemory[Index] | 
|---|
|  | 1414 | else | 
|---|
|  | 1415 | {In case it's out of bounds} | 
|---|
|  | 1416 | Result := nil; | 
|---|
|  | 1417 | end; | 
|---|
|  | 1418 |  | 
|---|
|  | 1419 | {Inserts a new item in the list} | 
|---|
|  | 1420 | procedure TPngPointerList.Insert(Value: Pointer; Position: Cardinal); | 
|---|
|  | 1421 | begin | 
|---|
|  | 1422 | if (Position < Count) then | 
|---|
|  | 1423 | begin | 
|---|
|  | 1424 | {Increase item count} | 
|---|
|  | 1425 | SetSize(Count + 1); | 
|---|
|  | 1426 | {Move other pointers} | 
|---|
|  | 1427 | if Position < Count then | 
|---|
|  | 1428 | System.Move(fMemory^[Position], fMemory^[Position + 1], | 
|---|
|  | 1429 | (Count - Position - 1) * SizeOf(Pointer)); | 
|---|
|  | 1430 | {Sets item} | 
|---|
|  | 1431 | Item[Position] := Value; | 
|---|
|  | 1432 | end; | 
|---|
|  | 1433 | end; | 
|---|
|  | 1434 |  | 
|---|
|  | 1435 | {Sets one item from the list} | 
|---|
|  | 1436 | procedure TPngPointerList.SetItem(Index: Cardinal; const Value: Pointer); | 
|---|
|  | 1437 | begin | 
|---|
|  | 1438 | {If index is in bounds, set value} | 
|---|
|  | 1439 | if (Index <= Count - 1) then | 
|---|
|  | 1440 | fMemory[Index] := Value | 
|---|
|  | 1441 | end; | 
|---|
|  | 1442 |  | 
|---|
|  | 1443 | {This method resizes the list} | 
|---|
|  | 1444 | procedure TPngPointerList.SetSize(const Size: Cardinal); | 
|---|
|  | 1445 | begin | 
|---|
|  | 1446 | {Sets the size} | 
|---|
|  | 1447 | if (fMemory = nil) and (Size > 0) then | 
|---|
|  | 1448 | GetMem(fMemory, Size * SIZEOF(Pointer)) | 
|---|
|  | 1449 | else | 
|---|
|  | 1450 | if Size > 0 then  {Only realloc if the new size is greater than 0} | 
|---|
|  | 1451 | ReallocMem(fMemory, Size * SIZEOF(Pointer)) | 
|---|
|  | 1452 | else | 
|---|
|  | 1453 | {In case user is resize to 0 items} | 
|---|
|  | 1454 | begin | 
|---|
|  | 1455 | FreeMem(fMemory); | 
|---|
|  | 1456 | fMemory := nil; | 
|---|
|  | 1457 | end; | 
|---|
|  | 1458 | {Update count} | 
|---|
|  | 1459 | fCount := Size; | 
|---|
|  | 1460 | end; | 
|---|
|  | 1461 |  | 
|---|
|  | 1462 | {TPNGList implementation} | 
|---|
|  | 1463 |  | 
|---|
|  | 1464 | {Removes an item} | 
|---|
|  | 1465 | procedure TPNGList.RemoveChunk(Chunk: TChunk); | 
|---|
|  | 1466 | begin | 
|---|
|  | 1467 | Remove(Chunk); | 
|---|
|  | 1468 | Chunk.Free | 
|---|
|  | 1469 | end; | 
|---|
|  | 1470 |  | 
|---|
|  | 1471 | {Add a new item} | 
|---|
|  | 1472 | function TPNGList.Add(ChunkClass: TChunkClass): TChunk; | 
|---|
|  | 1473 | var | 
|---|
|  | 1474 | IHDR: TChunkIHDR; | 
|---|
|  | 1475 | IEND: TChunkIEND; | 
|---|
|  | 1476 |  | 
|---|
|  | 1477 | IDAT: TChunkIDAT; | 
|---|
|  | 1478 | PLTE: TChunkPLTE; | 
|---|
|  | 1479 | begin | 
|---|
|  | 1480 | Result := nil; {Default result} | 
|---|
|  | 1481 | {Adding these is not allowed} | 
|---|
|  | 1482 | if (ChunkClass = TChunkIHDR) or (ChunkClass = TChunkIDAT) or | 
|---|
|  | 1483 | (ChunkClass = TChunkPLTE) or (ChunkClass = TChunkIEND) then | 
|---|
|  | 1484 | fOwner.RaiseError(EPngError, EPNGCannotAddChunkText) | 
|---|
|  | 1485 | {Two of these is not allowed} | 
|---|
|  | 1486 | else if ((ChunkClass = TChunkgAMA) and (ItemFromClass(TChunkgAMA) <> nil)) or | 
|---|
|  | 1487 | ((ChunkClass = TChunktRNS) and (ItemFromClass(TChunktRNS) <> nil)) then | 
|---|
|  | 1488 | fOwner.RaiseError(EPngError, EPNGCannotAddChunkText) | 
|---|
|  | 1489 | {There must have an IEND and IHDR chunk} | 
|---|
|  | 1490 | else if (ItemFromClass(TChunkIEND) = nil) or | 
|---|
|  | 1491 | (ItemFromClass(TChunkIHDR) = nil) then | 
|---|
|  | 1492 | fOwner.RaiseError(EPngError, EPNGCannotAddInvalidImageText) | 
|---|
|  | 1493 | else | 
|---|
|  | 1494 | begin | 
|---|
|  | 1495 | {Get common chunks} | 
|---|
|  | 1496 | IHDR := ItemFromClass(TChunkIHDR) as TChunkIHDR; | 
|---|
|  | 1497 | IEND := ItemFromClass(TChunkIEND) as TChunkIEND; | 
|---|
|  | 1498 | {Create new chunk} | 
|---|
|  | 1499 | Result := ChunkClass.Create(Owner); | 
|---|
|  | 1500 | {Add to the list} | 
|---|
|  | 1501 | if (ChunkClass = TChunkgAMA) then | 
|---|
|  | 1502 | Insert(Result, IHDR.Index + 1) | 
|---|
|  | 1503 | {Transparency chunk (fix by Ian Boyd)} | 
|---|
|  | 1504 | else if (ChunkClass = TChunktRNS) then | 
|---|
|  | 1505 | begin | 
|---|
|  | 1506 | {Transparecy chunk must be after PLTE; before IDAT} | 
|---|
|  | 1507 | IDAT := ItemFromClass(TChunkIDAT) as TChunkIDAT; | 
|---|
|  | 1508 | PLTE := ItemFromClass(TChunkPLTE) as TChunkPLTE; | 
|---|
|  | 1509 |  | 
|---|
|  | 1510 | if Assigned(PLTE) then | 
|---|
|  | 1511 | Insert(Result, PLTE.Index + 1) | 
|---|
|  | 1512 | else if Assigned(IDAT) then | 
|---|
|  | 1513 | Insert(Result, IDAT.Index) | 
|---|
|  | 1514 | else | 
|---|
|  | 1515 | Insert(Result, IHDR.Index + 1) | 
|---|
|  | 1516 | end | 
|---|
|  | 1517 | else {All other chunks} | 
|---|
|  | 1518 | Insert(Result, IEND.Index); | 
|---|
|  | 1519 | end {if} | 
|---|
|  | 1520 | end; | 
|---|
|  | 1521 |  | 
|---|
|  | 1522 | {Returns item from the list} | 
|---|
|  | 1523 | function TPNGList.GetItem(Index: Cardinal): TChunk; | 
|---|
|  | 1524 | begin | 
|---|
|  | 1525 | Result := inherited GetItem(Index); | 
|---|
|  | 1526 | end; | 
|---|
|  | 1527 |  | 
|---|
|  | 1528 | {Returns first item from the list using the class from parameter} | 
|---|
|  | 1529 | function TPNGList.ItemFromClass(ChunkClass: TChunkClass): TChunk; | 
|---|
|  | 1530 | var | 
|---|
|  | 1531 | i: Integer; | 
|---|
|  | 1532 | begin | 
|---|
|  | 1533 | Result := nil; {Initial result} | 
|---|
|  | 1534 | FOR i := 0 TO Count - 1 DO | 
|---|
|  | 1535 | {Test if this item has the same class} | 
|---|
|  | 1536 | if Item[i] is ChunkClass then | 
|---|
|  | 1537 | begin | 
|---|
|  | 1538 | {Returns this item and exit} | 
|---|
|  | 1539 | Result := Item[i]; | 
|---|
|  | 1540 | break; | 
|---|
|  | 1541 | end {if} | 
|---|
|  | 1542 | end; | 
|---|
|  | 1543 |  | 
|---|
|  | 1544 | {$IFNDEF UseDelphi} | 
|---|
|  | 1545 |  | 
|---|
|  | 1546 | {TStream implementation} | 
|---|
|  | 1547 |  | 
|---|
|  | 1548 | {Copies all from another stream} | 
|---|
|  | 1549 | function TStream.CopyFrom(Source: TStream; Count: Cardinal): Cardinal; | 
|---|
|  | 1550 | const | 
|---|
|  | 1551 | MaxBytes = $f000; | 
|---|
|  | 1552 | var | 
|---|
|  | 1553 | Buffer:  PChar; | 
|---|
|  | 1554 | BufSize, N: Cardinal; | 
|---|
|  | 1555 | begin | 
|---|
|  | 1556 | {If count is zero, copy everything from Source} | 
|---|
|  | 1557 | if Count = 0 then | 
|---|
|  | 1558 | begin | 
|---|
|  | 1559 | Source.Seek(0, soFromBeginning); | 
|---|
|  | 1560 | Count := Source.Size; | 
|---|
|  | 1561 | end; | 
|---|
|  | 1562 |  | 
|---|
|  | 1563 | Result := Count; {Returns the number of bytes readed} | 
|---|
|  | 1564 | {Allocates memory} | 
|---|
|  | 1565 | if Count > MaxBytes then BufSize := MaxBytes else BufSize := Count; | 
|---|
|  | 1566 | GetMem(Buffer, BufSize); | 
|---|
|  | 1567 |  | 
|---|
|  | 1568 | {Copy memory} | 
|---|
|  | 1569 | while Count > 0 do | 
|---|
|  | 1570 | begin | 
|---|
|  | 1571 | if Count > BufSize then N := BufSize else N := Count; | 
|---|
|  | 1572 | Source.Read(Buffer^, N); | 
|---|
|  | 1573 | Write(Buffer^, N); | 
|---|
|  | 1574 | dec(Count, N); | 
|---|
|  | 1575 | end; | 
|---|
|  | 1576 |  | 
|---|
|  | 1577 | {Deallocates memory} | 
|---|
|  | 1578 | FreeMem(Buffer, BufSize); | 
|---|
|  | 1579 | end; | 
|---|
|  | 1580 |  | 
|---|
|  | 1581 | {Set current stream position} | 
|---|
|  | 1582 | procedure TStream.SetPosition(const Value: Longint); | 
|---|
|  | 1583 | begin | 
|---|
|  | 1584 | Seek(Value, soFromBeginning); | 
|---|
|  | 1585 | end; | 
|---|
|  | 1586 |  | 
|---|
|  | 1587 | {Returns position} | 
|---|
|  | 1588 | function TStream.GetPosition: Longint; | 
|---|
|  | 1589 | begin | 
|---|
|  | 1590 | Result := Seek(0, soFromCurrent); | 
|---|
|  | 1591 | end; | 
|---|
|  | 1592 |  | 
|---|
|  | 1593 | {Returns stream size} | 
|---|
|  | 1594 | function TStream.GetSize: Longint; | 
|---|
|  | 1595 | var | 
|---|
|  | 1596 | Pos: Cardinal; | 
|---|
|  | 1597 | begin | 
|---|
|  | 1598 | Pos := Seek(0, soFromCurrent); | 
|---|
|  | 1599 | Result := Seek(0, soFromEnd); | 
|---|
|  | 1600 | Seek(Pos, soFromCurrent); | 
|---|
|  | 1601 | end; | 
|---|
|  | 1602 |  | 
|---|
|  | 1603 | {TFileStream implementation} | 
|---|
|  | 1604 |  | 
|---|
|  | 1605 | {Filestream object being created} | 
|---|
|  | 1606 | constructor TFileStream.Create(Filename: String; Mode: TFileStreamModeSet); | 
|---|
|  | 1607 | {Makes file mode} | 
|---|
|  | 1608 | function OpenMode: DWORD; | 
|---|
|  | 1609 | begin | 
|---|
|  | 1610 | Result := 0; | 
|---|
|  | 1611 | if fsmRead in Mode then Result := GENERIC_READ; | 
|---|
|  | 1612 | if (fsmWrite in Mode) or (fsmCreate in Mode) then | 
|---|
|  | 1613 | Result := Result OR GENERIC_WRITE; | 
|---|
|  | 1614 | end; | 
|---|
|  | 1615 | const | 
|---|
|  | 1616 | IsCreate: Array[Boolean] of Integer = (OPEN_ALWAYS, CREATE_ALWAYS); | 
|---|
|  | 1617 | begin | 
|---|
|  | 1618 | {Call ancestor} | 
|---|
|  | 1619 | inherited Create; | 
|---|
|  | 1620 |  | 
|---|
|  | 1621 | {Create handle} | 
|---|
|  | 1622 | fHandle := CreateFile(PChar(Filename), OpenMode, FILE_SHARE_READ or | 
|---|
|  | 1623 | FILE_SHARE_WRITE, nil, IsCreate[fsmCreate in Mode], 0, 0); | 
|---|
|  | 1624 | {Store mode} | 
|---|
|  | 1625 | FileMode := Mode; | 
|---|
|  | 1626 | end; | 
|---|
|  | 1627 |  | 
|---|
|  | 1628 | {Filestream object being destroyed} | 
|---|
|  | 1629 | destructor TFileStream.Destroy; | 
|---|
|  | 1630 | begin | 
|---|
|  | 1631 | {Terminates file and close} | 
|---|
|  | 1632 | if FileMode = [fsmWrite] then | 
|---|
|  | 1633 | SetEndOfFile(fHandle); | 
|---|
|  | 1634 | CloseHandle(fHandle); | 
|---|
|  | 1635 |  | 
|---|
|  | 1636 | {Call ancestor} | 
|---|
|  | 1637 | inherited Destroy; | 
|---|
|  | 1638 | end; | 
|---|
|  | 1639 |  | 
|---|
|  | 1640 | {Writes data to the file} | 
|---|
|  | 1641 | function TFileStream.Write(const Buffer; Count: Longint): Cardinal; | 
|---|
|  | 1642 | begin | 
|---|
|  | 1643 | if not WriteFile(fHandle, Buffer, Count, Result, nil) then | 
|---|
|  | 1644 | Result := 0; | 
|---|
|  | 1645 | end; | 
|---|
|  | 1646 |  | 
|---|
|  | 1647 | {Reads data from the file} | 
|---|
|  | 1648 | function TFileStream.Read(var Buffer; Count: Longint): Cardinal; | 
|---|
|  | 1649 | begin | 
|---|
|  | 1650 | if not ReadFile(fHandle, Buffer, Count, Result, nil) then | 
|---|
|  | 1651 | Result := 0; | 
|---|
|  | 1652 | end; | 
|---|
|  | 1653 |  | 
|---|
|  | 1654 | {Seeks the file position} | 
|---|
|  | 1655 | function TFileStream.Seek(Offset: Integer; Origin: Word): Longint; | 
|---|
|  | 1656 | begin | 
|---|
|  | 1657 | Result := SetFilePointer(fHandle, Offset, nil, Origin); | 
|---|
|  | 1658 | end; | 
|---|
|  | 1659 |  | 
|---|
|  | 1660 | {Sets the size of the file} | 
|---|
|  | 1661 | procedure TFileStream.SetSize(const Value: Longint); | 
|---|
|  | 1662 | begin | 
|---|
|  | 1663 | Seek(Value, soFromBeginning); | 
|---|
|  | 1664 | SetEndOfFile(fHandle); | 
|---|
|  | 1665 | end; | 
|---|
|  | 1666 |  | 
|---|
|  | 1667 | {TResourceStream implementation} | 
|---|
|  | 1668 |  | 
|---|
|  | 1669 | {Creates the resource stream} | 
|---|
|  | 1670 | constructor TResourceStream.Create(Instance: HInst; const ResName: String; | 
|---|
|  | 1671 | ResType: PChar); | 
|---|
|  | 1672 | var | 
|---|
|  | 1673 | ResID: HRSRC; | 
|---|
|  | 1674 | ResGlobal: HGlobal; | 
|---|
|  | 1675 | begin | 
|---|
|  | 1676 | {Obtains the resource ID} | 
|---|
|  | 1677 | ResID := FindResource(hInstance, PChar(ResName), RT_RCDATA); | 
|---|
|  | 1678 | if ResID = 0 then raise EPNGError.Create(''); | 
|---|
|  | 1679 | {Obtains memory and size} | 
|---|
|  | 1680 | ResGlobal := LoadResource(hInstance, ResID); | 
|---|
|  | 1681 | Size := SizeOfResource(hInstance, ResID); | 
|---|
|  | 1682 | Memory := LockResource(ResGlobal); | 
|---|
|  | 1683 | if (ResGlobal = 0) or (Memory = nil) then EPNGError.Create(''); | 
|---|
|  | 1684 | end; | 
|---|
|  | 1685 |  | 
|---|
|  | 1686 |  | 
|---|
|  | 1687 | {Setting resource stream size is not supported} | 
|---|
|  | 1688 | procedure TResourceStream.SetSize(const Value: Integer); | 
|---|
|  | 1689 | begin | 
|---|
|  | 1690 | end; | 
|---|
|  | 1691 |  | 
|---|
|  | 1692 | {Writing into a resource stream is not supported} | 
|---|
|  | 1693 | function TResourceStream.Write(const Buffer; Count: Integer): Cardinal; | 
|---|
|  | 1694 | begin | 
|---|
|  | 1695 | Result := 0; | 
|---|
|  | 1696 | end; | 
|---|
|  | 1697 |  | 
|---|
|  | 1698 | {Reads data from the stream} | 
|---|
|  | 1699 | function TResourceStream.Read(var Buffer; Count: Integer): Cardinal; | 
|---|
|  | 1700 | begin | 
|---|
|  | 1701 | //Returns data | 
|---|
|  | 1702 | CopyMemory(@Buffer, Ptr(Longint(Memory) + Position), Count); | 
|---|
|  | 1703 | //Update position | 
|---|
|  | 1704 | inc(Position, Count); | 
|---|
|  | 1705 | //Returns | 
|---|
|  | 1706 | Result := Count; | 
|---|
|  | 1707 | end; | 
|---|
|  | 1708 |  | 
|---|
|  | 1709 | {Seeks data} | 
|---|
|  | 1710 | function TResourceStream.Seek(Offset: Integer; Origin: Word): Longint; | 
|---|
|  | 1711 | begin | 
|---|
|  | 1712 | {Move depending on the origin} | 
|---|
|  | 1713 | case Origin of | 
|---|
|  | 1714 | soFromBeginning: Position := Offset; | 
|---|
|  | 1715 | soFromCurrent: inc(Position, Offset); | 
|---|
|  | 1716 | soFromEnd: Position := Size + Offset; | 
|---|
|  | 1717 | end; | 
|---|
|  | 1718 |  | 
|---|
|  | 1719 | {Returns the current position} | 
|---|
|  | 1720 | Result := Position; | 
|---|
|  | 1721 | end; | 
|---|
|  | 1722 |  | 
|---|
|  | 1723 | {$ENDIF} | 
|---|
|  | 1724 |  | 
|---|
|  | 1725 | {TChunk implementation} | 
|---|
|  | 1726 |  | 
|---|
|  | 1727 | {Resizes the data} | 
|---|
|  | 1728 | procedure TChunk.ResizeData(const NewSize: Cardinal); | 
|---|
|  | 1729 | begin | 
|---|
|  | 1730 | fDataSize := NewSize; | 
|---|
|  | 1731 | ReallocMem(fData, NewSize + 1); | 
|---|
|  | 1732 | end; | 
|---|
|  | 1733 |  | 
|---|
|  | 1734 | {Returns index from list} | 
|---|
|  | 1735 | function TChunk.GetIndex: Integer; | 
|---|
|  | 1736 | var | 
|---|
|  | 1737 | i: Integer; | 
|---|
|  | 1738 | begin | 
|---|
|  | 1739 | Result := -1; {Avoiding warnings} | 
|---|
|  | 1740 | {Searches in the list} | 
|---|
|  | 1741 | FOR i := 0 TO Owner.Chunks.Count - 1 DO | 
|---|
|  | 1742 | if Owner.Chunks.Item[i] = Self then | 
|---|
|  | 1743 | begin | 
|---|
|  | 1744 | {Found match} | 
|---|
|  | 1745 | Result := i; | 
|---|
|  | 1746 | exit; | 
|---|
|  | 1747 | end {for i} | 
|---|
|  | 1748 | end; | 
|---|
|  | 1749 |  | 
|---|
|  | 1750 | {Returns pointer to the TChunkIHDR} | 
|---|
|  | 1751 | function TChunk.GetHeader: TChunkIHDR; | 
|---|
|  | 1752 | begin | 
|---|
|  | 1753 | Result := Owner.Chunks.Item[0] as TChunkIHDR; | 
|---|
|  | 1754 | end; | 
|---|
|  | 1755 |  | 
|---|
|  | 1756 | {Assigns from another TChunk} | 
|---|
|  | 1757 | procedure TChunk.Assign(Source: TChunk); | 
|---|
|  | 1758 | begin | 
|---|
|  | 1759 | {Copy properties} | 
|---|
|  | 1760 | fName := Source.fName; | 
|---|
|  | 1761 | {Set data size and realloc} | 
|---|
|  | 1762 | ResizeData(Source.fDataSize); | 
|---|
|  | 1763 |  | 
|---|
|  | 1764 | {Copy data (if there's any)} | 
|---|
|  | 1765 | if fDataSize > 0 then CopyMemory(fData, Source.fData, fDataSize); | 
|---|
|  | 1766 | end; | 
|---|
|  | 1767 |  | 
|---|
|  | 1768 | {Chunk being created} | 
|---|
|  | 1769 | constructor TChunk.Create(Owner: TPngObject); | 
|---|
|  | 1770 | var | 
|---|
|  | 1771 | ChunkName: String; | 
|---|
|  | 1772 | begin | 
|---|
|  | 1773 | {Ancestor create} | 
|---|
|  | 1774 | inherited Create; | 
|---|
|  | 1775 |  | 
|---|
|  | 1776 | {If it's a registered class, set the chunk name based on the class} | 
|---|
|  | 1777 | {name. For instance, if the class name is TChunkgAMA, the GAMA part} | 
|---|
|  | 1778 | {will become the chunk name} | 
|---|
|  | 1779 | ChunkName := Copy(ClassName, Length('TChunk') + 1, Length(ClassName)); | 
|---|
|  | 1780 | if Length(ChunkName) = 4 then CopyMemory(@fName[0], @ChunkName[1], 4); | 
|---|
|  | 1781 |  | 
|---|
|  | 1782 | {Initialize data holder} | 
|---|
|  | 1783 | GetMem(fData, 1); | 
|---|
|  | 1784 | fDataSize := 0; | 
|---|
|  | 1785 | {Record owner} | 
|---|
|  | 1786 | fOwner := Owner; | 
|---|
|  | 1787 | end; | 
|---|
|  | 1788 |  | 
|---|
|  | 1789 | {Chunk being destroyed} | 
|---|
|  | 1790 | destructor TChunk.Destroy; | 
|---|
|  | 1791 | begin | 
|---|
|  | 1792 | {Free data holder} | 
|---|
|  | 1793 | FreeMem(fData, fDataSize + 1); | 
|---|
|  | 1794 | {Let ancestor destroy} | 
|---|
|  | 1795 | inherited Destroy; | 
|---|
|  | 1796 | end; | 
|---|
|  | 1797 |  | 
|---|
|  | 1798 | {Returns the chunk name 1} | 
|---|
|  | 1799 | function TChunk.GetChunkName: String; | 
|---|
|  | 1800 | begin | 
|---|
|  | 1801 | Result := fName | 
|---|
|  | 1802 | end; | 
|---|
|  | 1803 |  | 
|---|
|  | 1804 | {Returns the chunk name 2} | 
|---|
|  | 1805 | class function TChunk.GetName: String; | 
|---|
|  | 1806 | begin | 
|---|
|  | 1807 | {For avoid writing GetName for each TChunk descendent, by default for} | 
|---|
|  | 1808 | {classes which don't declare GetName, it will look for the class name} | 
|---|
|  | 1809 | {to extract the chunk kind. Example, if the class name is TChunkIEND } | 
|---|
|  | 1810 | {this method extracts and returns IEND} | 
|---|
|  | 1811 | Result := Copy(ClassName, Length('TChunk') + 1, Length(ClassName)); | 
|---|
|  | 1812 | end; | 
|---|
|  | 1813 |  | 
|---|
|  | 1814 | {Saves the data to the stream} | 
|---|
|  | 1815 | function TChunk.SaveData(Stream: TStream): Boolean; | 
|---|
|  | 1816 | var | 
|---|
|  | 1817 | ChunkSize, ChunkCRC: Cardinal; | 
|---|
|  | 1818 | begin | 
|---|
|  | 1819 | {First, write the size for the following data in the chunk} | 
|---|
|  | 1820 | ChunkSize := ByteSwap(DataSize); | 
|---|
|  | 1821 | Stream.Write(ChunkSize, 4); | 
|---|
|  | 1822 | {The chunk name} | 
|---|
|  | 1823 | Stream.Write(fName, 4); | 
|---|
|  | 1824 | {If there is data for the chunk, write it} | 
|---|
|  | 1825 | if DataSize > 0 then Stream.Write(Data^, DataSize); | 
|---|
|  | 1826 | {Calculates and write CRC} | 
|---|
|  | 1827 | ChunkCRC := update_crc($ffffffff, @fName[0], 4); | 
|---|
|  | 1828 | ChunkCRC := Byteswap(update_crc(ChunkCRC, Data, DataSize) xor $ffffffff); | 
|---|
|  | 1829 | Stream.Write(ChunkCRC, 4); | 
|---|
|  | 1830 |  | 
|---|
|  | 1831 | {Returns that everything went ok} | 
|---|
|  | 1832 | Result := TRUE; | 
|---|
|  | 1833 | end; | 
|---|
|  | 1834 |  | 
|---|
|  | 1835 | {Saves the chunk to the stream} | 
|---|
|  | 1836 | function TChunk.SaveToStream(Stream: TStream): Boolean; | 
|---|
|  | 1837 | begin | 
|---|
|  | 1838 | Result := SaveData(Stream) | 
|---|
|  | 1839 | end; | 
|---|
|  | 1840 |  | 
|---|
|  | 1841 |  | 
|---|
|  | 1842 | {Loads the chunk from a stream} | 
|---|
|  | 1843 | function TChunk.LoadFromStream(Stream: TStream; const ChunkName: TChunkName; | 
|---|
|  | 1844 | Size: Integer): Boolean; | 
|---|
|  | 1845 | var | 
|---|
|  | 1846 | CheckCRC: Cardinal; | 
|---|
|  | 1847 | {$IFDEF CheckCRC}RightCRC: Cardinal;{$ENDIF} | 
|---|
|  | 1848 | begin | 
|---|
|  | 1849 | {Copies data from source} | 
|---|
|  | 1850 | ResizeData(Size); | 
|---|
|  | 1851 | if Size > 0 then Stream.Read(fData^, Size); | 
|---|
|  | 1852 | {Reads CRC} | 
|---|
|  | 1853 | Stream.Read(CheckCRC, 4); | 
|---|
|  | 1854 | CheckCrc := ByteSwap(CheckCRC); | 
|---|
|  | 1855 |  | 
|---|
|  | 1856 | {Check if crc readed is valid} | 
|---|
|  | 1857 | {$IFDEF CheckCRC} | 
|---|
|  | 1858 | RightCRC := update_crc($ffffffff, @ChunkName[0], 4); | 
|---|
|  | 1859 | RightCRC := update_crc(RightCRC, fData, Size) xor $ffffffff; | 
|---|
|  | 1860 | Result := RightCRC = CheckCrc; | 
|---|
|  | 1861 |  | 
|---|
|  | 1862 | {Handle CRC error} | 
|---|
|  | 1863 | if not Result then | 
|---|
|  | 1864 | begin | 
|---|
|  | 1865 | {In case it coult not load chunk} | 
|---|
|  | 1866 | Owner.RaiseError(EPngInvalidCRC, EPngInvalidCRCText); | 
|---|
|  | 1867 | exit; | 
|---|
|  | 1868 | end | 
|---|
|  | 1869 | {$ELSE}Result := TRUE; {$ENDIF} | 
|---|
|  | 1870 |  | 
|---|
|  | 1871 | end; | 
|---|
|  | 1872 |  | 
|---|
|  | 1873 | {TChunktIME implementation} | 
|---|
|  | 1874 |  | 
|---|
|  | 1875 | {Chunk being loaded from a stream} | 
|---|
|  | 1876 | function TChunktIME.LoadFromStream(Stream: TStream; | 
|---|
|  | 1877 | const ChunkName: TChunkName; Size: Integer): Boolean; | 
|---|
|  | 1878 | begin | 
|---|
|  | 1879 | {Let ancestor load the data} | 
|---|
|  | 1880 | Result := inherited LoadFromStream(Stream, ChunkName, Size); | 
|---|
|  | 1881 | if not Result or (Size <> 7) then exit; {Size must be 7} | 
|---|
|  | 1882 |  | 
|---|
|  | 1883 | {Reads data} | 
|---|
|  | 1884 | fYear := ((pByte(Longint(Data) )^) * 256)+ (pByte(Longint(Data) + 1)^); | 
|---|
|  | 1885 | fMonth := pByte(Longint(Data) + 2)^; | 
|---|
|  | 1886 | fDay := pByte(Longint(Data) + 3)^; | 
|---|
|  | 1887 | fHour := pByte(Longint(Data) + 4)^; | 
|---|
|  | 1888 | fMinute := pByte(Longint(Data) + 5)^; | 
|---|
|  | 1889 | fSecond := pByte(Longint(Data) + 6)^; | 
|---|
|  | 1890 | end; | 
|---|
|  | 1891 |  | 
|---|
|  | 1892 | {Saving the chunk to a stream} | 
|---|
|  | 1893 | function TChunktIME.SaveToStream(Stream: TStream): Boolean; | 
|---|
|  | 1894 | begin | 
|---|
|  | 1895 | {Update data} | 
|---|
|  | 1896 | ResizeData(7);  {Make sure the size is 7} | 
|---|
|  | 1897 | pWord(Data)^ := Year; | 
|---|
|  | 1898 | pByte(Longint(Data) + 2)^ := Month; | 
|---|
|  | 1899 | pByte(Longint(Data) + 3)^ := Day; | 
|---|
|  | 1900 | pByte(Longint(Data) + 4)^ := Hour; | 
|---|
|  | 1901 | pByte(Longint(Data) + 5)^ := Minute; | 
|---|
|  | 1902 | pByte(Longint(Data) + 6)^ := Second; | 
|---|
|  | 1903 |  | 
|---|
|  | 1904 | {Let inherited save data} | 
|---|
|  | 1905 | Result := inherited SaveToStream(Stream); | 
|---|
|  | 1906 | end; | 
|---|
|  | 1907 |  | 
|---|
|  | 1908 | {TChunkztXt implementation} | 
|---|
|  | 1909 |  | 
|---|
|  | 1910 | {Loading the chunk from a stream} | 
|---|
|  | 1911 | function TChunkzTXt.LoadFromStream(Stream: TStream; | 
|---|
|  | 1912 | const ChunkName: TChunkName; Size: Integer): Boolean; | 
|---|
|  | 1913 | var | 
|---|
|  | 1914 | ErrorOutput: String; | 
|---|
|  | 1915 | CompressionMethod: Byte; | 
|---|
|  | 1916 | Output: Pointer; | 
|---|
|  | 1917 | OutputSize: Integer; | 
|---|
|  | 1918 | begin | 
|---|
|  | 1919 | {Load data from stream and validate} | 
|---|
|  | 1920 | Result := inherited LoadFromStream(Stream, ChunkName, Size); | 
|---|
|  | 1921 | if not Result or (Size < 4) then exit; | 
|---|
|  | 1922 | fKeyword := PChar(Data);  {Get keyword and compression method bellow} | 
|---|
|  | 1923 | CompressionMethod := pByte(Longint(fKeyword) + Length(fKeyword))^; | 
|---|
|  | 1924 | fText := ''; | 
|---|
|  | 1925 |  | 
|---|
|  | 1926 | {In case the compression is 0 (only one accepted by specs), reads it} | 
|---|
|  | 1927 | if CompressionMethod = 0 then | 
|---|
|  | 1928 | begin | 
|---|
|  | 1929 | Output := nil; | 
|---|
|  | 1930 | if DecompressZLIB(PChar(Longint(Data) + Length(fKeyword) + 2), | 
|---|
|  | 1931 | Size - Length(fKeyword) - 2, Output, OutputSize, ErrorOutput) then | 
|---|
|  | 1932 | begin | 
|---|
|  | 1933 | SetLength(fText, OutputSize); | 
|---|
|  | 1934 | CopyMemory(@fText[1], Output, OutputSize); | 
|---|
|  | 1935 | end {if DecompressZLIB(...}; | 
|---|
|  | 1936 | FreeMem(Output); | 
|---|
|  | 1937 | end {if CompressionMethod = 0} | 
|---|
|  | 1938 |  | 
|---|
|  | 1939 | end; | 
|---|
|  | 1940 |  | 
|---|
|  | 1941 | {Saving the chunk to a stream} | 
|---|
|  | 1942 | function TChunkztXt.SaveToStream(Stream: TStream): Boolean; | 
|---|
|  | 1943 | var | 
|---|
|  | 1944 | Output: Pointer; | 
|---|
|  | 1945 | OutputSize: Integer; | 
|---|
|  | 1946 | ErrorOutput: String; | 
|---|
|  | 1947 | begin | 
|---|
|  | 1948 | Output := nil; {Initializes output} | 
|---|
|  | 1949 | if fText = '' then fText := ' '; | 
|---|
|  | 1950 |  | 
|---|
|  | 1951 | {Compresses the data} | 
|---|
|  | 1952 | if CompressZLIB(@fText[1], Length(fText), Owner.CompressionLevel, Output, | 
|---|
|  | 1953 | OutputSize, ErrorOutput) then | 
|---|
|  | 1954 | begin | 
|---|
|  | 1955 | {Size is length from keyword, plus a null character to divide} | 
|---|
|  | 1956 | {plus the compression method, plus the length of the text (zlib compressed)} | 
|---|
|  | 1957 | ResizeData(Length(fKeyword) + 2 + OutputSize); | 
|---|
|  | 1958 |  | 
|---|
|  | 1959 | Fillchar(Data^, DataSize, #0); | 
|---|
|  | 1960 | {Copies the keyword data} | 
|---|
|  | 1961 | if Keyword <> '' then | 
|---|
|  | 1962 | CopyMemory(Data, @fKeyword[1], Length(Keyword)); | 
|---|
|  | 1963 | {Compression method 0 (inflate/deflate)} | 
|---|
|  | 1964 | pByte(Ptr(Longint(Data) + Length(Keyword) + 1))^ := 0; | 
|---|
|  | 1965 | if OutputSize > 0 then | 
|---|
|  | 1966 | CopyMemory(Ptr(Longint(Data) + Length(Keyword) + 2), Output, OutputSize); | 
|---|
|  | 1967 |  | 
|---|
|  | 1968 | {Let ancestor calculate crc and save} | 
|---|
|  | 1969 | Result := SaveData(Stream); | 
|---|
|  | 1970 | end {if CompressZLIB(...} else Result := False; | 
|---|
|  | 1971 |  | 
|---|
|  | 1972 | {Frees output} | 
|---|
|  | 1973 | if Output <> nil then FreeMem(Output) | 
|---|
|  | 1974 | end; | 
|---|
|  | 1975 |  | 
|---|
|  | 1976 | {TChunktEXt implementation} | 
|---|
|  | 1977 |  | 
|---|
|  | 1978 | {Assigns from another text chunk} | 
|---|
|  | 1979 | procedure TChunktEXt.Assign(Source: TChunk); | 
|---|
|  | 1980 | begin | 
|---|
|  | 1981 | fKeyword := TChunktEXt(Source).fKeyword; | 
|---|
|  | 1982 | fText := TChunktEXt(Source).fText; | 
|---|
|  | 1983 | end; | 
|---|
|  | 1984 |  | 
|---|
|  | 1985 | {Loading the chunk from a stream} | 
|---|
|  | 1986 | function TChunktEXt.LoadFromStream(Stream: TStream; | 
|---|
|  | 1987 | const ChunkName: TChunkName; Size: Integer): Boolean; | 
|---|
|  | 1988 | begin | 
|---|
|  | 1989 | {Load data from stream and validate} | 
|---|
|  | 1990 | Result := inherited LoadFromStream(Stream, ChunkName, Size); | 
|---|
|  | 1991 | if not Result or (Size < 3) then exit; | 
|---|
|  | 1992 | {Get text} | 
|---|
|  | 1993 | fKeyword := PChar(Data); | 
|---|
|  | 1994 | SetLength(fText, Size - Length(fKeyword) - 1); | 
|---|
|  | 1995 | CopyMemory(@fText[1], Ptr(Longint(Data) + Length(fKeyword) + 1), | 
|---|
|  | 1996 | Length(fText)); | 
|---|
|  | 1997 | end; | 
|---|
|  | 1998 |  | 
|---|
|  | 1999 | {Saving the chunk to a stream} | 
|---|
|  | 2000 | function TChunktEXt.SaveToStream(Stream: TStream): Boolean; | 
|---|
|  | 2001 | begin | 
|---|
|  | 2002 | {Size is length from keyword, plus a null character to divide} | 
|---|
|  | 2003 | {plus the length of the text} | 
|---|
|  | 2004 | ResizeData(Length(fKeyword) + 1 + Length(fText)); | 
|---|
|  | 2005 | Fillchar(Data^, DataSize, #0); | 
|---|
|  | 2006 | {Copy data} | 
|---|
|  | 2007 | if Keyword <> '' then | 
|---|
|  | 2008 | CopyMemory(Data, @fKeyword[1], Length(Keyword)); | 
|---|
|  | 2009 | if Text <> '' then | 
|---|
|  | 2010 | CopyMemory(Ptr(Longint(Data) + Length(Keyword) + 1), @fText[1], | 
|---|
|  | 2011 | Length(Text)); | 
|---|
|  | 2012 | {Let ancestor calculate crc and save} | 
|---|
|  | 2013 | Result := inherited SaveToStream(Stream); | 
|---|
|  | 2014 | end; | 
|---|
|  | 2015 |  | 
|---|
|  | 2016 |  | 
|---|
|  | 2017 | {TChunkIHDR implementation} | 
|---|
|  | 2018 |  | 
|---|
|  | 2019 | {Chunk being created} | 
|---|
|  | 2020 | constructor TChunkIHDR.Create(Owner: TPngObject); | 
|---|
|  | 2021 | begin | 
|---|
|  | 2022 | {Call inherited} | 
|---|
|  | 2023 | inherited Create(Owner); | 
|---|
|  | 2024 | {Prepare pointers} | 
|---|
|  | 2025 | ImageHandle := 0; | 
|---|
|  | 2026 | ImageDC := 0; | 
|---|
|  | 2027 | end; | 
|---|
|  | 2028 |  | 
|---|
|  | 2029 | {Chunk being destroyed} | 
|---|
|  | 2030 | destructor TChunkIHDR.Destroy; | 
|---|
|  | 2031 | begin | 
|---|
|  | 2032 | {Free memory} | 
|---|
|  | 2033 | FreeImageData(); | 
|---|
|  | 2034 |  | 
|---|
|  | 2035 | {Calls TChunk destroy} | 
|---|
|  | 2036 | inherited Destroy; | 
|---|
|  | 2037 | end; | 
|---|
|  | 2038 |  | 
|---|
|  | 2039 | {Assigns from another IHDR chunk} | 
|---|
|  | 2040 | procedure TChunkIHDR.Assign(Source: TChunk); | 
|---|
|  | 2041 | begin | 
|---|
|  | 2042 | {Copy the IHDR data} | 
|---|
|  | 2043 | if Source is TChunkIHDR then | 
|---|
|  | 2044 | begin | 
|---|
|  | 2045 | {Copy IHDR values} | 
|---|
|  | 2046 | IHDRData := TChunkIHDR(Source).IHDRData; | 
|---|
|  | 2047 |  | 
|---|
|  | 2048 | {Prepare to hold data by filling BitmapInfo structure and} | 
|---|
|  | 2049 | {resizing ImageData and ImageAlpha memory allocations} | 
|---|
|  | 2050 | PrepareImageData(); | 
|---|
|  | 2051 |  | 
|---|
|  | 2052 | {Copy image data} | 
|---|
|  | 2053 | CopyMemory(ImageData, TChunkIHDR(Source).ImageData, | 
|---|
|  | 2054 | BytesPerRow * Integer(Height)); | 
|---|
|  | 2055 | CopyMemory(ImageAlpha, TChunkIHDR(Source).ImageAlpha, | 
|---|
|  | 2056 | Integer(Width) * Integer(Height)); | 
|---|
|  | 2057 |  | 
|---|
|  | 2058 | {Copy palette colors} | 
|---|
|  | 2059 | BitmapInfo.bmiColors := TChunkIHDR(Source).BitmapInfo.bmiColors; | 
|---|
|  | 2060 | end | 
|---|
|  | 2061 | else | 
|---|
|  | 2062 | Owner.RaiseError(EPNGError, EPNGCannotAssignChunkText); | 
|---|
|  | 2063 | end; | 
|---|
|  | 2064 |  | 
|---|
|  | 2065 | {Release allocated image data} | 
|---|
|  | 2066 | procedure TChunkIHDR.FreeImageData; | 
|---|
|  | 2067 | begin | 
|---|
|  | 2068 | {Free old image data} | 
|---|
|  | 2069 | if ImageHandle <> 0  then DeleteObject(ImageHandle); | 
|---|
|  | 2070 | if ImageDC     <> 0  then DeleteDC(ImageDC); | 
|---|
|  | 2071 | if ImageAlpha <> nil then FreeMem(ImageAlpha); | 
|---|
|  | 2072 | {$IFDEF Store16bits} | 
|---|
|  | 2073 | if ExtraImageData <> nil then FreeMem(ExtraImageData); | 
|---|
|  | 2074 | {$ENDIF} | 
|---|
|  | 2075 | ImageHandle := 0; ImageDC := 0; ImageAlpha := nil; ImageData := nil; | 
|---|
|  | 2076 | end; | 
|---|
|  | 2077 |  | 
|---|
|  | 2078 | {Chunk being loaded from a stream} | 
|---|
|  | 2079 | function TChunkIHDR.LoadFromStream(Stream: TStream; const ChunkName: TChunkName; | 
|---|
|  | 2080 | Size: Integer): Boolean; | 
|---|
|  | 2081 | begin | 
|---|
|  | 2082 | {Let TChunk load it} | 
|---|
|  | 2083 | Result := inherited LoadFromStream(Stream, ChunkName, Size); | 
|---|
|  | 2084 | if not Result then Exit; | 
|---|
|  | 2085 |  | 
|---|
|  | 2086 | {Now check values} | 
|---|
|  | 2087 | {Note: It's recommended by png specification to make sure that the size} | 
|---|
|  | 2088 | {must be 13 bytes to be valid, but some images with 14 bytes were found} | 
|---|
|  | 2089 | {which could be loaded by internet explorer and other tools} | 
|---|
|  | 2090 | if (fDataSize < SIZEOF(TIHdrData)) then | 
|---|
|  | 2091 | begin | 
|---|
|  | 2092 | {Ihdr must always have at least 13 bytes} | 
|---|
|  | 2093 | Result := False; | 
|---|
|  | 2094 | Owner.RaiseError(EPNGInvalidIHDR, EPNGInvalidIHDRText); | 
|---|
|  | 2095 | exit; | 
|---|
|  | 2096 | end; | 
|---|
|  | 2097 |  | 
|---|
|  | 2098 | {Everything ok, reads IHDR} | 
|---|
|  | 2099 | IHDRData := pIHDRData(fData)^; | 
|---|
|  | 2100 | IHDRData.Width := ByteSwap(IHDRData.Width); | 
|---|
|  | 2101 | IHDRData.Height := ByteSwap(IHDRData.Height); | 
|---|
|  | 2102 |  | 
|---|
|  | 2103 | {The width and height must not be larger than 65535 pixels} | 
|---|
|  | 2104 | if (IHDRData.Width > High(Word)) or (IHDRData.Height > High(Word)) then | 
|---|
|  | 2105 | begin | 
|---|
|  | 2106 | Result := False; | 
|---|
|  | 2107 | Owner.RaiseError(EPNGSizeExceeds, EPNGSizeExceedsText); | 
|---|
|  | 2108 | exit; | 
|---|
|  | 2109 | end {if IHDRData.Width > High(Word)}; | 
|---|
|  | 2110 | {Compression method must be 0 (inflate/deflate)} | 
|---|
|  | 2111 | if (IHDRData.CompressionMethod <> 0) then | 
|---|
|  | 2112 | begin | 
|---|
|  | 2113 | Result := False; | 
|---|
|  | 2114 | Owner.RaiseError(EPNGUnknownCompression, EPNGUnknownCompressionText); | 
|---|
|  | 2115 | exit; | 
|---|
|  | 2116 | end; | 
|---|
|  | 2117 | {Interlace must be either 0 (none) or 7 (adam7)} | 
|---|
|  | 2118 | if (IHDRData.InterlaceMethod <> 0) and (IHDRData.InterlaceMethod <> 1) then | 
|---|
|  | 2119 | begin | 
|---|
|  | 2120 | Result := False; | 
|---|
|  | 2121 | Owner.RaiseError(EPNGUnknownInterlace, EPNGUnknownInterlaceText); | 
|---|
|  | 2122 | exit; | 
|---|
|  | 2123 | end; | 
|---|
|  | 2124 |  | 
|---|
|  | 2125 | {Updates owner properties} | 
|---|
|  | 2126 | Owner.InterlaceMethod := TInterlaceMethod(IHDRData.InterlaceMethod); | 
|---|
|  | 2127 |  | 
|---|
|  | 2128 | {Prepares data to hold image} | 
|---|
|  | 2129 | PrepareImageData(); | 
|---|
|  | 2130 | end; | 
|---|
|  | 2131 |  | 
|---|
|  | 2132 | {Saving the IHDR chunk to a stream} | 
|---|
|  | 2133 | function TChunkIHDR.SaveToStream(Stream: TStream): Boolean; | 
|---|
|  | 2134 | begin | 
|---|
|  | 2135 | {Ignore 2 bits images} | 
|---|
|  | 2136 | if BitDepth = 2 then BitDepth := 4; | 
|---|
|  | 2137 |  | 
|---|
|  | 2138 | {It needs to do is update the data with the IHDR data} | 
|---|
|  | 2139 | {structure containing the write values} | 
|---|
|  | 2140 | ResizeData(SizeOf(TIHDRData)); | 
|---|
|  | 2141 | pIHDRData(fData)^ := IHDRData; | 
|---|
|  | 2142 | {..byteswap 4 byte types} | 
|---|
|  | 2143 | pIHDRData(fData)^.Width := ByteSwap(pIHDRData(fData)^.Width); | 
|---|
|  | 2144 | pIHDRData(fData)^.Height := ByteSwap(pIHDRData(fData)^.Height); | 
|---|
|  | 2145 | {..update interlace method} | 
|---|
|  | 2146 | pIHDRData(fData)^.InterlaceMethod := Byte(Owner.InterlaceMethod); | 
|---|
|  | 2147 | {..and then let the ancestor SaveToStream do the hard work} | 
|---|
|  | 2148 | Result := inherited SaveToStream(Stream); | 
|---|
|  | 2149 | end; | 
|---|
|  | 2150 |  | 
|---|
|  | 2151 | {Resizes the image data to fill the color type, bit depth, } | 
|---|
|  | 2152 | {width and height parameters} | 
|---|
|  | 2153 | procedure TChunkIHDR.PrepareImageData(); | 
|---|
|  | 2154 |  | 
|---|
|  | 2155 | {Set the bitmap info} | 
|---|
|  | 2156 | procedure SetInfo(const Bitdepth: Integer; const Palette: Boolean); | 
|---|
|  | 2157 | begin | 
|---|
|  | 2158 |  | 
|---|
|  | 2159 | {Copy if the bitmap contain palette entries} | 
|---|
|  | 2160 | HasPalette := Palette; | 
|---|
|  | 2161 | {Initialize the structure with zeros} | 
|---|
|  | 2162 | fillchar(BitmapInfo, sizeof(BitmapInfo), #0); | 
|---|
|  | 2163 | {Fill the strucutre} | 
|---|
|  | 2164 | with BitmapInfo.bmiHeader do | 
|---|
|  | 2165 | begin | 
|---|
|  | 2166 | biSize := sizeof(TBitmapInfoHeader); | 
|---|
|  | 2167 | biHeight := Height; | 
|---|
|  | 2168 | biWidth := Width; | 
|---|
|  | 2169 | biPlanes := 1; | 
|---|
|  | 2170 | biBitCount := BitDepth; | 
|---|
|  | 2171 | biCompression := BI_RGB; | 
|---|
|  | 2172 | end {with BitmapInfo.bmiHeader} | 
|---|
|  | 2173 | end; | 
|---|
|  | 2174 | begin | 
|---|
|  | 2175 | {Prepare bitmap info header} | 
|---|
|  | 2176 | Fillchar(BitmapInfo, sizeof(TMaxBitmapInfo), #0); | 
|---|
|  | 2177 | {Release old image data} | 
|---|
|  | 2178 | FreeImageData(); | 
|---|
|  | 2179 |  | 
|---|
|  | 2180 | {Obtain number of bits for each pixel} | 
|---|
|  | 2181 | case ColorType of | 
|---|
|  | 2182 | COLOR_GRAYSCALE, COLOR_PALETTE, COLOR_GRAYSCALEALPHA: | 
|---|
|  | 2183 | case BitDepth of | 
|---|
|  | 2184 | {These are supported by windows} | 
|---|
|  | 2185 | 1, 4, 8: SetInfo(BitDepth, TRUE); | 
|---|
|  | 2186 | {2 bits for each pixel is not supported by windows bitmap} | 
|---|
|  | 2187 | 2      : SetInfo(4, TRUE); | 
|---|
|  | 2188 | {Also 16 bits (2 bytes) for each pixel is not supported} | 
|---|
|  | 2189 | {and should be transormed into a 8 bit grayscale} | 
|---|
|  | 2190 | 16     : SetInfo(8, TRUE); | 
|---|
|  | 2191 | end; | 
|---|
|  | 2192 | {Only 1 byte (8 bits) is supported} | 
|---|
|  | 2193 | COLOR_RGB, COLOR_RGBALPHA:  SetInfo(24, FALSE); | 
|---|
|  | 2194 | end {case ColorType}; | 
|---|
|  | 2195 | {Number of bytes for each scanline} | 
|---|
|  | 2196 | BytesPerRow := (((BitmapInfo.bmiHeader.biBitCount * Width) + 31) | 
|---|
|  | 2197 | and not 31) div 8; | 
|---|
|  | 2198 |  | 
|---|
|  | 2199 | {Build array for alpha information, if necessary} | 
|---|
|  | 2200 | if (ColorType = COLOR_RGBALPHA) or (ColorType = COLOR_GRAYSCALEALPHA) then | 
|---|
|  | 2201 | begin | 
|---|
|  | 2202 | GetMem(ImageAlpha, Integer(Width) * Integer(Height)); | 
|---|
|  | 2203 | FillChar(ImageAlpha^, Integer(Width) * Integer(Height), #0); | 
|---|
|  | 2204 | end; | 
|---|
|  | 2205 |  | 
|---|
|  | 2206 | {Build array for extra byte information} | 
|---|
|  | 2207 | {$IFDEF Store16bits} | 
|---|
|  | 2208 | if (BitDepth = 16) then | 
|---|
|  | 2209 | begin | 
|---|
|  | 2210 | GetMem(ExtraImageData, BytesPerRow * Integer(Height)); | 
|---|
|  | 2211 | FillChar(ExtraImageData^, BytesPerRow * Integer(Height), #0); | 
|---|
|  | 2212 | end; | 
|---|
|  | 2213 | {$ENDIF} | 
|---|
|  | 2214 |  | 
|---|
|  | 2215 | {Creates the image to hold the data, CreateDIBSection does a better} | 
|---|
|  | 2216 | {work in allocating necessary memory} | 
|---|
|  | 2217 | ImageDC := CreateCompatibleDC(0); | 
|---|
|  | 2218 | ImageHandle := CreateDIBSection(ImageDC, pBitmapInfo(@BitmapInfo)^, | 
|---|
|  | 2219 | DIB_RGB_COLORS, ImageData, 0, 0); | 
|---|
|  | 2220 |  | 
|---|
|  | 2221 | {Clears the old palette (if any)} | 
|---|
|  | 2222 | with Owner do | 
|---|
|  | 2223 | if  TempPalette <> 0 then | 
|---|
|  | 2224 | begin | 
|---|
|  | 2225 | DeleteObject(TempPalette); | 
|---|
|  | 2226 | TempPalette := 0; | 
|---|
|  | 2227 | end {with Owner, if TempPalette <> 0}; | 
|---|
|  | 2228 |  | 
|---|
|  | 2229 | {Build array and allocate bytes for each row} | 
|---|
|  | 2230 | zeromemory(ImageData, BytesPerRow * Integer(Height)); | 
|---|
|  | 2231 | end; | 
|---|
|  | 2232 |  | 
|---|
|  | 2233 | {TChunktRNS implementation} | 
|---|
|  | 2234 |  | 
|---|
|  | 2235 | {$IFNDEF UseDelphi} | 
|---|
|  | 2236 | function CompareMem(P1, P2: pByte; const Size: Integer): Boolean; | 
|---|
|  | 2237 | var i: Integer; | 
|---|
|  | 2238 | begin | 
|---|
|  | 2239 | Result := True; | 
|---|
|  | 2240 | for i := 1 to Size do | 
|---|
|  | 2241 | begin | 
|---|
|  | 2242 | if P1^ <> P2^ then Result := False; | 
|---|
|  | 2243 | inc(P1); inc(P2); | 
|---|
|  | 2244 | end {for i} | 
|---|
|  | 2245 | end; | 
|---|
|  | 2246 | {$ENDIF} | 
|---|
|  | 2247 |  | 
|---|
|  | 2248 | {Sets the transpararent color} | 
|---|
|  | 2249 | procedure TChunktRNS.SetTransparentColor(const Value: ColorRef); | 
|---|
|  | 2250 | var | 
|---|
|  | 2251 | i: Byte; | 
|---|
|  | 2252 | LookColor: TRGBQuad; | 
|---|
|  | 2253 | begin | 
|---|
|  | 2254 | {Clears the palette values} | 
|---|
|  | 2255 | Fillchar(PaletteValues, SizeOf(PaletteValues), #0); | 
|---|
|  | 2256 | {Sets that it uses bit transparency} | 
|---|
|  | 2257 | fBitTransparency := True; | 
|---|
|  | 2258 |  | 
|---|
|  | 2259 |  | 
|---|
|  | 2260 | {Depends on the color type} | 
|---|
|  | 2261 | with Header do | 
|---|
|  | 2262 | case ColorType of | 
|---|
|  | 2263 | COLOR_GRAYSCALE: | 
|---|
|  | 2264 | begin | 
|---|
|  | 2265 | Self.ResizeData(2); | 
|---|
|  | 2266 | pWord(@PaletteValues[0])^ := ByteSwap16(GetRValue(Value)); | 
|---|
|  | 2267 | end; | 
|---|
|  | 2268 | COLOR_RGB: | 
|---|
|  | 2269 | begin | 
|---|
|  | 2270 | Self.ResizeData(6); | 
|---|
|  | 2271 | pWord(@PaletteValues[0])^ := ByteSwap16(GetRValue(Value)); | 
|---|
|  | 2272 | pWord(@PaletteValues[2])^ := ByteSwap16(GetGValue(Value)); | 
|---|
|  | 2273 | pWord(@PaletteValues[4])^ := ByteSwap16(GetBValue(Value)); | 
|---|
|  | 2274 | end; | 
|---|
|  | 2275 | COLOR_PALETTE: | 
|---|
|  | 2276 | begin | 
|---|
|  | 2277 | {Creates a RGBQuad to search for the color} | 
|---|
|  | 2278 | LookColor.rgbRed := GetRValue(Value); | 
|---|
|  | 2279 | LookColor.rgbGreen := GetGValue(Value); | 
|---|
|  | 2280 | LookColor.rgbBlue := GetBValue(Value); | 
|---|
|  | 2281 | {Look in the table for the entry} | 
|---|
|  | 2282 | for i := 0 to 255 do | 
|---|
|  | 2283 | if CompareMem(@BitmapInfo.bmiColors[i], @LookColor, 3) then | 
|---|
|  | 2284 | Break; | 
|---|
|  | 2285 | {Fill the transparency table} | 
|---|
|  | 2286 | Fillchar(PaletteValues, i, 255); | 
|---|
|  | 2287 | Self.ResizeData(i + 1) | 
|---|
|  | 2288 |  | 
|---|
|  | 2289 | end | 
|---|
|  | 2290 | end {case / with}; | 
|---|
|  | 2291 |  | 
|---|
|  | 2292 | end; | 
|---|
|  | 2293 |  | 
|---|
|  | 2294 | {Returns the transparent color for the image} | 
|---|
|  | 2295 | function TChunktRNS.GetTransparentColor: ColorRef; | 
|---|
|  | 2296 | var | 
|---|
|  | 2297 | PaletteChunk: TChunkPLTE; | 
|---|
|  | 2298 | i: Integer; | 
|---|
|  | 2299 | begin | 
|---|
|  | 2300 | Result := 0; {Default: Unknown transparent color} | 
|---|
|  | 2301 |  | 
|---|
|  | 2302 | {Depends on the color type} | 
|---|
|  | 2303 | with Header do | 
|---|
|  | 2304 | case ColorType of | 
|---|
|  | 2305 | COLOR_GRAYSCALE: | 
|---|
|  | 2306 | Result := RGB(PaletteValues[0], PaletteValues[0], | 
|---|
|  | 2307 | PaletteValues[0]); | 
|---|
|  | 2308 | COLOR_RGB: | 
|---|
|  | 2309 | Result := RGB(PaletteValues[1], PaletteValues[3], PaletteValues[5]); | 
|---|
|  | 2310 | COLOR_PALETTE: | 
|---|
|  | 2311 | begin | 
|---|
|  | 2312 | {Obtains the palette chunk} | 
|---|
|  | 2313 | PaletteChunk := Owner.Chunks.ItemFromClass(TChunkPLTE) as TChunkPLTE; | 
|---|
|  | 2314 |  | 
|---|
|  | 2315 | {Looks for an entry with 0 transparency meaning that it is the} | 
|---|
|  | 2316 | {full transparent entry} | 
|---|
|  | 2317 | for i := 0 to Self.DataSize - 1 do | 
|---|
|  | 2318 | if PaletteValues[i] = 0 then | 
|---|
|  | 2319 | with PaletteChunk.GetPaletteItem(i) do | 
|---|
|  | 2320 | begin | 
|---|
|  | 2321 | Result := RGB(rgbRed, rgbGreen, rgbBlue); | 
|---|
|  | 2322 | break | 
|---|
|  | 2323 | end | 
|---|
|  | 2324 | end {COLOR_PALETTE} | 
|---|
|  | 2325 | end {case Header.ColorType}; | 
|---|
|  | 2326 | end; | 
|---|
|  | 2327 |  | 
|---|
|  | 2328 | {Saving the chunk to a stream} | 
|---|
|  | 2329 | function TChunktRNS.SaveToStream(Stream: TStream): Boolean; | 
|---|
|  | 2330 | begin | 
|---|
|  | 2331 | {Copy palette into data buffer} | 
|---|
|  | 2332 | if DataSize <= 256 then | 
|---|
|  | 2333 | CopyMemory(fData, @PaletteValues[0], DataSize); | 
|---|
|  | 2334 |  | 
|---|
|  | 2335 | Result := inherited SaveToStream(Stream); | 
|---|
|  | 2336 | end; | 
|---|
|  | 2337 |  | 
|---|
|  | 2338 | {Assigns from another chunk} | 
|---|
|  | 2339 | procedure TChunktRNS.Assign(Source: TChunk); | 
|---|
|  | 2340 | begin | 
|---|
|  | 2341 | CopyMemory(@PaletteValues[0], @TChunkTrns(Source).PaletteValues[0], 256); | 
|---|
|  | 2342 | fBitTransparency := TChunkTrns(Source).fBitTransparency; | 
|---|
|  | 2343 | inherited Assign(Source); | 
|---|
|  | 2344 | end; | 
|---|
|  | 2345 |  | 
|---|
|  | 2346 | {Loads the chunk from a stream} | 
|---|
|  | 2347 | function TChunktRNS.LoadFromStream(Stream: TStream; const ChunkName: TChunkName; | 
|---|
|  | 2348 | Size: Integer): Boolean; | 
|---|
|  | 2349 | var | 
|---|
|  | 2350 | i, Differ255: Integer; | 
|---|
|  | 2351 | begin | 
|---|
|  | 2352 | {Let inherited load} | 
|---|
|  | 2353 | Result := inherited LoadFromStream(Stream, ChunkName, Size); | 
|---|
|  | 2354 |  | 
|---|
|  | 2355 | if not Result then Exit; | 
|---|
|  | 2356 |  | 
|---|
|  | 2357 | {Make sure size is correct} | 
|---|
|  | 2358 | if Size > 256 then Owner.RaiseError(EPNGInvalidPalette, | 
|---|
|  | 2359 | EPNGInvalidPaletteText); | 
|---|
|  | 2360 |  | 
|---|
|  | 2361 | {The unset items should have value 255} | 
|---|
|  | 2362 | Fillchar(PaletteValues[0], 256, 255); | 
|---|
|  | 2363 | {Copy the other values} | 
|---|
|  | 2364 | CopyMemory(@PaletteValues[0], fData, Size); | 
|---|
|  | 2365 |  | 
|---|
|  | 2366 | {Create the mask if needed} | 
|---|
|  | 2367 | case Header.ColorType of | 
|---|
|  | 2368 | {Mask for grayscale and RGB} | 
|---|
|  | 2369 | COLOR_RGB, COLOR_GRAYSCALE: fBitTransparency := True; | 
|---|
|  | 2370 | COLOR_PALETTE: | 
|---|
|  | 2371 | begin | 
|---|
|  | 2372 | Differ255 := 0; {Count the entries with a value different from 255} | 
|---|
|  | 2373 | {Tests if it uses bit transparency} | 
|---|
|  | 2374 | for i := 0 to Size - 1 do | 
|---|
|  | 2375 | if PaletteValues[i] <> 255 then inc(Differ255); | 
|---|
|  | 2376 |  | 
|---|
|  | 2377 | {If it has one value different from 255 it is a bit transparency} | 
|---|
|  | 2378 | fBitTransparency := (Differ255 = 1); | 
|---|
|  | 2379 | end {COLOR_PALETTE} | 
|---|
|  | 2380 | end {case Header.ColorType}; | 
|---|
|  | 2381 |  | 
|---|
|  | 2382 | end; | 
|---|
|  | 2383 |  | 
|---|
|  | 2384 | {Prepares the image palette} | 
|---|
|  | 2385 | procedure TChunkIDAT.PreparePalette; | 
|---|
|  | 2386 | var | 
|---|
|  | 2387 | Entries: Word; | 
|---|
|  | 2388 | j      : Integer; | 
|---|
|  | 2389 | begin | 
|---|
|  | 2390 | {In case the image uses grayscale, build a grayscale palette} | 
|---|
|  | 2391 | with Header do | 
|---|
|  | 2392 | if (ColorType = COLOR_GRAYSCALE) or (ColorType = COLOR_GRAYSCALEALPHA) then | 
|---|
|  | 2393 | begin | 
|---|
|  | 2394 | {Calculate total number of palette entries} | 
|---|
|  | 2395 | Entries := (1 shl Byte(BitmapInfo.bmiHeader.biBitCount)); | 
|---|
|  | 2396 |  | 
|---|
|  | 2397 | FOR j := 0 TO Entries - 1 DO | 
|---|
|  | 2398 | with BitmapInfo.bmiColors[j] do | 
|---|
|  | 2399 | begin | 
|---|
|  | 2400 |  | 
|---|
|  | 2401 | {Calculate each palette entry} | 
|---|
|  | 2402 | rgbRed := fOwner.GammaTable[MulDiv(j, 255, Entries - 1)]; | 
|---|
|  | 2403 | rgbGreen := rgbRed; | 
|---|
|  | 2404 | rgbBlue := rgbRed; | 
|---|
|  | 2405 | end {with BitmapInfo.bmiColors[j]} | 
|---|
|  | 2406 | end {if ColorType = COLOR_GRAYSCALE..., with Header} | 
|---|
|  | 2407 | end; | 
|---|
|  | 2408 |  | 
|---|
|  | 2409 | {Reads from ZLIB} | 
|---|
|  | 2410 | function TChunkIDAT.IDATZlibRead(var ZLIBStream: TZStreamRec2; | 
|---|
|  | 2411 | Buffer: Pointer; Count: Integer; var EndPos: Integer; | 
|---|
|  | 2412 | var crcfile: Cardinal): Integer; | 
|---|
|  | 2413 | var | 
|---|
|  | 2414 | ProcResult : Integer; | 
|---|
|  | 2415 | IDATHeader : Array[0..3] of char; | 
|---|
|  | 2416 | IDATCRC    : Cardinal; | 
|---|
|  | 2417 | begin | 
|---|
|  | 2418 | {Uses internal record pointed by ZLIBStream to gather information} | 
|---|
|  | 2419 | with ZLIBStream, ZLIBStream.zlib do | 
|---|
|  | 2420 | begin | 
|---|
|  | 2421 | {Set the buffer the zlib will read into} | 
|---|
|  | 2422 | next_out := Buffer; | 
|---|
|  | 2423 | avail_out := Count; | 
|---|
|  | 2424 |  | 
|---|
|  | 2425 | {Decode until it reach the Count variable} | 
|---|
|  | 2426 | while avail_out > 0 do | 
|---|
|  | 2427 | begin | 
|---|
|  | 2428 | {In case it needs more data and it's in the end of a IDAT chunk,} | 
|---|
|  | 2429 | {it means that there are more IDAT chunks} | 
|---|
|  | 2430 | if (fStream.Position = EndPos) and (avail_out > 0) and | 
|---|
|  | 2431 | (avail_in = 0) then | 
|---|
|  | 2432 | begin | 
|---|
|  | 2433 | {End this chunk by reading and testing the crc value} | 
|---|
|  | 2434 | fStream.Read(IDATCRC, 4); | 
|---|
|  | 2435 |  | 
|---|
|  | 2436 | {$IFDEF CheckCRC} | 
|---|
|  | 2437 | if crcfile xor $ffffffff <> Cardinal(ByteSwap(IDATCRC)) then | 
|---|
|  | 2438 | begin | 
|---|
|  | 2439 | Result := -1; | 
|---|
|  | 2440 | Owner.RaiseError(EPNGInvalidCRC, EPNGInvalidCRCText); | 
|---|
|  | 2441 | exit; | 
|---|
|  | 2442 | end; | 
|---|
|  | 2443 | {$ENDIF} | 
|---|
|  | 2444 |  | 
|---|
|  | 2445 | {Start reading the next chunk} | 
|---|
|  | 2446 | fStream.Read(EndPos, 4);        {Reads next chunk size} | 
|---|
|  | 2447 | fStream.Read(IDATHeader[0], 4); {Next chunk header} | 
|---|
|  | 2448 | {It must be a IDAT chunk since image data is required and PNG} | 
|---|
|  | 2449 | {specification says that multiple IDAT chunks must be consecutive} | 
|---|
|  | 2450 | if IDATHeader <> 'IDAT' then | 
|---|
|  | 2451 | begin | 
|---|
|  | 2452 | Owner.RaiseError(EPNGMissingMultipleIDAT, EPNGMissingMultipleIDATText); | 
|---|
|  | 2453 | result := -1; | 
|---|
|  | 2454 | exit; | 
|---|
|  | 2455 | end; | 
|---|
|  | 2456 |  | 
|---|
|  | 2457 | {Calculate chunk name part of the crc} | 
|---|
|  | 2458 | {$IFDEF CheckCRC} | 
|---|
|  | 2459 | crcfile := update_crc($ffffffff, @IDATHeader[0], 4); | 
|---|
|  | 2460 | {$ENDIF} | 
|---|
|  | 2461 | EndPos := fStream.Position + ByteSwap(EndPos); | 
|---|
|  | 2462 | end; | 
|---|
|  | 2463 |  | 
|---|
|  | 2464 |  | 
|---|
|  | 2465 | {In case it needs compressed data to read from} | 
|---|
|  | 2466 | if avail_in = 0 then | 
|---|
|  | 2467 | begin | 
|---|
|  | 2468 | {In case it's trying to read more than it is avaliable} | 
|---|
|  | 2469 | if fStream.Position + ZLIBAllocate > EndPos then | 
|---|
|  | 2470 | avail_in := fStream.Read(Data^, EndPos - fStream.Position) | 
|---|
|  | 2471 | else | 
|---|
|  | 2472 | avail_in := fStream.Read(Data^, ZLIBAllocate); | 
|---|
|  | 2473 | {Update crc} | 
|---|
|  | 2474 | {$IFDEF CheckCRC} | 
|---|
|  | 2475 | crcfile := update_crc(crcfile, Data, avail_in); | 
|---|
|  | 2476 | {$ENDIF} | 
|---|
|  | 2477 |  | 
|---|
|  | 2478 | {In case there is no more compressed data to read from} | 
|---|
|  | 2479 | if avail_in = 0 then | 
|---|
|  | 2480 | begin | 
|---|
|  | 2481 | Result := Count - avail_out; | 
|---|
|  | 2482 | Exit; | 
|---|
|  | 2483 | end; | 
|---|
|  | 2484 |  | 
|---|
|  | 2485 | {Set next buffer to read and record current position} | 
|---|
|  | 2486 | next_in := Data; | 
|---|
|  | 2487 |  | 
|---|
|  | 2488 | end {if avail_in = 0}; | 
|---|
|  | 2489 |  | 
|---|
|  | 2490 | ProcResult := inflate(zlib, 0); | 
|---|
|  | 2491 |  | 
|---|
|  | 2492 | {In case the result was not sucessfull} | 
|---|
|  | 2493 | if (ProcResult < 0) then | 
|---|
|  | 2494 | begin | 
|---|
|  | 2495 | Result := -1; | 
|---|
|  | 2496 | Owner.RaiseError(EPNGZLIBError, | 
|---|
|  | 2497 | EPNGZLIBErrorText + zliberrors[procresult]); | 
|---|
|  | 2498 | exit; | 
|---|
|  | 2499 | end; | 
|---|
|  | 2500 |  | 
|---|
|  | 2501 | end {while avail_out > 0}; | 
|---|
|  | 2502 |  | 
|---|
|  | 2503 | end {with}; | 
|---|
|  | 2504 |  | 
|---|
|  | 2505 | {If everything gone ok, it returns the count bytes} | 
|---|
|  | 2506 | Result := Count; | 
|---|
|  | 2507 | end; | 
|---|
|  | 2508 |  | 
|---|
|  | 2509 | {TChunkIDAT implementation} | 
|---|
|  | 2510 |  | 
|---|
|  | 2511 | const | 
|---|
|  | 2512 | {Adam 7 interlacing values} | 
|---|
|  | 2513 | RowStart: array[0..6] of Integer = (0, 0, 4, 0, 2, 0, 1); | 
|---|
|  | 2514 | ColumnStart: array[0..6] of Integer = (0, 4, 0, 2, 0, 1, 0); | 
|---|
|  | 2515 | RowIncrement: array[0..6] of Integer = (8, 8, 8, 4, 4, 2, 2); | 
|---|
|  | 2516 | ColumnIncrement: array[0..6] of Integer = (8, 8, 4, 4, 2, 2, 1); | 
|---|
|  | 2517 |  | 
|---|
|  | 2518 | {Copy interlaced images with 1 byte for R, G, B} | 
|---|
|  | 2519 | procedure TChunkIDAT.CopyInterlacedRGB8(const Pass: Byte; | 
|---|
|  | 2520 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 2521 | var | 
|---|
|  | 2522 | Col: Integer; | 
|---|
|  | 2523 | begin | 
|---|
|  | 2524 | {Get first column and enter in loop} | 
|---|
|  | 2525 | Col := ColumnStart[Pass]; | 
|---|
|  | 2526 | Dest := pChar(Longint(Dest) + Col * 3); | 
|---|
|  | 2527 | repeat | 
|---|
|  | 2528 | {Copy this row} | 
|---|
|  | 2529 | Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest); | 
|---|
|  | 2530 | Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Dest); | 
|---|
|  | 2531 | Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src)    )^]; inc(Dest); | 
|---|
|  | 2532 |  | 
|---|
|  | 2533 | {Move to next column} | 
|---|
|  | 2534 | inc(Src, 3); | 
|---|
|  | 2535 | inc(Dest, ColumnIncrement[Pass] * 3 - 3); | 
|---|
|  | 2536 | inc(Col, ColumnIncrement[Pass]); | 
|---|
|  | 2537 | until Col >= ImageWidth; | 
|---|
|  | 2538 | end; | 
|---|
|  | 2539 |  | 
|---|
|  | 2540 | {Copy interlaced images with 2 bytes for R, G, B} | 
|---|
|  | 2541 | procedure TChunkIDAT.CopyInterlacedRGB16(const Pass: Byte; | 
|---|
|  | 2542 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 2543 | var | 
|---|
|  | 2544 | Col: Integer; | 
|---|
|  | 2545 | begin | 
|---|
|  | 2546 | {Get first column and enter in loop} | 
|---|
|  | 2547 | Col := ColumnStart[Pass]; | 
|---|
|  | 2548 | Dest := pChar(Longint(Dest) + Col * 3); | 
|---|
|  | 2549 | repeat | 
|---|
|  | 2550 | {Copy this row} | 
|---|
|  | 2551 | Byte(Dest^) := Owner.GammaTable[pByte(Longint(Src) + 4)^]; inc(Dest); | 
|---|
|  | 2552 | Byte(Dest^) := Owner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest); | 
|---|
|  | 2553 | Byte(Dest^) := Owner.GammaTable[pByte(Longint(Src)    )^]; inc(Dest); | 
|---|
|  | 2554 | {$IFDEF Store16bits} | 
|---|
|  | 2555 | {Copy extra pixel values} | 
|---|
|  | 2556 | Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 5)^]; inc(Extra); | 
|---|
|  | 2557 | Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 3)^]; inc(Extra); | 
|---|
|  | 2558 | Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Extra); | 
|---|
|  | 2559 | {$ENDIF} | 
|---|
|  | 2560 |  | 
|---|
|  | 2561 | {Move to next column} | 
|---|
|  | 2562 | inc(Src, 6); | 
|---|
|  | 2563 | inc(Dest, ColumnIncrement[Pass] * 3 - 3); | 
|---|
|  | 2564 | inc(Col, ColumnIncrement[Pass]); | 
|---|
|  | 2565 | until Col >= ImageWidth; | 
|---|
|  | 2566 | end; | 
|---|
|  | 2567 |  | 
|---|
|  | 2568 | {Copy ímages with palette using bit depths 1, 4 or 8} | 
|---|
|  | 2569 | procedure TChunkIDAT.CopyInterlacedPalette148(const Pass: Byte; | 
|---|
|  | 2570 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 2571 | const | 
|---|
|  | 2572 | BitTable: Array[1..8] of Integer = ($1, $3, 0, $F, 0, 0, 0, $FF); | 
|---|
|  | 2573 | StartBit: Array[1..8] of Integer = (7 , 0 , 0, 4,  0, 0, 0, 0); | 
|---|
|  | 2574 | var | 
|---|
|  | 2575 | CurBit, Col: Integer; | 
|---|
|  | 2576 | Dest2: PChar; | 
|---|
|  | 2577 | begin | 
|---|
|  | 2578 | {Get first column and enter in loop} | 
|---|
|  | 2579 | Col := ColumnStart[Pass]; | 
|---|
|  | 2580 | repeat | 
|---|
|  | 2581 | {Copy data} | 
|---|
|  | 2582 | CurBit := StartBit[Header.BitDepth]; | 
|---|
|  | 2583 | repeat | 
|---|
|  | 2584 | {Adjust pointer to pixel byte bounds} | 
|---|
|  | 2585 | Dest2 := pChar(Longint(Dest) + (Header.BitDepth * Col) div 8); | 
|---|
|  | 2586 | {Copy data} | 
|---|
|  | 2587 | Byte(Dest2^) := Byte(Dest2^) or | 
|---|
|  | 2588 | ( ((Byte(Src^) shr CurBit) and BitTable[Header.BitDepth]) | 
|---|
|  | 2589 | shl (StartBit[Header.BitDepth] - (Col * Header.BitDepth mod 8))); | 
|---|
|  | 2590 |  | 
|---|
|  | 2591 | {Move to next column} | 
|---|
|  | 2592 | inc(Col, ColumnIncrement[Pass]); | 
|---|
|  | 2593 | {Will read next bits} | 
|---|
|  | 2594 | dec(CurBit, Header.BitDepth); | 
|---|
|  | 2595 | until CurBit < 0; | 
|---|
|  | 2596 |  | 
|---|
|  | 2597 | {Move to next byte in source} | 
|---|
|  | 2598 | inc(Src); | 
|---|
|  | 2599 | until Col >= ImageWidth; | 
|---|
|  | 2600 | end; | 
|---|
|  | 2601 |  | 
|---|
|  | 2602 | {Copy ímages with palette using bit depth 2} | 
|---|
|  | 2603 | procedure TChunkIDAT.CopyInterlacedPalette2(const Pass: Byte; Src, Dest, | 
|---|
|  | 2604 | Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 2605 | var | 
|---|
|  | 2606 | CurBit, Col: Integer; | 
|---|
|  | 2607 | Dest2: PChar; | 
|---|
|  | 2608 | begin | 
|---|
|  | 2609 | {Get first column and enter in loop} | 
|---|
|  | 2610 | Col := ColumnStart[Pass]; | 
|---|
|  | 2611 | repeat | 
|---|
|  | 2612 | {Copy data} | 
|---|
|  | 2613 | CurBit := 6; | 
|---|
|  | 2614 | repeat | 
|---|
|  | 2615 | {Adjust pointer to pixel byte bounds} | 
|---|
|  | 2616 | Dest2 := pChar(Longint(Dest) + Col div 2); | 
|---|
|  | 2617 | {Copy data} | 
|---|
|  | 2618 | Byte(Dest2^) := Byte(Dest2^) or (((Byte(Src^) shr CurBit) and $3) | 
|---|
|  | 2619 | shl (4 - (4 * Col) mod 8)); | 
|---|
|  | 2620 | {Move to next column} | 
|---|
|  | 2621 | inc(Col, ColumnIncrement[Pass]); | 
|---|
|  | 2622 | {Will read next bits} | 
|---|
|  | 2623 | dec(CurBit, 2); | 
|---|
|  | 2624 | until CurBit < 0; | 
|---|
|  | 2625 |  | 
|---|
|  | 2626 | {Move to next byte in source} | 
|---|
|  | 2627 | inc(Src); | 
|---|
|  | 2628 | until Col >= ImageWidth; | 
|---|
|  | 2629 | end; | 
|---|
|  | 2630 |  | 
|---|
|  | 2631 | {Copy ímages with grayscale using bit depth 2} | 
|---|
|  | 2632 | procedure TChunkIDAT.CopyInterlacedGray2(const Pass: Byte; | 
|---|
|  | 2633 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 2634 | var | 
|---|
|  | 2635 | CurBit, Col: Integer; | 
|---|
|  | 2636 | Dest2: PChar; | 
|---|
|  | 2637 | begin | 
|---|
|  | 2638 | {Get first column and enter in loop} | 
|---|
|  | 2639 | Col := ColumnStart[Pass]; | 
|---|
|  | 2640 | repeat | 
|---|
|  | 2641 | {Copy data} | 
|---|
|  | 2642 | CurBit := 6; | 
|---|
|  | 2643 | repeat | 
|---|
|  | 2644 | {Adjust pointer to pixel byte bounds} | 
|---|
|  | 2645 | Dest2 := pChar(Longint(Dest) + Col div 2); | 
|---|
|  | 2646 | {Copy data} | 
|---|
|  | 2647 | Byte(Dest2^) := Byte(Dest2^) or ((((Byte(Src^) shr CurBit) shl 2) and $F) | 
|---|
|  | 2648 | shl (4 - (Col*4) mod 8)); | 
|---|
|  | 2649 | {Move to next column} | 
|---|
|  | 2650 | inc(Col, ColumnIncrement[Pass]); | 
|---|
|  | 2651 | {Will read next bits} | 
|---|
|  | 2652 | dec(CurBit, 2); | 
|---|
|  | 2653 | until CurBit < 0; | 
|---|
|  | 2654 |  | 
|---|
|  | 2655 | {Move to next byte in source} | 
|---|
|  | 2656 | inc(Src); | 
|---|
|  | 2657 | until Col >= ImageWidth; | 
|---|
|  | 2658 | end; | 
|---|
|  | 2659 |  | 
|---|
|  | 2660 | {Copy ímages with palette using 2 bytes for each pixel} | 
|---|
|  | 2661 | procedure TChunkIDAT.CopyInterlacedGrayscale16(const Pass: Byte; | 
|---|
|  | 2662 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 2663 | var | 
|---|
|  | 2664 | Col: Integer; | 
|---|
|  | 2665 | begin | 
|---|
|  | 2666 | {Get first column and enter in loop} | 
|---|
|  | 2667 | Col := ColumnStart[Pass]; | 
|---|
|  | 2668 | Dest := pChar(Longint(Dest) + Col); | 
|---|
|  | 2669 | repeat | 
|---|
|  | 2670 | {Copy this row} | 
|---|
|  | 2671 | Dest^ := Src^; inc(Dest); | 
|---|
|  | 2672 | {$IFDEF Store16bits} | 
|---|
|  | 2673 | Extra^ := pChar(Longint(Src) + 1)^; inc(Extra); | 
|---|
|  | 2674 | {$ENDIF} | 
|---|
|  | 2675 |  | 
|---|
|  | 2676 | {Move to next column} | 
|---|
|  | 2677 | inc(Src, 2); | 
|---|
|  | 2678 | inc(Dest, ColumnIncrement[Pass] - 1); | 
|---|
|  | 2679 | inc(Col, ColumnIncrement[Pass]); | 
|---|
|  | 2680 | until Col >= ImageWidth; | 
|---|
|  | 2681 | end; | 
|---|
|  | 2682 |  | 
|---|
|  | 2683 | {Decodes interlaced RGB alpha with 1 byte for each sample} | 
|---|
|  | 2684 | procedure TChunkIDAT.CopyInterlacedRGBAlpha8(const Pass: Byte; | 
|---|
|  | 2685 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 2686 | var | 
|---|
|  | 2687 | Col: Integer; | 
|---|
|  | 2688 | begin | 
|---|
|  | 2689 | {Get first column and enter in loop} | 
|---|
|  | 2690 | Col := ColumnStart[Pass]; | 
|---|
|  | 2691 | Dest := pChar(Longint(Dest) + Col * 3); | 
|---|
|  | 2692 | Trans := pChar(Longint(Trans) + Col); | 
|---|
|  | 2693 | repeat | 
|---|
|  | 2694 | {Copy this row and alpha value} | 
|---|
|  | 2695 | Trans^ := pChar(Longint(Src) + 3)^; | 
|---|
|  | 2696 | Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest); | 
|---|
|  | 2697 | Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Dest); | 
|---|
|  | 2698 | Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src)    )^]; inc(Dest); | 
|---|
|  | 2699 |  | 
|---|
|  | 2700 | {Move to next column} | 
|---|
|  | 2701 | inc(Src, 4); | 
|---|
|  | 2702 | inc(Dest, ColumnIncrement[Pass] * 3 - 3); | 
|---|
|  | 2703 | inc(Trans, ColumnIncrement[Pass]); | 
|---|
|  | 2704 | inc(Col, ColumnIncrement[Pass]); | 
|---|
|  | 2705 | until Col >= ImageWidth; | 
|---|
|  | 2706 | end; | 
|---|
|  | 2707 |  | 
|---|
|  | 2708 | {Decodes interlaced RGB alpha with 2 bytes for each sample} | 
|---|
|  | 2709 | procedure TChunkIDAT.CopyInterlacedRGBAlpha16(const Pass: Byte; | 
|---|
|  | 2710 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 2711 | var | 
|---|
|  | 2712 | Col: Integer; | 
|---|
|  | 2713 | begin | 
|---|
|  | 2714 | {Get first column and enter in loop} | 
|---|
|  | 2715 | Col := ColumnStart[Pass]; | 
|---|
|  | 2716 | Dest := pChar(Longint(Dest) + Col * 3); | 
|---|
|  | 2717 | Trans := pChar(Longint(Trans) + Col); | 
|---|
|  | 2718 | repeat | 
|---|
|  | 2719 | {Copy this row and alpha value} | 
|---|
|  | 2720 | Trans^ := pChar(Longint(Src) + 6)^; | 
|---|
|  | 2721 | Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src) + 4)^]; inc(Dest); | 
|---|
|  | 2722 | Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest); | 
|---|
|  | 2723 | Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src)    )^]; inc(Dest); | 
|---|
|  | 2724 | {$IFDEF Store16bits} | 
|---|
|  | 2725 | {Copy extra pixel values} | 
|---|
|  | 2726 | Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 5)^]; inc(Extra); | 
|---|
|  | 2727 | Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 3)^]; inc(Extra); | 
|---|
|  | 2728 | Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Extra); | 
|---|
|  | 2729 | {$ENDIF} | 
|---|
|  | 2730 |  | 
|---|
|  | 2731 | {Move to next column} | 
|---|
|  | 2732 | inc(Src, 8); | 
|---|
|  | 2733 | inc(Dest, ColumnIncrement[Pass] * 3 - 3); | 
|---|
|  | 2734 | inc(Trans, ColumnIncrement[Pass]); | 
|---|
|  | 2735 | inc(Col, ColumnIncrement[Pass]); | 
|---|
|  | 2736 | until Col >= ImageWidth; | 
|---|
|  | 2737 | end; | 
|---|
|  | 2738 |  | 
|---|
|  | 2739 | {Decodes 8 bit grayscale image followed by an alpha sample} | 
|---|
|  | 2740 | procedure TChunkIDAT.CopyInterlacedGrayscaleAlpha8(const Pass: Byte; | 
|---|
|  | 2741 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 2742 | var | 
|---|
|  | 2743 | Col: Integer; | 
|---|
|  | 2744 | begin | 
|---|
|  | 2745 | {Get first column, pointers to the data and enter in loop} | 
|---|
|  | 2746 | Col := ColumnStart[Pass]; | 
|---|
|  | 2747 | Dest := pChar(Longint(Dest) + Col); | 
|---|
|  | 2748 | Trans := pChar(Longint(Trans) + Col); | 
|---|
|  | 2749 | repeat | 
|---|
|  | 2750 | {Copy this grayscale value and alpha} | 
|---|
|  | 2751 | Dest^ := Src^;  inc(Src); | 
|---|
|  | 2752 | Trans^ := Src^; inc(Src); | 
|---|
|  | 2753 |  | 
|---|
|  | 2754 | {Move to next column} | 
|---|
|  | 2755 | inc(Dest, ColumnIncrement[Pass]); | 
|---|
|  | 2756 | inc(Trans, ColumnIncrement[Pass]); | 
|---|
|  | 2757 | inc(Col, ColumnIncrement[Pass]); | 
|---|
|  | 2758 | until Col >= ImageWidth; | 
|---|
|  | 2759 | end; | 
|---|
|  | 2760 |  | 
|---|
|  | 2761 | {Decodes 16 bit grayscale image followed by an alpha sample} | 
|---|
|  | 2762 | procedure TChunkIDAT.CopyInterlacedGrayscaleAlpha16(const Pass: Byte; | 
|---|
|  | 2763 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 2764 | var | 
|---|
|  | 2765 | Col: Integer; | 
|---|
|  | 2766 | begin | 
|---|
|  | 2767 | {Get first column, pointers to the data and enter in loop} | 
|---|
|  | 2768 | Col := ColumnStart[Pass]; | 
|---|
|  | 2769 | Dest := pChar(Longint(Dest) + Col); | 
|---|
|  | 2770 | Trans := pChar(Longint(Trans) + Col); | 
|---|
|  | 2771 | repeat | 
|---|
|  | 2772 | {$IFDEF Store16bits} | 
|---|
|  | 2773 | Extra^ := pChar(Longint(Src) + 1)^; inc(Extra); | 
|---|
|  | 2774 | {$ENDIF} | 
|---|
|  | 2775 | {Copy this grayscale value and alpha, transforming 16 bits into 8} | 
|---|
|  | 2776 | Dest^ := Src^;  inc(Src, 2); | 
|---|
|  | 2777 | Trans^ := Src^; inc(Src, 2); | 
|---|
|  | 2778 |  | 
|---|
|  | 2779 | {Move to next column} | 
|---|
|  | 2780 | inc(Dest, ColumnIncrement[Pass]); | 
|---|
|  | 2781 | inc(Trans, ColumnIncrement[Pass]); | 
|---|
|  | 2782 | inc(Col, ColumnIncrement[Pass]); | 
|---|
|  | 2783 | until Col >= ImageWidth; | 
|---|
|  | 2784 | end; | 
|---|
|  | 2785 |  | 
|---|
|  | 2786 | {Decodes an interlaced image} | 
|---|
|  | 2787 | procedure TChunkIDAT.DecodeInterlacedAdam7(Stream: TStream; | 
|---|
|  | 2788 | var ZLIBStream: TZStreamRec2; const Size: Integer; var crcfile: Cardinal); | 
|---|
|  | 2789 | var | 
|---|
|  | 2790 | CurrentPass: Byte; | 
|---|
|  | 2791 | PixelsThisRow: Integer; | 
|---|
|  | 2792 | CurrentRow: Integer; | 
|---|
|  | 2793 | Trans, Data{$IFDEF Store16bits}, Extra{$ENDIF}: pChar; | 
|---|
|  | 2794 | CopyProc: procedure(const Pass: Byte; Src, Dest, | 
|---|
|  | 2795 | Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar) of object; | 
|---|
|  | 2796 | begin | 
|---|
|  | 2797 |  | 
|---|
|  | 2798 | CopyProc := nil; {Initialize} | 
|---|
|  | 2799 | {Determine method to copy the image data} | 
|---|
|  | 2800 | case Header.ColorType of | 
|---|
|  | 2801 | {R, G, B values for each pixel} | 
|---|
|  | 2802 | COLOR_RGB: | 
|---|
|  | 2803 | case Header.BitDepth of | 
|---|
|  | 2804 | 8:  CopyProc := CopyInterlacedRGB8; | 
|---|
|  | 2805 | 16:  CopyProc := CopyInterlacedRGB16; | 
|---|
|  | 2806 | end {case Header.BitDepth}; | 
|---|
|  | 2807 | {Palette} | 
|---|
|  | 2808 | COLOR_PALETTE, COLOR_GRAYSCALE: | 
|---|
|  | 2809 | case Header.BitDepth of | 
|---|
|  | 2810 | 1, 4, 8: CopyProc := CopyInterlacedPalette148; | 
|---|
|  | 2811 | 2      : if Header.ColorType = COLOR_PALETTE then | 
|---|
|  | 2812 | CopyProc := CopyInterlacedPalette2 | 
|---|
|  | 2813 | else | 
|---|
|  | 2814 | CopyProc := CopyInterlacedGray2; | 
|---|
|  | 2815 | 16     : CopyProc := CopyInterlacedGrayscale16; | 
|---|
|  | 2816 | end; | 
|---|
|  | 2817 | {RGB followed by alpha} | 
|---|
|  | 2818 | COLOR_RGBALPHA: | 
|---|
|  | 2819 | case Header.BitDepth of | 
|---|
|  | 2820 | 8:  CopyProc := CopyInterlacedRGBAlpha8; | 
|---|
|  | 2821 | 16:  CopyProc := CopyInterlacedRGBAlpha16; | 
|---|
|  | 2822 | end; | 
|---|
|  | 2823 | {Grayscale followed by alpha} | 
|---|
|  | 2824 | COLOR_GRAYSCALEALPHA: | 
|---|
|  | 2825 | case Header.BitDepth of | 
|---|
|  | 2826 | 8:  CopyProc := CopyInterlacedGrayscaleAlpha8; | 
|---|
|  | 2827 | 16:  CopyProc := CopyInterlacedGrayscaleAlpha16; | 
|---|
|  | 2828 | end; | 
|---|
|  | 2829 | end {case Header.ColorType}; | 
|---|
|  | 2830 |  | 
|---|
|  | 2831 | {Adam7 method has 7 passes to make the final image} | 
|---|
|  | 2832 | FOR CurrentPass := 0 TO 6 DO | 
|---|
|  | 2833 | begin | 
|---|
|  | 2834 | {Calculates the number of pixels and bytes for this pass row} | 
|---|
|  | 2835 | PixelsThisRow := (ImageWidth - ColumnStart[CurrentPass] + | 
|---|
|  | 2836 | ColumnIncrement[CurrentPass] - 1) div ColumnIncrement[CurrentPass]; | 
|---|
|  | 2837 | Row_Bytes := BytesForPixels(PixelsThisRow, Header.ColorType, | 
|---|
|  | 2838 | Header.BitDepth); | 
|---|
|  | 2839 | {Clear buffer for this pass} | 
|---|
|  | 2840 | ZeroMemory(Row_Buffer[not RowUsed], Row_Bytes); | 
|---|
|  | 2841 |  | 
|---|
|  | 2842 | {Get current row index} | 
|---|
|  | 2843 | CurrentRow := RowStart[CurrentPass]; | 
|---|
|  | 2844 | {Get a pointer to the current row image data} | 
|---|
|  | 2845 | Data := Ptr(Longint(Header.ImageData) + Header.BytesPerRow * | 
|---|
|  | 2846 | (ImageHeight - 1 - CurrentRow)); | 
|---|
|  | 2847 | Trans := Ptr(Longint(Header.ImageAlpha) + ImageWidth * CurrentRow); | 
|---|
|  | 2848 | {$IFDEF Store16bits} | 
|---|
|  | 2849 | Extra := Ptr(Longint(Header.ExtraImageData) + Header.BytesPerRow * | 
|---|
|  | 2850 | (ImageHeight - 1 - CurrentRow)); | 
|---|
|  | 2851 | {$ENDIF} | 
|---|
|  | 2852 |  | 
|---|
|  | 2853 | if Row_Bytes > 0 then {There must have bytes for this interlaced pass} | 
|---|
|  | 2854 | while CurrentRow < ImageHeight do | 
|---|
|  | 2855 | begin | 
|---|
|  | 2856 | {Reads this line and filter} | 
|---|
|  | 2857 | if IDATZlibRead(ZLIBStream, @Row_Buffer[RowUsed][0], Row_Bytes + 1, | 
|---|
|  | 2858 | EndPos, CRCFile) = 0 then break; | 
|---|
|  | 2859 |  | 
|---|
|  | 2860 | FilterRow; | 
|---|
|  | 2861 | {Copy image data} | 
|---|
|  | 2862 |  | 
|---|
|  | 2863 | CopyProc(CurrentPass, @Row_Buffer[RowUsed][1], Data, Trans | 
|---|
|  | 2864 | {$IFDEF Store16bits}, Extra{$ENDIF}); | 
|---|
|  | 2865 |  | 
|---|
|  | 2866 | {Use the other RowBuffer item} | 
|---|
|  | 2867 | RowUsed := not RowUsed; | 
|---|
|  | 2868 |  | 
|---|
|  | 2869 | {Move to the next row} | 
|---|
|  | 2870 | inc(CurrentRow, RowIncrement[CurrentPass]); | 
|---|
|  | 2871 | {Move pointer to the next line} | 
|---|
|  | 2872 | dec(Data, RowIncrement[CurrentPass] * Header.BytesPerRow); | 
|---|
|  | 2873 | inc(Trans, RowIncrement[CurrentPass] * ImageWidth); | 
|---|
|  | 2874 | {$IFDEF Store16bits} | 
|---|
|  | 2875 | dec(Extra, RowIncrement[CurrentPass] * Header.BytesPerRow); | 
|---|
|  | 2876 | {$ENDIF} | 
|---|
|  | 2877 | end {while CurrentRow < ImageHeight}; | 
|---|
|  | 2878 |  | 
|---|
|  | 2879 | end {FOR CurrentPass}; | 
|---|
|  | 2880 |  | 
|---|
|  | 2881 | end; | 
|---|
|  | 2882 |  | 
|---|
|  | 2883 | {Copy 8 bits RGB image} | 
|---|
|  | 2884 | procedure TChunkIDAT.CopyNonInterlacedRGB8( | 
|---|
|  | 2885 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 2886 | var | 
|---|
|  | 2887 | I: Integer; | 
|---|
|  | 2888 | begin | 
|---|
|  | 2889 | FOR I := 1 TO ImageWidth DO | 
|---|
|  | 2890 | begin | 
|---|
|  | 2891 | {Copy pixel values} | 
|---|
|  | 2892 | Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest); | 
|---|
|  | 2893 | Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Dest); | 
|---|
|  | 2894 | Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src)    )^]; inc(Dest); | 
|---|
|  | 2895 | {Move to next pixel} | 
|---|
|  | 2896 | inc(Src, 3); | 
|---|
|  | 2897 | end {for I} | 
|---|
|  | 2898 | end; | 
|---|
|  | 2899 |  | 
|---|
|  | 2900 | {Copy 16 bits RGB image} | 
|---|
|  | 2901 | procedure TChunkIDAT.CopyNonInterlacedRGB16( | 
|---|
|  | 2902 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 2903 | var | 
|---|
|  | 2904 | I: Integer; | 
|---|
|  | 2905 | begin | 
|---|
|  | 2906 | FOR I := 1 TO ImageWidth DO | 
|---|
|  | 2907 | begin | 
|---|
|  | 2908 | //Since windows does not supports 2 bytes for | 
|---|
|  | 2909 | //each R, G, B value, the method will read only 1 byte from it | 
|---|
|  | 2910 | {Copy pixel values} | 
|---|
|  | 2911 | Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 4)^]; inc(Dest); | 
|---|
|  | 2912 | Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest); | 
|---|
|  | 2913 | Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src)    )^]; inc(Dest); | 
|---|
|  | 2914 | {$IFDEF Store16bits} | 
|---|
|  | 2915 | {Copy extra pixel values} | 
|---|
|  | 2916 | Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 5)^]; inc(Extra); | 
|---|
|  | 2917 | Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 3)^]; inc(Extra); | 
|---|
|  | 2918 | Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Extra); | 
|---|
|  | 2919 | {$ENDIF} | 
|---|
|  | 2920 |  | 
|---|
|  | 2921 | {Move to next pixel} | 
|---|
|  | 2922 | inc(Src, 6); | 
|---|
|  | 2923 | end {for I} | 
|---|
|  | 2924 | end; | 
|---|
|  | 2925 |  | 
|---|
|  | 2926 | {Copy types using palettes (1, 4 or 8 bits per pixel)} | 
|---|
|  | 2927 | procedure TChunkIDAT.CopyNonInterlacedPalette148( | 
|---|
|  | 2928 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 2929 | begin | 
|---|
|  | 2930 | {It's simple as copying the data} | 
|---|
|  | 2931 | CopyMemory(Dest, Src, Row_Bytes); | 
|---|
|  | 2932 | end; | 
|---|
|  | 2933 |  | 
|---|
|  | 2934 | {Copy grayscale types using 2 bits for each pixel} | 
|---|
|  | 2935 | procedure TChunkIDAT.CopyNonInterlacedGray2( | 
|---|
|  | 2936 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 2937 | var | 
|---|
|  | 2938 | i: Integer; | 
|---|
|  | 2939 | begin | 
|---|
|  | 2940 | {2 bits is not supported, this routine will converted into 4 bits} | 
|---|
|  | 2941 | FOR i := 1 TO Row_Bytes do | 
|---|
|  | 2942 | begin | 
|---|
|  | 2943 | Byte(Dest^) := ((Byte(Src^) shr 2) and $F) or ((Byte(Src^)) and $F0); inc(Dest); | 
|---|
|  | 2944 | Byte(Dest^) := ((Byte(Src^) shl 2) and $F) or ((Byte(Src^) shl 4) and $F0); inc(Dest); | 
|---|
|  | 2945 | inc(Src); | 
|---|
|  | 2946 | end {FOR i} | 
|---|
|  | 2947 | end; | 
|---|
|  | 2948 |  | 
|---|
|  | 2949 | {Copy types using palette with 2 bits for each pixel} | 
|---|
|  | 2950 | procedure TChunkIDAT.CopyNonInterlacedPalette2( | 
|---|
|  | 2951 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 2952 | var | 
|---|
|  | 2953 | i: Integer; | 
|---|
|  | 2954 | begin | 
|---|
|  | 2955 | {2 bits is not supported, this routine will converted into 4 bits} | 
|---|
|  | 2956 | FOR i := 1 TO Row_Bytes do | 
|---|
|  | 2957 | begin | 
|---|
|  | 2958 | Byte(Dest^) := ((Byte(Src^) shr 4) and $3) or ((Byte(Src^) shr 2) and $30); inc(Dest); | 
|---|
|  | 2959 | Byte(Dest^) := (Byte(Src^) and $3) or ((Byte(Src^) shl 2) and $30); inc(Dest); | 
|---|
|  | 2960 | inc(Src); | 
|---|
|  | 2961 | end {FOR i} | 
|---|
|  | 2962 | end; | 
|---|
|  | 2963 |  | 
|---|
|  | 2964 | {Copy grayscale images with 16 bits} | 
|---|
|  | 2965 | procedure TChunkIDAT.CopyNonInterlacedGrayscale16( | 
|---|
|  | 2966 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 2967 | var | 
|---|
|  | 2968 | I: Integer; | 
|---|
|  | 2969 | begin | 
|---|
|  | 2970 | FOR I := 1 TO ImageWidth DO | 
|---|
|  | 2971 | begin | 
|---|
|  | 2972 | {Windows does not supports 16 bits for each pixel in grayscale} | 
|---|
|  | 2973 | {mode, so reduce to 8} | 
|---|
|  | 2974 | Dest^ := Src^; inc(Dest); | 
|---|
|  | 2975 | {$IFDEF Store16bits} | 
|---|
|  | 2976 | Extra^ := pChar(Longint(Src) + 1)^; inc(Extra); | 
|---|
|  | 2977 | {$ENDIF} | 
|---|
|  | 2978 |  | 
|---|
|  | 2979 | {Move to next pixel} | 
|---|
|  | 2980 | inc(Src, 2); | 
|---|
|  | 2981 | end {for I} | 
|---|
|  | 2982 | end; | 
|---|
|  | 2983 |  | 
|---|
|  | 2984 | {Copy 8 bits per sample RGB images followed by an alpha byte} | 
|---|
|  | 2985 | procedure TChunkIDAT.CopyNonInterlacedRGBAlpha8( | 
|---|
|  | 2986 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 2987 | var | 
|---|
|  | 2988 | i: Integer; | 
|---|
|  | 2989 | begin | 
|---|
|  | 2990 | FOR I := 1 TO ImageWidth DO | 
|---|
|  | 2991 | begin | 
|---|
|  | 2992 | {Copy pixel values and transparency} | 
|---|
|  | 2993 | Trans^ := pChar(Longint(Src) + 3)^; | 
|---|
|  | 2994 | Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest); | 
|---|
|  | 2995 | Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Dest); | 
|---|
|  | 2996 | Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src)    )^]; inc(Dest); | 
|---|
|  | 2997 | {Move to next pixel} | 
|---|
|  | 2998 | inc(Src, 4); inc(Trans); | 
|---|
|  | 2999 | end {for I} | 
|---|
|  | 3000 | end; | 
|---|
|  | 3001 |  | 
|---|
|  | 3002 | {Copy 16 bits RGB image with alpha using 2 bytes for each sample} | 
|---|
|  | 3003 | procedure TChunkIDAT.CopyNonInterlacedRGBAlpha16( | 
|---|
|  | 3004 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 3005 | var | 
|---|
|  | 3006 | I: Integer; | 
|---|
|  | 3007 | begin | 
|---|
|  | 3008 | FOR I := 1 TO ImageWidth DO | 
|---|
|  | 3009 | begin | 
|---|
|  | 3010 | //Copy rgb and alpha values (transforming from 16 bits to 8 bits) | 
|---|
|  | 3011 | {Copy pixel values} | 
|---|
|  | 3012 | Trans^ := pChar(Longint(Src) + 6)^; | 
|---|
|  | 3013 | Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src) + 4)^]; inc(Dest); | 
|---|
|  | 3014 | Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest); | 
|---|
|  | 3015 | Byte(Dest^)  := fOwner.GammaTable[pByte(Longint(Src)    )^]; inc(Dest); | 
|---|
|  | 3016 | {$IFDEF Store16bits} | 
|---|
|  | 3017 | {Copy extra pixel values} | 
|---|
|  | 3018 | Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 5)^]; inc(Extra); | 
|---|
|  | 3019 | Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 3)^]; inc(Extra); | 
|---|
|  | 3020 | Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Extra); | 
|---|
|  | 3021 | {$ENDIF} | 
|---|
|  | 3022 | {Move to next pixel} | 
|---|
|  | 3023 | inc(Src, 8); inc(Trans); | 
|---|
|  | 3024 | end {for I} | 
|---|
|  | 3025 | end; | 
|---|
|  | 3026 |  | 
|---|
|  | 3027 | {Copy 8 bits per sample grayscale followed by alpha} | 
|---|
|  | 3028 | procedure TChunkIDAT.CopyNonInterlacedGrayscaleAlpha8( | 
|---|
|  | 3029 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 3030 | var | 
|---|
|  | 3031 | I: Integer; | 
|---|
|  | 3032 | begin | 
|---|
|  | 3033 | FOR I := 1 TO ImageWidth DO | 
|---|
|  | 3034 | begin | 
|---|
|  | 3035 | {Copy alpha value and then gray value} | 
|---|
|  | 3036 | Dest^  := Src^;  inc(Src); | 
|---|
|  | 3037 | Trans^ := Src^;  inc(Src); | 
|---|
|  | 3038 | inc(Dest); inc(Trans); | 
|---|
|  | 3039 | end; | 
|---|
|  | 3040 | end; | 
|---|
|  | 3041 |  | 
|---|
|  | 3042 | {Copy 16 bits per sample grayscale followed by alpha} | 
|---|
|  | 3043 | procedure TChunkIDAT.CopyNonInterlacedGrayscaleAlpha16( | 
|---|
|  | 3044 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar); | 
|---|
|  | 3045 | var | 
|---|
|  | 3046 | I: Integer; | 
|---|
|  | 3047 | begin | 
|---|
|  | 3048 | FOR I := 1 TO ImageWidth DO | 
|---|
|  | 3049 | begin | 
|---|
|  | 3050 | {Copy alpha value and then gray value} | 
|---|
|  | 3051 | {$IFDEF Store16bits} | 
|---|
|  | 3052 | Extra^ := pChar(Longint(Src) + 1)^; inc(Extra); | 
|---|
|  | 3053 | {$ENDIF} | 
|---|
|  | 3054 | Dest^  := Src^;  inc(Src, 2); | 
|---|
|  | 3055 | Trans^ := Src^;  inc(Src, 2); | 
|---|
|  | 3056 | inc(Dest); inc(Trans); | 
|---|
|  | 3057 | end; | 
|---|
|  | 3058 | end; | 
|---|
|  | 3059 |  | 
|---|
|  | 3060 | {Decode non interlaced image} | 
|---|
|  | 3061 | procedure TChunkIDAT.DecodeNonInterlaced(Stream: TStream; | 
|---|
|  | 3062 | var ZLIBStream: TZStreamRec2; const Size: Integer; var crcfile: Cardinal); | 
|---|
|  | 3063 | var | 
|---|
|  | 3064 | j: Cardinal; | 
|---|
|  | 3065 | Trans, Data{$IFDEF Store16bits}, Extra{$ENDIF}: pChar; | 
|---|
|  | 3066 | CopyProc: procedure( | 
|---|
|  | 3067 | Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar) of object; | 
|---|
|  | 3068 | begin | 
|---|
|  | 3069 | CopyProc := nil; {Initialize} | 
|---|
|  | 3070 | {Determines the method to copy the image data} | 
|---|
|  | 3071 | case Header.ColorType of | 
|---|
|  | 3072 | {R, G, B values} | 
|---|
|  | 3073 | COLOR_RGB: | 
|---|
|  | 3074 | case Header.BitDepth of | 
|---|
|  | 3075 | 8: CopyProc := CopyNonInterlacedRGB8; | 
|---|
|  | 3076 | 16: CopyProc := CopyNonInterlacedRGB16; | 
|---|
|  | 3077 | end; | 
|---|
|  | 3078 | {Types using palettes} | 
|---|
|  | 3079 | COLOR_PALETTE, COLOR_GRAYSCALE: | 
|---|
|  | 3080 | case Header.BitDepth of | 
|---|
|  | 3081 | 1, 4, 8: CopyProc := CopyNonInterlacedPalette148; | 
|---|
|  | 3082 | 2      : if Header.ColorType = COLOR_PALETTE then | 
|---|
|  | 3083 | CopyProc := CopyNonInterlacedPalette2 | 
|---|
|  | 3084 | else | 
|---|
|  | 3085 | CopyProc := CopyNonInterlacedGray2; | 
|---|
|  | 3086 | 16     : CopyProc := CopyNonInterlacedGrayscale16; | 
|---|
|  | 3087 | end; | 
|---|
|  | 3088 | {R, G, B followed by alpha} | 
|---|
|  | 3089 | COLOR_RGBALPHA: | 
|---|
|  | 3090 | case Header.BitDepth of | 
|---|
|  | 3091 | 8  : CopyProc := CopyNonInterlacedRGBAlpha8; | 
|---|
|  | 3092 | 16  : CopyProc := CopyNonInterlacedRGBAlpha16; | 
|---|
|  | 3093 | end; | 
|---|
|  | 3094 | {Grayscale followed by alpha} | 
|---|
|  | 3095 | COLOR_GRAYSCALEALPHA: | 
|---|
|  | 3096 | case Header.BitDepth of | 
|---|
|  | 3097 | 8  : CopyProc := CopyNonInterlacedGrayscaleAlpha8; | 
|---|
|  | 3098 | 16  : CopyProc := CopyNonInterlacedGrayscaleAlpha16; | 
|---|
|  | 3099 | end; | 
|---|
|  | 3100 | end; | 
|---|
|  | 3101 |  | 
|---|
|  | 3102 | {Get the image data pointer} | 
|---|
|  | 3103 | Longint(Data) := Longint(Header.ImageData) + | 
|---|
|  | 3104 | Header.BytesPerRow * (ImageHeight - 1); | 
|---|
|  | 3105 | Trans := Header.ImageAlpha; | 
|---|
|  | 3106 | {$IFDEF Store16bits} | 
|---|
|  | 3107 | Longint(Extra) := Longint(Header.ExtraImageData) + | 
|---|
|  | 3108 | Header.BytesPerRow * (ImageHeight - 1); | 
|---|
|  | 3109 | {$ENDIF} | 
|---|
|  | 3110 | {Reads each line} | 
|---|
|  | 3111 | FOR j := 0 to ImageHeight - 1 do | 
|---|
|  | 3112 | begin | 
|---|
|  | 3113 | {Read this line Row_Buffer[RowUsed][0] if the filter type for this line} | 
|---|
|  | 3114 | if IDATZlibRead(ZLIBStream, @Row_Buffer[RowUsed][0], Row_Bytes + 1, EndPos, | 
|---|
|  | 3115 | CRCFile) = 0 then break; | 
|---|
|  | 3116 |  | 
|---|
|  | 3117 | {Filter the current row} | 
|---|
|  | 3118 | FilterRow; | 
|---|
|  | 3119 | {Copies non interlaced row to image} | 
|---|
|  | 3120 | CopyProc(@Row_Buffer[RowUsed][1], Data, Trans{$IFDEF Store16bits}, Extra | 
|---|
|  | 3121 | {$ENDIF}); | 
|---|
|  | 3122 |  | 
|---|
|  | 3123 | {Invert line used} | 
|---|
|  | 3124 | RowUsed := not RowUsed; | 
|---|
|  | 3125 | dec(Data, Header.BytesPerRow); | 
|---|
|  | 3126 | {$IFDEF Store16bits}dec(Extra, Header.BytesPerRow);{$ENDIF} | 
|---|
|  | 3127 | inc(Trans, ImageWidth); | 
|---|
|  | 3128 | end {for I}; | 
|---|
|  | 3129 |  | 
|---|
|  | 3130 |  | 
|---|
|  | 3131 | end; | 
|---|
|  | 3132 |  | 
|---|
|  | 3133 | {Filter the current line} | 
|---|
|  | 3134 | procedure TChunkIDAT.FilterRow; | 
|---|
|  | 3135 | var | 
|---|
|  | 3136 | pp: Byte; | 
|---|
|  | 3137 | vv, left, above, aboveleft: Integer; | 
|---|
|  | 3138 | Col: Cardinal; | 
|---|
|  | 3139 | begin | 
|---|
|  | 3140 | {Test the filter} | 
|---|
|  | 3141 | case Row_Buffer[RowUsed]^[0] of | 
|---|
|  | 3142 | {No filtering for this line} | 
|---|
|  | 3143 | FILTER_NONE: begin end; | 
|---|
|  | 3144 | {AND 255 serves only to never let the result be larger than one byte} | 
|---|
|  | 3145 | {Sub filter} | 
|---|
|  | 3146 | FILTER_SUB: | 
|---|
|  | 3147 | FOR Col := Offset + 1 to Row_Bytes DO | 
|---|
|  | 3148 | Row_Buffer[RowUsed][Col] := (Row_Buffer[RowUsed][Col] + | 
|---|
|  | 3149 | Row_Buffer[RowUsed][Col - Offset]) and 255; | 
|---|
|  | 3150 | {Up filter} | 
|---|
|  | 3151 | FILTER_UP: | 
|---|
|  | 3152 | FOR Col := 1 to Row_Bytes DO | 
|---|
|  | 3153 | Row_Buffer[RowUsed][Col] := (Row_Buffer[RowUsed][Col] + | 
|---|
|  | 3154 | Row_Buffer[not RowUsed][Col]) and 255; | 
|---|
|  | 3155 | {Average filter} | 
|---|
|  | 3156 | FILTER_AVERAGE: | 
|---|
|  | 3157 | FOR Col := 1 to Row_Bytes DO | 
|---|
|  | 3158 | begin | 
|---|
|  | 3159 | {Obtains up and left pixels} | 
|---|
|  | 3160 | above := Row_Buffer[not RowUsed][Col]; | 
|---|
|  | 3161 | if col - 1 < Offset then | 
|---|
|  | 3162 | left := 0 | 
|---|
|  | 3163 | else | 
|---|
|  | 3164 | Left := Row_Buffer[RowUsed][Col - Offset]; | 
|---|
|  | 3165 |  | 
|---|
|  | 3166 | {Calculates} | 
|---|
|  | 3167 | Row_Buffer[RowUsed][Col] := (Row_Buffer[RowUsed][Col] + | 
|---|
|  | 3168 | (left + above) div 2) and 255; | 
|---|
|  | 3169 | end; | 
|---|
|  | 3170 | {Paeth filter} | 
|---|
|  | 3171 | FILTER_PAETH: | 
|---|
|  | 3172 | begin | 
|---|
|  | 3173 | {Initialize} | 
|---|
|  | 3174 | left := 0; | 
|---|
|  | 3175 | aboveleft := 0; | 
|---|
|  | 3176 | {Test each byte} | 
|---|
|  | 3177 | FOR Col := 1 to Row_Bytes DO | 
|---|
|  | 3178 | begin | 
|---|
|  | 3179 | {Obtains above pixel} | 
|---|
|  | 3180 | above := Row_Buffer[not RowUsed][Col]; | 
|---|
|  | 3181 | {Obtains left and top-left pixels} | 
|---|
|  | 3182 | if (col - 1 >= offset) Then | 
|---|
|  | 3183 | begin | 
|---|
|  | 3184 | left := row_buffer[RowUsed][col - offset]; | 
|---|
|  | 3185 | aboveleft := row_buffer[not RowUsed][col - offset]; | 
|---|
|  | 3186 | end; | 
|---|
|  | 3187 |  | 
|---|
|  | 3188 | {Obtains current pixel and paeth predictor} | 
|---|
|  | 3189 | vv := row_buffer[RowUsed][Col]; | 
|---|
|  | 3190 | pp := PaethPredictor(left, above, aboveleft); | 
|---|
|  | 3191 |  | 
|---|
|  | 3192 | {Calculates} | 
|---|
|  | 3193 | Row_Buffer[RowUsed][Col] := (pp + vv) and $FF; | 
|---|
|  | 3194 | end {for}; | 
|---|
|  | 3195 | end; | 
|---|
|  | 3196 |  | 
|---|
|  | 3197 | end {case}; | 
|---|
|  | 3198 | end; | 
|---|
|  | 3199 |  | 
|---|
|  | 3200 | {Reads the image data from the stream} | 
|---|
|  | 3201 | function TChunkIDAT.LoadFromStream(Stream: TStream; const ChunkName: TChunkName; | 
|---|
|  | 3202 | Size: Integer): Boolean; | 
|---|
|  | 3203 | var | 
|---|
|  | 3204 | ZLIBStream: TZStreamRec2; | 
|---|
|  | 3205 | CRCCheck, | 
|---|
|  | 3206 | CRCFile  : Cardinal; | 
|---|
|  | 3207 | begin | 
|---|
|  | 3208 | {Get pointer to the header chunk} | 
|---|
|  | 3209 | Header := Owner.Chunks.Item[0] as TChunkIHDR; | 
|---|
|  | 3210 | {Build palette if necessary} | 
|---|
|  | 3211 | if Header.HasPalette then PreparePalette(); | 
|---|
|  | 3212 |  | 
|---|
|  | 3213 | {Copy image width and height} | 
|---|
|  | 3214 | ImageWidth := Header.Width; | 
|---|
|  | 3215 | ImageHeight := Header.Height; | 
|---|
|  | 3216 |  | 
|---|
|  | 3217 | {Initialize to calculate CRC} | 
|---|
|  | 3218 | {$IFDEF CheckCRC} | 
|---|
|  | 3219 | CRCFile := update_crc($ffffffff, @ChunkName[0], 4); | 
|---|
|  | 3220 | {$ENDIF} | 
|---|
|  | 3221 |  | 
|---|
|  | 3222 | Owner.GetPixelInfo(Row_Bytes, Offset); {Obtain line information} | 
|---|
|  | 3223 | ZLIBStream := ZLIBInitInflate(Stream);  {Initializes decompression} | 
|---|
|  | 3224 |  | 
|---|
|  | 3225 | {Calculate ending position for the current IDAT chunk} | 
|---|
|  | 3226 | EndPos := Stream.Position + Size; | 
|---|
|  | 3227 |  | 
|---|
|  | 3228 | {Allocate memory} | 
|---|
|  | 3229 | GetMem(Row_Buffer[false], Row_Bytes + 1); | 
|---|
|  | 3230 | GetMem(Row_Buffer[true], Row_Bytes + 1); | 
|---|
|  | 3231 | ZeroMemory(Row_Buffer[false], Row_bytes + 1); | 
|---|
|  | 3232 | {Set the variable to alternate the Row_Buffer item to use} | 
|---|
|  | 3233 | RowUsed := TRUE; | 
|---|
|  | 3234 |  | 
|---|
|  | 3235 | {Call special methods for the different interlace methods} | 
|---|
|  | 3236 | case Owner.InterlaceMethod of | 
|---|
|  | 3237 | imNone:  DecodeNonInterlaced(stream, ZLIBStream, Size, crcfile); | 
|---|
|  | 3238 | imAdam7: DecodeInterlacedAdam7(stream, ZLIBStream, size, crcfile); | 
|---|
|  | 3239 | end; | 
|---|
|  | 3240 |  | 
|---|
|  | 3241 | {Free memory} | 
|---|
|  | 3242 | ZLIBTerminateInflate(ZLIBStream); {Terminates decompression} | 
|---|
|  | 3243 | FreeMem(Row_Buffer[False], Row_Bytes + 1); | 
|---|
|  | 3244 | FreeMem(Row_Buffer[True], Row_Bytes + 1); | 
|---|
|  | 3245 |  | 
|---|
|  | 3246 | {Now checks CRC} | 
|---|
|  | 3247 | Stream.Read(CRCCheck, 4); | 
|---|
|  | 3248 | {$IFDEF CheckCRC} | 
|---|
|  | 3249 | CRCFile := CRCFile xor $ffffffff; | 
|---|
|  | 3250 | CRCCheck := ByteSwap(CRCCheck); | 
|---|
|  | 3251 | Result := CRCCheck = CRCFile; | 
|---|
|  | 3252 |  | 
|---|
|  | 3253 | {Handle CRC error} | 
|---|
|  | 3254 | if not Result then | 
|---|
|  | 3255 | begin | 
|---|
|  | 3256 | {In case it coult not load chunk} | 
|---|
|  | 3257 | Owner.RaiseError(EPngInvalidCRC, EPngInvalidCRCText); | 
|---|
|  | 3258 | exit; | 
|---|
|  | 3259 | end; | 
|---|
|  | 3260 | {$ELSE}Result := TRUE; {$ENDIF} | 
|---|
|  | 3261 | end; | 
|---|
|  | 3262 |  | 
|---|
|  | 3263 | const | 
|---|
|  | 3264 | IDATHeader: Array[0..3] of char = ('I', 'D', 'A', 'T'); | 
|---|
|  | 3265 | BUFFER = 5; | 
|---|
|  | 3266 |  | 
|---|
|  | 3267 | {Saves the IDAT chunk to a stream} | 
|---|
|  | 3268 | function TChunkIDAT.SaveToStream(Stream: TStream): Boolean; | 
|---|
|  | 3269 | var | 
|---|
|  | 3270 | ZLIBStream : TZStreamRec2; | 
|---|
|  | 3271 | begin | 
|---|
|  | 3272 | {Get pointer to the header chunk} | 
|---|
|  | 3273 | Header := Owner.Chunks.Item[0] as TChunkIHDR; | 
|---|
|  | 3274 | {Copy image width and height} | 
|---|
|  | 3275 | ImageWidth := Header.Width; | 
|---|
|  | 3276 | ImageHeight := Header.Height; | 
|---|
|  | 3277 | Owner.GetPixelInfo(Row_Bytes, Offset); {Obtain line information} | 
|---|
|  | 3278 |  | 
|---|
|  | 3279 | {Allocate memory} | 
|---|
|  | 3280 | GetMem(Encode_Buffer[BUFFER], Row_Bytes); | 
|---|
|  | 3281 | ZeroMemory(Encode_Buffer[BUFFER], Row_Bytes); | 
|---|
|  | 3282 | {Allocate buffers for the filters selected} | 
|---|
|  | 3283 | {Filter none will always be calculated to the other filters to work} | 
|---|
|  | 3284 | GetMem(Encode_Buffer[FILTER_NONE], Row_Bytes); | 
|---|
|  | 3285 | ZeroMemory(Encode_Buffer[FILTER_NONE], Row_Bytes); | 
|---|
|  | 3286 | if pfSub in Owner.Filters then | 
|---|
|  | 3287 | GetMem(Encode_Buffer[FILTER_SUB], Row_Bytes); | 
|---|
|  | 3288 | if pfUp in Owner.Filters then | 
|---|
|  | 3289 | GetMem(Encode_Buffer[FILTER_UP], Row_Bytes); | 
|---|
|  | 3290 | if pfAverage in Owner.Filters then | 
|---|
|  | 3291 | GetMem(Encode_Buffer[FILTER_AVERAGE], Row_Bytes); | 
|---|
|  | 3292 | if pfPaeth in Owner.Filters then | 
|---|
|  | 3293 | GetMem(Encode_Buffer[FILTER_PAETH], Row_Bytes); | 
|---|
|  | 3294 |  | 
|---|
|  | 3295 | {Initialize ZLIB} | 
|---|
|  | 3296 | ZLIBStream := ZLIBInitDeflate(Stream, Owner.fCompressionLevel, | 
|---|
|  | 3297 | Owner.MaxIdatSize); | 
|---|
|  | 3298 | {Write data depending on the interlace method} | 
|---|
|  | 3299 | case Owner.InterlaceMethod of | 
|---|
|  | 3300 | imNone: EncodeNonInterlaced(stream, ZLIBStream); | 
|---|
|  | 3301 | imAdam7: EncodeInterlacedAdam7(stream, ZLIBStream); | 
|---|
|  | 3302 | end; | 
|---|
|  | 3303 | {Terminates ZLIB} | 
|---|
|  | 3304 | ZLIBTerminateDeflate(ZLIBStream); | 
|---|
|  | 3305 |  | 
|---|
|  | 3306 | {Release allocated memory} | 
|---|
|  | 3307 | FreeMem(Encode_Buffer[BUFFER], Row_Bytes); | 
|---|
|  | 3308 | FreeMem(Encode_Buffer[FILTER_NONE], Row_Bytes); | 
|---|
|  | 3309 | if pfSub in Owner.Filters then | 
|---|
|  | 3310 | FreeMem(Encode_Buffer[FILTER_SUB], Row_Bytes); | 
|---|
|  | 3311 | if pfUp in Owner.Filters then | 
|---|
|  | 3312 | FreeMem(Encode_Buffer[FILTER_UP], Row_Bytes); | 
|---|
|  | 3313 | if pfAverage in Owner.Filters then | 
|---|
|  | 3314 | FreeMem(Encode_Buffer[FILTER_AVERAGE], Row_Bytes); | 
|---|
|  | 3315 | if pfPaeth in Owner.Filters then | 
|---|
|  | 3316 | FreeMem(Encode_Buffer[FILTER_PAETH], Row_Bytes); | 
|---|
|  | 3317 |  | 
|---|
|  | 3318 | {Everything went ok} | 
|---|
|  | 3319 | Result := True; | 
|---|
|  | 3320 | end; | 
|---|
|  | 3321 |  | 
|---|
|  | 3322 | {Writes the IDAT using the settings} | 
|---|
|  | 3323 | procedure WriteIDAT(Stream: TStream; Data: Pointer; const Length: Cardinal); | 
|---|
|  | 3324 | var | 
|---|
|  | 3325 | ChunkLen, CRC: Cardinal; | 
|---|
|  | 3326 | begin | 
|---|
|  | 3327 | {Writes IDAT header} | 
|---|
|  | 3328 | ChunkLen := ByteSwap(Length); | 
|---|
|  | 3329 | Stream.Write(ChunkLen, 4);                      {Chunk length} | 
|---|
|  | 3330 | Stream.Write(IDATHeader[0], 4);                 {Idat header} | 
|---|
|  | 3331 | CRC := update_crc($ffffffff, @IDATHeader[0], 4); {Crc part for header} | 
|---|
|  | 3332 |  | 
|---|
|  | 3333 | {Writes IDAT data and calculates CRC for data} | 
|---|
|  | 3334 | Stream.Write(Data^, Length); | 
|---|
|  | 3335 | CRC := Byteswap(update_crc(CRC, Data, Length) xor $ffffffff); | 
|---|
|  | 3336 | {Writes final CRC} | 
|---|
|  | 3337 | Stream.Write(CRC, 4); | 
|---|
|  | 3338 | end; | 
|---|
|  | 3339 |  | 
|---|
|  | 3340 | {Compress and writes IDAT chunk data} | 
|---|
|  | 3341 | procedure TChunkIDAT.IDATZlibWrite(var ZLIBStream: TZStreamRec2; | 
|---|
|  | 3342 | Buffer: Pointer; const Length: Cardinal); | 
|---|
|  | 3343 | begin | 
|---|
|  | 3344 | with ZLIBStream, ZLIBStream.ZLIB do | 
|---|
|  | 3345 | begin | 
|---|
|  | 3346 | {Set data to be compressed} | 
|---|
|  | 3347 | next_in := Buffer; | 
|---|
|  | 3348 | avail_in := Length; | 
|---|
|  | 3349 |  | 
|---|
|  | 3350 | {Compress all the data avaliable to compress} | 
|---|
|  | 3351 | while avail_in > 0 do | 
|---|
|  | 3352 | begin | 
|---|
|  | 3353 | deflate(ZLIB, Z_NO_FLUSH); | 
|---|
|  | 3354 |  | 
|---|
|  | 3355 | {The whole buffer was used, save data to stream and restore buffer} | 
|---|
|  | 3356 | if avail_out = 0 then | 
|---|
|  | 3357 | begin | 
|---|
|  | 3358 | {Writes this IDAT chunk} | 
|---|
|  | 3359 | WriteIDAT(fStream, Data, Owner.MaxIdatSize); | 
|---|
|  | 3360 |  | 
|---|
|  | 3361 | {Restore buffer} | 
|---|
|  | 3362 | next_out := Data; | 
|---|
|  | 3363 | avail_out := Owner.MaxIdatSize; | 
|---|
|  | 3364 | end {if avail_out = 0}; | 
|---|
|  | 3365 |  | 
|---|
|  | 3366 | end {while avail_in}; | 
|---|
|  | 3367 |  | 
|---|
|  | 3368 | end {with ZLIBStream, ZLIBStream.ZLIB} | 
|---|
|  | 3369 | end; | 
|---|
|  | 3370 |  | 
|---|
|  | 3371 | {Finishes compressing data to write IDAT chunk} | 
|---|
|  | 3372 | procedure TChunkIDAT.FinishIDATZlib(var ZLIBStream: TZStreamRec2); | 
|---|
|  | 3373 | begin | 
|---|
|  | 3374 | with ZLIBStream, ZLIBStream.ZLIB do | 
|---|
|  | 3375 | begin | 
|---|
|  | 3376 | {Set data to be compressed} | 
|---|
|  | 3377 | next_in := nil; | 
|---|
|  | 3378 | avail_in := 0; | 
|---|
|  | 3379 |  | 
|---|
|  | 3380 | while deflate(ZLIB,Z_FINISH) <> Z_STREAM_END do | 
|---|
|  | 3381 | begin | 
|---|
|  | 3382 | {Writes this IDAT chunk} | 
|---|
|  | 3383 | WriteIDAT(fStream, Data, Owner.MaxIdatSize - avail_out); | 
|---|
|  | 3384 | {Re-update buffer} | 
|---|
|  | 3385 | next_out := Data; | 
|---|
|  | 3386 | avail_out := Owner.MaxIdatSize; | 
|---|
|  | 3387 | end; | 
|---|
|  | 3388 |  | 
|---|
|  | 3389 | if avail_out < Owner.MaxIdatSize then | 
|---|
|  | 3390 | {Writes final IDAT} | 
|---|
|  | 3391 | WriteIDAT(fStream, Data, Owner.MaxIdatSize - avail_out); | 
|---|
|  | 3392 |  | 
|---|
|  | 3393 | end {with ZLIBStream, ZLIBStream.ZLIB}; | 
|---|
|  | 3394 | end; | 
|---|
|  | 3395 |  | 
|---|
|  | 3396 | {Copy memory to encode RGB image with 1 byte for each color sample} | 
|---|
|  | 3397 | procedure TChunkIDAT.EncodeNonInterlacedRGB8(Src, Dest, Trans: pChar); | 
|---|
|  | 3398 | var | 
|---|
|  | 3399 | I: Integer; | 
|---|
|  | 3400 | begin | 
|---|
|  | 3401 | FOR I := 1 TO ImageWidth DO | 
|---|
|  | 3402 | begin | 
|---|
|  | 3403 | {Copy pixel values} | 
|---|
|  | 3404 | Byte(Dest^) := fOwner.InverseGamma[pByte(Longint(Src) + 2)^]; inc(Dest); | 
|---|
|  | 3405 | Byte(Dest^) := fOwner.InverseGamma[pByte(Longint(Src) + 1)^]; inc(Dest); | 
|---|
|  | 3406 | Byte(Dest^) := fOwner.InverseGamma[pByte(Longint(Src)    )^]; inc(Dest); | 
|---|
|  | 3407 | {Move to next pixel} | 
|---|
|  | 3408 | inc(Src, 3); | 
|---|
|  | 3409 | end {for I} | 
|---|
|  | 3410 | end; | 
|---|
|  | 3411 |  | 
|---|
|  | 3412 | {Copy memory to encode RGB images with 16 bits for each color sample} | 
|---|
|  | 3413 | procedure TChunkIDAT.EncodeNonInterlacedRGB16(Src, Dest, Trans: pChar); | 
|---|
|  | 3414 | var | 
|---|
|  | 3415 | I: Integer; | 
|---|
|  | 3416 | begin | 
|---|
|  | 3417 | FOR I := 1 TO ImageWidth DO | 
|---|
|  | 3418 | begin | 
|---|
|  | 3419 | //Now we copy from 1 byte for each sample stored to a 2 bytes (or 1 word) | 
|---|
|  | 3420 | //for sample | 
|---|
|  | 3421 | {Copy pixel values} | 
|---|
|  | 3422 | pWORD(Dest)^ := fOwner.InverseGamma[pByte(Longint(Src) + 2)^]; inc(Dest, 2); | 
|---|
|  | 3423 | pWORD(Dest)^ := fOwner.InverseGamma[pByte(Longint(Src) + 1)^]; inc(Dest, 2); | 
|---|
|  | 3424 | pWORD(Dest)^ := fOwner.InverseGamma[pByte(Longint(Src)    )^]; inc(Dest, 2); | 
|---|
|  | 3425 | {Move to next pixel} | 
|---|
|  | 3426 | inc(Src, 3); | 
|---|
|  | 3427 | end {for I} | 
|---|
|  | 3428 |  | 
|---|
|  | 3429 | end; | 
|---|
|  | 3430 |  | 
|---|
|  | 3431 | {Copy memory to encode types using palettes (1, 4 or 8 bits per pixel)} | 
|---|
|  | 3432 | procedure TChunkIDAT.EncodeNonInterlacedPalette148(Src, Dest, Trans: pChar); | 
|---|
|  | 3433 | begin | 
|---|
|  | 3434 | {It's simple as copying the data} | 
|---|
|  | 3435 | CopyMemory(Dest, Src, Row_Bytes); | 
|---|
|  | 3436 | end; | 
|---|
|  | 3437 |  | 
|---|
|  | 3438 | {Copy memory to encode grayscale images with 2 bytes for each sample} | 
|---|
|  | 3439 | procedure TChunkIDAT.EncodeNonInterlacedGrayscale16(Src, Dest, Trans: pChar); | 
|---|
|  | 3440 | var | 
|---|
|  | 3441 | I: Integer; | 
|---|
|  | 3442 | begin | 
|---|
|  | 3443 | FOR I := 1 TO ImageWidth DO | 
|---|
|  | 3444 | begin | 
|---|
|  | 3445 | //Now we copy from 1 byte for each sample stored to a 2 bytes (or 1 word) | 
|---|
|  | 3446 | //for sample | 
|---|
|  | 3447 | pWORD(Dest)^ := pByte(Longint(Src))^; inc(Dest, 2); | 
|---|
|  | 3448 | {Move to next pixel} | 
|---|
|  | 3449 | inc(Src); | 
|---|
|  | 3450 | end {for I} | 
|---|
|  | 3451 | end; | 
|---|
|  | 3452 |  | 
|---|
|  | 3453 | {Encode images using RGB followed by an alpha value using 1 byte for each} | 
|---|
|  | 3454 | procedure TChunkIDAT.EncodeNonInterlacedRGBAlpha8(Src, Dest, Trans: pChar); | 
|---|
|  | 3455 | var | 
|---|
|  | 3456 | i: Integer; | 
|---|
|  | 3457 | begin | 
|---|
|  | 3458 | {Copy the data to the destination, including data from Trans pointer} | 
|---|
|  | 3459 | FOR i := 1 TO ImageWidth do | 
|---|
|  | 3460 | begin | 
|---|
|  | 3461 | Byte(Dest^) := Owner.InverseGamma[PByte(Longint(Src) + 2)^]; inc(Dest); | 
|---|
|  | 3462 | Byte(Dest^) := Owner.InverseGamma[PByte(Longint(Src) + 1)^]; inc(Dest); | 
|---|
|  | 3463 | Byte(Dest^) := Owner.InverseGamma[PByte(Longint(Src)    )^]; inc(Dest); | 
|---|
|  | 3464 | Dest^ := Trans^; inc(Dest); | 
|---|
|  | 3465 | inc(Src, 3); inc(Trans); | 
|---|
|  | 3466 | end {for i}; | 
|---|
|  | 3467 | end; | 
|---|
|  | 3468 |  | 
|---|
|  | 3469 | {Encode images using RGB followed by an alpha value using 2 byte for each} | 
|---|
|  | 3470 | procedure TChunkIDAT.EncodeNonInterlacedRGBAlpha16(Src, Dest, Trans: pChar); | 
|---|
|  | 3471 | var | 
|---|
|  | 3472 | i: Integer; | 
|---|
|  | 3473 | begin | 
|---|
|  | 3474 | {Copy the data to the destination, including data from Trans pointer} | 
|---|
|  | 3475 | FOR i := 1 TO ImageWidth do | 
|---|
|  | 3476 | begin | 
|---|
|  | 3477 | pWord(Dest)^ := Owner.InverseGamma[PByte(Longint(Src) + 2)^]; inc(Dest, 2); | 
|---|
|  | 3478 | pWord(Dest)^ := Owner.InverseGamma[PByte(Longint(Src) + 1)^]; inc(Dest, 2); | 
|---|
|  | 3479 | pWord(Dest)^ := Owner.InverseGamma[PByte(Longint(Src)    )^]; inc(Dest, 2); | 
|---|
|  | 3480 | pWord(Dest)^ := PByte(Longint(Trans)  )^; inc(Dest, 2); | 
|---|
|  | 3481 | inc(Src, 3); inc(Trans); | 
|---|
|  | 3482 | end {for i}; | 
|---|
|  | 3483 | end; | 
|---|
|  | 3484 |  | 
|---|
|  | 3485 | {Encode grayscale images followed by an alpha value using 1 byte for each} | 
|---|
|  | 3486 | procedure TChunkIDAT.EncodeNonInterlacedGrayscaleAlpha8( | 
|---|
|  | 3487 | Src, Dest, Trans: pChar); | 
|---|
|  | 3488 | var | 
|---|
|  | 3489 | i: Integer; | 
|---|
|  | 3490 | begin | 
|---|
|  | 3491 | {Copy the data to the destination, including data from Trans pointer} | 
|---|
|  | 3492 | FOR i := 1 TO ImageWidth do | 
|---|
|  | 3493 | begin | 
|---|
|  | 3494 | Dest^ := Src^; inc(Dest); | 
|---|
|  | 3495 | Dest^ := Trans^; inc(Dest); | 
|---|
|  | 3496 | inc(Src); inc(Trans); | 
|---|
|  | 3497 | end {for i}; | 
|---|
|  | 3498 | end; | 
|---|
|  | 3499 |  | 
|---|
|  | 3500 | {Encode grayscale images followed by an alpha value using 2 byte for each} | 
|---|
|  | 3501 | procedure TChunkIDAT.EncodeNonInterlacedGrayscaleAlpha16( | 
|---|
|  | 3502 | Src, Dest, Trans: pChar); | 
|---|
|  | 3503 | var | 
|---|
|  | 3504 | i: Integer; | 
|---|
|  | 3505 | begin | 
|---|
|  | 3506 | {Copy the data to the destination, including data from Trans pointer} | 
|---|
|  | 3507 | FOR i := 1 TO ImageWidth do | 
|---|
|  | 3508 | begin | 
|---|
|  | 3509 | pWord(Dest)^ := pByte(Src)^;    inc(Dest, 2); | 
|---|
|  | 3510 | pWord(Dest)^ := pByte(Trans)^;  inc(Dest, 2); | 
|---|
|  | 3511 | inc(Src); inc(Trans); | 
|---|
|  | 3512 | end {for i}; | 
|---|
|  | 3513 | end; | 
|---|
|  | 3514 |  | 
|---|
|  | 3515 | {Encode non interlaced images} | 
|---|
|  | 3516 | procedure TChunkIDAT.EncodeNonInterlaced(Stream: TStream; | 
|---|
|  | 3517 | var ZLIBStream: TZStreamRec2); | 
|---|
|  | 3518 | var | 
|---|
|  | 3519 | {Current line} | 
|---|
|  | 3520 | j: Cardinal; | 
|---|
|  | 3521 | {Pointers to image data} | 
|---|
|  | 3522 | Data, Trans: PChar; | 
|---|
|  | 3523 | {Filter used for this line} | 
|---|
|  | 3524 | Filter: Byte; | 
|---|
|  | 3525 | {Method which will copy the data into the buffer} | 
|---|
|  | 3526 | CopyProc: procedure(Src, Dest, Trans: pChar) of object; | 
|---|
|  | 3527 | begin | 
|---|
|  | 3528 | CopyProc := nil;  {Initialize to avoid warnings} | 
|---|
|  | 3529 | {Defines the method to copy the data to the buffer depending on} | 
|---|
|  | 3530 | {the image parameters} | 
|---|
|  | 3531 | case Header.ColorType of | 
|---|
|  | 3532 | {R, G, B values} | 
|---|
|  | 3533 | COLOR_RGB: | 
|---|
|  | 3534 | case Header.BitDepth of | 
|---|
|  | 3535 | 8: CopyProc := EncodeNonInterlacedRGB8; | 
|---|
|  | 3536 | 16: CopyProc := EncodeNonInterlacedRGB16; | 
|---|
|  | 3537 | end; | 
|---|
|  | 3538 | {Palette and grayscale values} | 
|---|
|  | 3539 | COLOR_GRAYSCALE, COLOR_PALETTE: | 
|---|
|  | 3540 | case Header.BitDepth of | 
|---|
|  | 3541 | 1, 4, 8: CopyProc := EncodeNonInterlacedPalette148; | 
|---|
|  | 3542 | 16: CopyProc := EncodeNonInterlacedGrayscale16; | 
|---|
|  | 3543 | end; | 
|---|
|  | 3544 | {RGB with a following alpha value} | 
|---|
|  | 3545 | COLOR_RGBALPHA: | 
|---|
|  | 3546 | case Header.BitDepth of | 
|---|
|  | 3547 | 8: CopyProc := EncodeNonInterlacedRGBAlpha8; | 
|---|
|  | 3548 | 16: CopyProc := EncodeNonInterlacedRGBAlpha16; | 
|---|
|  | 3549 | end; | 
|---|
|  | 3550 | {Grayscale images followed by an alpha} | 
|---|
|  | 3551 | COLOR_GRAYSCALEALPHA: | 
|---|
|  | 3552 | case Header.BitDepth of | 
|---|
|  | 3553 | 8:  CopyProc := EncodeNonInterlacedGrayscaleAlpha8; | 
|---|
|  | 3554 | 16:  CopyProc := EncodeNonInterlacedGrayscaleAlpha16; | 
|---|
|  | 3555 | end; | 
|---|
|  | 3556 | end {case Header.ColorType}; | 
|---|
|  | 3557 |  | 
|---|
|  | 3558 | {Get the image data pointer} | 
|---|
|  | 3559 | Longint(Data) := Longint(Header.ImageData) + | 
|---|
|  | 3560 | Header.BytesPerRow * (ImageHeight - 1); | 
|---|
|  | 3561 | Trans := Header.ImageAlpha; | 
|---|
|  | 3562 |  | 
|---|
|  | 3563 | {Writes each line} | 
|---|
|  | 3564 | FOR j := 0 to ImageHeight - 1 do | 
|---|
|  | 3565 | begin | 
|---|
|  | 3566 | {Copy data into buffer} | 
|---|
|  | 3567 | CopyProc(Data, @Encode_Buffer[BUFFER][0], Trans); | 
|---|
|  | 3568 | {Filter data} | 
|---|
|  | 3569 | Filter := FilterToEncode; | 
|---|
|  | 3570 |  | 
|---|
|  | 3571 | {Compress data} | 
|---|
|  | 3572 | IDATZlibWrite(ZLIBStream, @Filter, 1); | 
|---|
|  | 3573 | IDATZlibWrite(ZLIBStream, @Encode_Buffer[Filter][0], Row_Bytes); | 
|---|
|  | 3574 |  | 
|---|
|  | 3575 | {Adjust pointers to the actual image data} | 
|---|
|  | 3576 | dec(Data, Header.BytesPerRow); | 
|---|
|  | 3577 | inc(Trans, ImageWidth); | 
|---|
|  | 3578 | end; | 
|---|
|  | 3579 |  | 
|---|
|  | 3580 | {Compress and finishes copying the remaining data} | 
|---|
|  | 3581 | FinishIDATZlib(ZLIBStream); | 
|---|
|  | 3582 | end; | 
|---|
|  | 3583 |  | 
|---|
|  | 3584 | {Copy memory to encode interlaced images using RGB value with 1 byte for} | 
|---|
|  | 3585 | {each color sample} | 
|---|
|  | 3586 | procedure TChunkIDAT.EncodeInterlacedRGB8(const Pass: Byte; | 
|---|
|  | 3587 | Src, Dest, Trans: pChar); | 
|---|
|  | 3588 | var | 
|---|
|  | 3589 | Col: Integer; | 
|---|
|  | 3590 | begin | 
|---|
|  | 3591 | {Get first column and enter in loop} | 
|---|
|  | 3592 | Col := ColumnStart[Pass]; | 
|---|
|  | 3593 | Src := pChar(Longint(Src) + Col * 3); | 
|---|
|  | 3594 | repeat | 
|---|
|  | 3595 | {Copy this row} | 
|---|
|  | 3596 | Byte(Dest^) := fOwner.InverseGamma[pByte(Longint(Src) + 2)^]; inc(Dest); | 
|---|
|  | 3597 | Byte(Dest^) := fOwner.InverseGamma[pByte(Longint(Src) + 1)^]; inc(Dest); | 
|---|
|  | 3598 | Byte(Dest^) := fOwner.InverseGamma[pByte(Longint(Src)    )^]; inc(Dest); | 
|---|
|  | 3599 |  | 
|---|
|  | 3600 | {Move to next column} | 
|---|
|  | 3601 | inc(Src, ColumnIncrement[Pass] * 3); | 
|---|
|  | 3602 | inc(Col, ColumnIncrement[Pass]); | 
|---|
|  | 3603 | until Col >= ImageWidth; | 
|---|
|  | 3604 | end; | 
|---|
|  | 3605 |  | 
|---|
|  | 3606 | {Copy memory to encode interlaced RGB images with 2 bytes each color sample} | 
|---|
|  | 3607 | procedure TChunkIDAT.EncodeInterlacedRGB16(const Pass: Byte; | 
|---|
|  | 3608 | Src, Dest, Trans: pChar); | 
|---|
|  | 3609 | var | 
|---|
|  | 3610 | Col: Integer; | 
|---|
|  | 3611 | begin | 
|---|
|  | 3612 | {Get first column and enter in loop} | 
|---|
|  | 3613 | Col := ColumnStart[Pass]; | 
|---|
|  | 3614 | Src := pChar(Longint(Src) + Col * 3); | 
|---|
|  | 3615 | repeat | 
|---|
|  | 3616 | {Copy this row} | 
|---|
|  | 3617 | pWord(Dest)^ := Owner.InverseGamma[pByte(Longint(Src) + 2)^]; inc(Dest, 2); | 
|---|
|  | 3618 | pWord(Dest)^ := Owner.InverseGamma[pByte(Longint(Src) + 1)^]; inc(Dest, 2); | 
|---|
|  | 3619 | pWord(Dest)^ := Owner.InverseGamma[pByte(Longint(Src)    )^]; inc(Dest, 2); | 
|---|
|  | 3620 |  | 
|---|
|  | 3621 | {Move to next column} | 
|---|
|  | 3622 | inc(Src, ColumnIncrement[Pass] * 3); | 
|---|
|  | 3623 | inc(Col, ColumnIncrement[Pass]); | 
|---|
|  | 3624 | until Col >= ImageWidth; | 
|---|
|  | 3625 | end; | 
|---|
|  | 3626 |  | 
|---|
|  | 3627 | {Copy memory to encode interlaced images using palettes using bit depths} | 
|---|
|  | 3628 | {1, 4, 8 (each pixel in the image)} | 
|---|
|  | 3629 | procedure TChunkIDAT.EncodeInterlacedPalette148(const Pass: Byte; | 
|---|
|  | 3630 | Src, Dest, Trans: pChar); | 
|---|
|  | 3631 | const | 
|---|
|  | 3632 | BitTable: Array[1..8] of Integer = ($1, $3, 0, $F, 0, 0, 0, $FF); | 
|---|
|  | 3633 | StartBit: Array[1..8] of Integer = (7 , 0 , 0, 4,  0, 0, 0, 0); | 
|---|
|  | 3634 | var | 
|---|
|  | 3635 | CurBit, Col: Integer; | 
|---|
|  | 3636 | Src2: PChar; | 
|---|
|  | 3637 | begin | 
|---|
|  | 3638 | {Clean the line} | 
|---|
|  | 3639 | fillchar(Dest^, Row_Bytes, #0); | 
|---|
|  | 3640 | {Get first column and enter in loop} | 
|---|
|  | 3641 | Col := ColumnStart[Pass]; | 
|---|
|  | 3642 | with Header.BitmapInfo.bmiHeader do | 
|---|
|  | 3643 | repeat | 
|---|
|  | 3644 | {Copy data} | 
|---|
|  | 3645 | CurBit := StartBit[biBitCount]; | 
|---|
|  | 3646 | repeat | 
|---|
|  | 3647 | {Adjust pointer to pixel byte bounds} | 
|---|
|  | 3648 | Src2 := pChar(Longint(Src) + (biBitCount * Col) div 8); | 
|---|
|  | 3649 | {Copy data} | 
|---|
|  | 3650 | Byte(Dest^) := Byte(Dest^) or | 
|---|
|  | 3651 | (((Byte(Src2^) shr (StartBit[Header.BitDepth] - (biBitCount * Col) | 
|---|
|  | 3652 | mod 8))) and (BitTable[biBitCount])) shl CurBit; | 
|---|
|  | 3653 |  | 
|---|
|  | 3654 | {Move to next column} | 
|---|
|  | 3655 | inc(Col, ColumnIncrement[Pass]); | 
|---|
|  | 3656 | {Will read next bits} | 
|---|
|  | 3657 | dec(CurBit, biBitCount); | 
|---|
|  | 3658 | until CurBit < 0; | 
|---|
|  | 3659 |  | 
|---|
|  | 3660 | {Move to next byte in source} | 
|---|
|  | 3661 | inc(Dest); | 
|---|
|  | 3662 | until Col >= ImageWidth; | 
|---|
|  | 3663 | end; | 
|---|
|  | 3664 |  | 
|---|
|  | 3665 | {Copy to encode interlaced grayscale images using 16 bits for each sample} | 
|---|
|  | 3666 | procedure TChunkIDAT.EncodeInterlacedGrayscale16(const Pass: Byte; | 
|---|
|  | 3667 | Src, Dest, Trans: pChar); | 
|---|
|  | 3668 | var | 
|---|
|  | 3669 | Col: Integer; | 
|---|
|  | 3670 | begin | 
|---|
|  | 3671 | {Get first column and enter in loop} | 
|---|
|  | 3672 | Col := ColumnStart[Pass]; | 
|---|
|  | 3673 | Src := pChar(Longint(Src) + Col); | 
|---|
|  | 3674 | repeat | 
|---|
|  | 3675 | {Copy this row} | 
|---|
|  | 3676 | pWord(Dest)^ := Byte(Src^); inc(Dest, 2); | 
|---|
|  | 3677 |  | 
|---|
|  | 3678 | {Move to next column} | 
|---|
|  | 3679 | inc(Src, ColumnIncrement[Pass]); | 
|---|
|  | 3680 | inc(Col, ColumnIncrement[Pass]); | 
|---|
|  | 3681 | until Col >= ImageWidth; | 
|---|
|  | 3682 | end; | 
|---|
|  | 3683 |  | 
|---|
|  | 3684 | {Copy to encode interlaced rgb images followed by an alpha value, all using} | 
|---|
|  | 3685 | {one byte for each sample} | 
|---|
|  | 3686 | procedure TChunkIDAT.EncodeInterlacedRGBAlpha8(const Pass: Byte; | 
|---|
|  | 3687 | Src, Dest, Trans: pChar); | 
|---|
|  | 3688 | var | 
|---|
|  | 3689 | Col: Integer; | 
|---|
|  | 3690 | begin | 
|---|
|  | 3691 | {Get first column and enter in loop} | 
|---|
|  | 3692 | Col := ColumnStart[Pass]; | 
|---|
|  | 3693 | Src := pChar(Longint(Src) + Col * 3); | 
|---|
|  | 3694 | Trans := pChar(Longint(Trans) + Col); | 
|---|
|  | 3695 | repeat | 
|---|
|  | 3696 | {Copy this row} | 
|---|
|  | 3697 | Byte(Dest^) := Owner.InverseGamma[pByte(Longint(Src) + 2)^]; inc(Dest); | 
|---|
|  | 3698 | Byte(Dest^) := Owner.InverseGamma[pByte(Longint(Src) + 1)^]; inc(Dest); | 
|---|
|  | 3699 | Byte(Dest^) := Owner.InverseGamma[pByte(Longint(Src)    )^]; inc(Dest); | 
|---|
|  | 3700 | Dest^ := Trans^; inc(Dest); | 
|---|
|  | 3701 |  | 
|---|
|  | 3702 | {Move to next column} | 
|---|
|  | 3703 | inc(Src, ColumnIncrement[Pass] * 3); | 
|---|
|  | 3704 | inc(Trans, ColumnIncrement[Pass]); | 
|---|
|  | 3705 | inc(Col, ColumnIncrement[Pass]); | 
|---|
|  | 3706 | until Col >= ImageWidth; | 
|---|
|  | 3707 | end; | 
|---|
|  | 3708 |  | 
|---|
|  | 3709 | {Copy to encode interlaced rgb images followed by an alpha value, all using} | 
|---|
|  | 3710 | {two byte for each sample} | 
|---|
|  | 3711 | procedure TChunkIDAT.EncodeInterlacedRGBAlpha16(const Pass: Byte; | 
|---|
|  | 3712 | Src, Dest, Trans: pChar); | 
|---|
|  | 3713 | var | 
|---|
|  | 3714 | Col: Integer; | 
|---|
|  | 3715 | begin | 
|---|
|  | 3716 | {Get first column and enter in loop} | 
|---|
|  | 3717 | Col := ColumnStart[Pass]; | 
|---|
|  | 3718 | Src := pChar(Longint(Src) + Col * 3); | 
|---|
|  | 3719 | Trans := pChar(Longint(Trans) + Col); | 
|---|
|  | 3720 | repeat | 
|---|
|  | 3721 | {Copy this row} | 
|---|
|  | 3722 | pWord(Dest)^ := pByte(Longint(Src) + 2)^; inc(Dest, 2); | 
|---|
|  | 3723 | pWord(Dest)^ := pByte(Longint(Src) + 1)^; inc(Dest, 2); | 
|---|
|  | 3724 | pWord(Dest)^ := pByte(Longint(Src)    )^; inc(Dest, 2); | 
|---|
|  | 3725 | pWord(Dest)^ := pByte(Trans)^; inc(Dest, 2); | 
|---|
|  | 3726 |  | 
|---|
|  | 3727 | {Move to next column} | 
|---|
|  | 3728 | inc(Src, ColumnIncrement[Pass] * 3); | 
|---|
|  | 3729 | inc(Trans, ColumnIncrement[Pass]); | 
|---|
|  | 3730 | inc(Col, ColumnIncrement[Pass]); | 
|---|
|  | 3731 | until Col >= ImageWidth; | 
|---|
|  | 3732 | end; | 
|---|
|  | 3733 |  | 
|---|
|  | 3734 | {Copy to encode grayscale interlaced images followed by an alpha value, all} | 
|---|
|  | 3735 | {using 1 byte for each sample} | 
|---|
|  | 3736 | procedure TChunkIDAT.EncodeInterlacedGrayscaleAlpha8(const Pass: Byte; | 
|---|
|  | 3737 | Src, Dest, Trans: pChar); | 
|---|
|  | 3738 | var | 
|---|
|  | 3739 | Col: Integer; | 
|---|
|  | 3740 | begin | 
|---|
|  | 3741 | {Get first column and enter in loop} | 
|---|
|  | 3742 | Col := ColumnStart[Pass]; | 
|---|
|  | 3743 | Src := pChar(Longint(Src) + Col); | 
|---|
|  | 3744 | Trans := pChar(Longint(Trans) + Col); | 
|---|
|  | 3745 | repeat | 
|---|
|  | 3746 | {Copy this row} | 
|---|
|  | 3747 | Dest^ := Src^;   inc(Dest); | 
|---|
|  | 3748 | Dest^ := Trans^; inc(Dest); | 
|---|
|  | 3749 |  | 
|---|
|  | 3750 | {Move to next column} | 
|---|
|  | 3751 | inc(Src, ColumnIncrement[Pass]); | 
|---|
|  | 3752 | inc(Trans, ColumnIncrement[Pass]); | 
|---|
|  | 3753 | inc(Col, ColumnIncrement[Pass]); | 
|---|
|  | 3754 | until Col >= ImageWidth; | 
|---|
|  | 3755 | end; | 
|---|
|  | 3756 |  | 
|---|
|  | 3757 | {Copy to encode grayscale interlaced images followed by an alpha value, all} | 
|---|
|  | 3758 | {using 2 bytes for each sample} | 
|---|
|  | 3759 | procedure TChunkIDAT.EncodeInterlacedGrayscaleAlpha16(const Pass: Byte; | 
|---|
|  | 3760 | Src, Dest, Trans: pChar); | 
|---|
|  | 3761 | var | 
|---|
|  | 3762 | Col: Integer; | 
|---|
|  | 3763 | begin | 
|---|
|  | 3764 | {Get first column and enter in loop} | 
|---|
|  | 3765 | Col := ColumnStart[Pass]; | 
|---|
|  | 3766 | Src := pChar(Longint(Src) + Col); | 
|---|
|  | 3767 | Trans := pChar(Longint(Trans) + Col); | 
|---|
|  | 3768 | repeat | 
|---|
|  | 3769 | {Copy this row} | 
|---|
|  | 3770 | pWord(Dest)^ := pByte(Src)^; inc(Dest, 2); | 
|---|
|  | 3771 | pWord(Dest)^ := pByte(Trans)^; inc(Dest, 2); | 
|---|
|  | 3772 |  | 
|---|
|  | 3773 | {Move to next column} | 
|---|
|  | 3774 | inc(Src, ColumnIncrement[Pass]); | 
|---|
|  | 3775 | inc(Trans, ColumnIncrement[Pass]); | 
|---|
|  | 3776 | inc(Col, ColumnIncrement[Pass]); | 
|---|
|  | 3777 | until Col >= ImageWidth; | 
|---|
|  | 3778 | end; | 
|---|
|  | 3779 |  | 
|---|
|  | 3780 | {Encode interlaced images} | 
|---|
|  | 3781 | procedure TChunkIDAT.EncodeInterlacedAdam7(Stream: TStream; | 
|---|
|  | 3782 | var ZLIBStream: TZStreamRec2); | 
|---|
|  | 3783 | var | 
|---|
|  | 3784 | CurrentPass, Filter: Byte; | 
|---|
|  | 3785 | PixelsThisRow: Integer; | 
|---|
|  | 3786 | CurrentRow : Integer; | 
|---|
|  | 3787 | Trans, Data: pChar; | 
|---|
|  | 3788 | CopyProc: procedure(const Pass: Byte; | 
|---|
|  | 3789 | Src, Dest, Trans: pChar) of object; | 
|---|
|  | 3790 | begin | 
|---|
|  | 3791 | CopyProc := nil;  {Initialize to avoid warnings} | 
|---|
|  | 3792 | {Defines the method to copy the data to the buffer depending on} | 
|---|
|  | 3793 | {the image parameters} | 
|---|
|  | 3794 | case Header.ColorType of | 
|---|
|  | 3795 | {R, G, B values} | 
|---|
|  | 3796 | COLOR_RGB: | 
|---|
|  | 3797 | case Header.BitDepth of | 
|---|
|  | 3798 | 8: CopyProc := EncodeInterlacedRGB8; | 
|---|
|  | 3799 | 16: CopyProc := EncodeInterlacedRGB16; | 
|---|
|  | 3800 | end; | 
|---|
|  | 3801 | {Grayscale and palette} | 
|---|
|  | 3802 | COLOR_PALETTE, COLOR_GRAYSCALE: | 
|---|
|  | 3803 | case Header.BitDepth of | 
|---|
|  | 3804 | 1, 4, 8: CopyProc := EncodeInterlacedPalette148; | 
|---|
|  | 3805 | 16: CopyProc := EncodeInterlacedGrayscale16; | 
|---|
|  | 3806 | end; | 
|---|
|  | 3807 | {RGB followed by alpha} | 
|---|
|  | 3808 | COLOR_RGBALPHA: | 
|---|
|  | 3809 | case Header.BitDepth of | 
|---|
|  | 3810 | 8: CopyProc := EncodeInterlacedRGBAlpha8; | 
|---|
|  | 3811 | 16: CopyProc := EncodeInterlacedRGBAlpha16; | 
|---|
|  | 3812 | end; | 
|---|
|  | 3813 | COLOR_GRAYSCALEALPHA: | 
|---|
|  | 3814 | {Grayscale followed by alpha} | 
|---|
|  | 3815 | case Header.BitDepth of | 
|---|
|  | 3816 | 8: CopyProc := EncodeInterlacedGrayscaleAlpha8; | 
|---|
|  | 3817 | 16: CopyProc := EncodeInterlacedGrayscaleAlpha16; | 
|---|
|  | 3818 | end; | 
|---|
|  | 3819 | end {case Header.ColorType}; | 
|---|
|  | 3820 |  | 
|---|
|  | 3821 | {Compress the image using the seven passes for ADAM 7} | 
|---|
|  | 3822 | FOR CurrentPass := 0 TO 6 DO | 
|---|
|  | 3823 | begin | 
|---|
|  | 3824 | {Calculates the number of pixels and bytes for this pass row} | 
|---|
|  | 3825 | PixelsThisRow := (ImageWidth - ColumnStart[CurrentPass] + | 
|---|
|  | 3826 | ColumnIncrement[CurrentPass] - 1) div ColumnIncrement[CurrentPass]; | 
|---|
|  | 3827 | Row_Bytes := BytesForPixels(PixelsThisRow, Header.ColorType, | 
|---|
|  | 3828 | Header.BitDepth); | 
|---|
|  | 3829 | ZeroMemory(Encode_Buffer[FILTER_NONE], Row_Bytes); | 
|---|
|  | 3830 |  | 
|---|
|  | 3831 | {Get current row index} | 
|---|
|  | 3832 | CurrentRow := RowStart[CurrentPass]; | 
|---|
|  | 3833 | {Get a pointer to the current row image data} | 
|---|
|  | 3834 | Data := Ptr(Longint(Header.ImageData) + Header.BytesPerRow * | 
|---|
|  | 3835 | (ImageHeight - 1 - CurrentRow)); | 
|---|
|  | 3836 | Trans := Ptr(Longint(Header.ImageAlpha) + ImageWidth * CurrentRow); | 
|---|
|  | 3837 |  | 
|---|
|  | 3838 | {Process all the image rows} | 
|---|
|  | 3839 | if Row_Bytes > 0 then | 
|---|
|  | 3840 | while CurrentRow < ImageHeight do | 
|---|
|  | 3841 | begin | 
|---|
|  | 3842 | {Copy data into buffer} | 
|---|
|  | 3843 | CopyProc(CurrentPass, Data, @Encode_Buffer[BUFFER][0], Trans); | 
|---|
|  | 3844 | {Filter data} | 
|---|
|  | 3845 | Filter := FilterToEncode; | 
|---|
|  | 3846 |  | 
|---|
|  | 3847 | {Compress data} | 
|---|
|  | 3848 | IDATZlibWrite(ZLIBStream, @Filter, 1); | 
|---|
|  | 3849 | IDATZlibWrite(ZLIBStream, @Encode_Buffer[Filter][0], Row_Bytes); | 
|---|
|  | 3850 |  | 
|---|
|  | 3851 | {Move to the next row} | 
|---|
|  | 3852 | inc(CurrentRow, RowIncrement[CurrentPass]); | 
|---|
|  | 3853 | {Move pointer to the next line} | 
|---|
|  | 3854 | dec(Data, RowIncrement[CurrentPass] * Header.BytesPerRow); | 
|---|
|  | 3855 | inc(Trans, RowIncrement[CurrentPass] * ImageWidth); | 
|---|
|  | 3856 | end {while CurrentRow < ImageHeight} | 
|---|
|  | 3857 |  | 
|---|
|  | 3858 | end {CurrentPass}; | 
|---|
|  | 3859 |  | 
|---|
|  | 3860 | {Compress and finishes copying the remaining data} | 
|---|
|  | 3861 | FinishIDATZlib(ZLIBStream); | 
|---|
|  | 3862 | end; | 
|---|
|  | 3863 |  | 
|---|
|  | 3864 | {Filters the row to be encoded and returns the best filter} | 
|---|
|  | 3865 | function TChunkIDAT.FilterToEncode: Byte; | 
|---|
|  | 3866 | var | 
|---|
|  | 3867 | Run, LongestRun, ii, jj: Cardinal; | 
|---|
|  | 3868 | Last, Above, LastAbove: Byte; | 
|---|
|  | 3869 | begin | 
|---|
|  | 3870 | {Selecting more filters using the Filters property from TPngObject} | 
|---|
|  | 3871 | {increases the chances to the file be much smaller, but decreases} | 
|---|
|  | 3872 | {the performace} | 
|---|
|  | 3873 |  | 
|---|
|  | 3874 | {This method will creates the same line data using the different} | 
|---|
|  | 3875 | {filter methods and select the best} | 
|---|
|  | 3876 |  | 
|---|
|  | 3877 | {Sub-filter} | 
|---|
|  | 3878 | if pfSub in Owner.Filters then | 
|---|
|  | 3879 | for ii := 0 to Row_Bytes - 1 do | 
|---|
|  | 3880 | begin | 
|---|
|  | 3881 | {There is no previous pixel when it's on the first pixel, so} | 
|---|
|  | 3882 | {set last as zero when in the first} | 
|---|
|  | 3883 | if (ii >= Offset) then | 
|---|
|  | 3884 | last := Encode_Buffer[BUFFER]^[ii - Offset] | 
|---|
|  | 3885 | else | 
|---|
|  | 3886 | last := 0; | 
|---|
|  | 3887 | Encode_Buffer[FILTER_SUB]^[ii] := Encode_Buffer[BUFFER]^[ii] - last; | 
|---|
|  | 3888 | end; | 
|---|
|  | 3889 |  | 
|---|
|  | 3890 | {Up filter} | 
|---|
|  | 3891 | if pfUp in Owner.Filters then | 
|---|
|  | 3892 | for ii := 0 to Row_Bytes - 1 do | 
|---|
|  | 3893 | Encode_Buffer[FILTER_UP]^[ii] := Encode_Buffer[BUFFER]^[ii] - | 
|---|
|  | 3894 | Encode_Buffer[FILTER_NONE]^[ii]; | 
|---|
|  | 3895 |  | 
|---|
|  | 3896 | {Average filter} | 
|---|
|  | 3897 | if pfAverage in Owner.Filters then | 
|---|
|  | 3898 | for ii := 0 to Row_Bytes - 1 do | 
|---|
|  | 3899 | begin | 
|---|
|  | 3900 | {Get the previous pixel, if the current pixel is the first, the} | 
|---|
|  | 3901 | {previous is considered to be 0} | 
|---|
|  | 3902 | if (ii >= Offset) then | 
|---|
|  | 3903 | last := Encode_Buffer[BUFFER]^[ii - Offset] | 
|---|
|  | 3904 | else | 
|---|
|  | 3905 | last := 0; | 
|---|
|  | 3906 | {Get the pixel above} | 
|---|
|  | 3907 | above := Encode_Buffer[FILTER_NONE]^[ii]; | 
|---|
|  | 3908 |  | 
|---|
|  | 3909 | {Calculates formula to the average pixel} | 
|---|
|  | 3910 | Encode_Buffer[FILTER_AVERAGE]^[ii] := Encode_Buffer[BUFFER]^[ii] - | 
|---|
|  | 3911 | (above + last) div 2 ; | 
|---|
|  | 3912 | end; | 
|---|
|  | 3913 |  | 
|---|
|  | 3914 | {Paeth filter (the slower)} | 
|---|
|  | 3915 | if pfPaeth in Owner.Filters then | 
|---|
|  | 3916 | begin | 
|---|
|  | 3917 | {Initialize} | 
|---|
|  | 3918 | last := 0; | 
|---|
|  | 3919 | lastabove := 0; | 
|---|
|  | 3920 | for ii := 0 to Row_Bytes - 1 do | 
|---|
|  | 3921 | begin | 
|---|
|  | 3922 | {In case this pixel is not the first in the line obtains the} | 
|---|
|  | 3923 | {previous one and the one above the previous} | 
|---|
|  | 3924 | if (ii >= Offset) then | 
|---|
|  | 3925 | begin | 
|---|
|  | 3926 | last := Encode_Buffer[BUFFER]^[ii - Offset]; | 
|---|
|  | 3927 | lastabove := Encode_Buffer[FILTER_NONE]^[ii - Offset]; | 
|---|
|  | 3928 | end; | 
|---|
|  | 3929 | {Obtains the pixel above} | 
|---|
|  | 3930 | above := Encode_Buffer[FILTER_NONE]^[ii]; | 
|---|
|  | 3931 | {Calculate paeth filter for this byte} | 
|---|
|  | 3932 | Encode_Buffer[FILTER_PAETH]^[ii] := Encode_Buffer[BUFFER]^[ii] - | 
|---|
|  | 3933 | PaethPredictor(last, above, lastabove); | 
|---|
|  | 3934 | end; | 
|---|
|  | 3935 | end; | 
|---|
|  | 3936 |  | 
|---|
|  | 3937 | {Now calculates the same line using no filter, which is necessary} | 
|---|
|  | 3938 | {in order to have data to the filters when the next line comes} | 
|---|
|  | 3939 | CopyMemory(@Encode_Buffer[FILTER_NONE]^[0], | 
|---|
|  | 3940 | @Encode_Buffer[BUFFER]^[0], Row_Bytes); | 
|---|
|  | 3941 |  | 
|---|
|  | 3942 | {If only filter none is selected in the filter list, we don't need} | 
|---|
|  | 3943 | {to proceed and further} | 
|---|
|  | 3944 | if (Owner.Filters = [pfNone]) or (Owner.Filters = []) then | 
|---|
|  | 3945 | begin | 
|---|
|  | 3946 | Result := FILTER_NONE; | 
|---|
|  | 3947 | exit; | 
|---|
|  | 3948 | end {if (Owner.Filters = [pfNone...}; | 
|---|
|  | 3949 |  | 
|---|
|  | 3950 | {Check which filter is the best by checking which has the larger} | 
|---|
|  | 3951 | {sequence of the same byte, since they are best compressed} | 
|---|
|  | 3952 | LongestRun := 0; Result := FILTER_NONE; | 
|---|
|  | 3953 | for ii := FILTER_NONE TO FILTER_PAETH do | 
|---|
|  | 3954 | {Check if this filter was selected} | 
|---|
|  | 3955 | if TFilter(ii) in Owner.Filters then | 
|---|
|  | 3956 | begin | 
|---|
|  | 3957 | Run := 0; | 
|---|
|  | 3958 | {Check if it's the only filter} | 
|---|
|  | 3959 | if Owner.Filters = [TFilter(ii)] then | 
|---|
|  | 3960 | begin | 
|---|
|  | 3961 | Result := ii; | 
|---|
|  | 3962 | exit; | 
|---|
|  | 3963 | end; | 
|---|
|  | 3964 |  | 
|---|
|  | 3965 | {Check using a sequence of four bytes} | 
|---|
|  | 3966 | for jj := 2 to Row_Bytes - 1 do | 
|---|
|  | 3967 | if (Encode_Buffer[ii]^[jj] = Encode_Buffer [ii]^[jj-1]) or | 
|---|
|  | 3968 | (Encode_Buffer[ii]^[jj] = Encode_Buffer [ii]^[jj-2]) then | 
|---|
|  | 3969 | inc(Run);  {Count the number of sequences} | 
|---|
|  | 3970 |  | 
|---|
|  | 3971 | {Check if this one is the best so far} | 
|---|
|  | 3972 | if (Run > LongestRun) then | 
|---|
|  | 3973 | begin | 
|---|
|  | 3974 | Result := ii; | 
|---|
|  | 3975 | LongestRun := Run; | 
|---|
|  | 3976 | end {if (Run > LongestRun)}; | 
|---|
|  | 3977 |  | 
|---|
|  | 3978 | end {if TFilter(ii) in Owner.Filters}; | 
|---|
|  | 3979 | end; | 
|---|
|  | 3980 |  | 
|---|
|  | 3981 | {TChunkPLTE implementation} | 
|---|
|  | 3982 |  | 
|---|
|  | 3983 | {Returns an item in the palette} | 
|---|
|  | 3984 | function TChunkPLTE.GetPaletteItem(Index: Byte): TRGBQuad; | 
|---|
|  | 3985 | begin | 
|---|
|  | 3986 | {Test if item is valid, if not raise error} | 
|---|
|  | 3987 | if Index > Count - 1 then | 
|---|
|  | 3988 | Owner.RaiseError(EPNGError, EPNGUnknownPalEntryText) | 
|---|
|  | 3989 | else | 
|---|
|  | 3990 | {Returns the item} | 
|---|
|  | 3991 | Result := Header.BitmapInfo.bmiColors[Index]; | 
|---|
|  | 3992 | end; | 
|---|
|  | 3993 |  | 
|---|
|  | 3994 | {Loads the palette chunk from a stream} | 
|---|
|  | 3995 | function TChunkPLTE.LoadFromStream(Stream: TStream; | 
|---|
|  | 3996 | const ChunkName: TChunkName; Size: Integer): Boolean; | 
|---|
|  | 3997 | type | 
|---|
|  | 3998 | pPalEntry = ^PalEntry; | 
|---|
|  | 3999 | PalEntry = record | 
|---|
|  | 4000 | r, g, b: Byte; | 
|---|
|  | 4001 | end; | 
|---|
|  | 4002 | var | 
|---|
|  | 4003 | j        : Integer;          {For the FOR} | 
|---|
|  | 4004 | PalColor : pPalEntry; | 
|---|
|  | 4005 | begin | 
|---|
|  | 4006 | {Let ancestor load data and check CRC} | 
|---|
|  | 4007 | Result := inherited LoadFromStream(Stream, ChunkName, Size); | 
|---|
|  | 4008 | if not Result then exit; | 
|---|
|  | 4009 |  | 
|---|
|  | 4010 | {This chunk must be divisible by 3 in order to be valid} | 
|---|
|  | 4011 | if (Size mod 3 <> 0) or (Size div 3 > 256) then | 
|---|
|  | 4012 | begin | 
|---|
|  | 4013 | {Raise error} | 
|---|
|  | 4014 | Result := FALSE; | 
|---|
|  | 4015 | Owner.RaiseError(EPNGInvalidPalette, EPNGInvalidPaletteText); | 
|---|
|  | 4016 | exit; | 
|---|
|  | 4017 | end {if Size mod 3 <> 0}; | 
|---|
|  | 4018 |  | 
|---|
|  | 4019 | {Fill array with the palette entries} | 
|---|
|  | 4020 | fCount := Size div 3; | 
|---|
|  | 4021 | PalColor := Data; | 
|---|
|  | 4022 | FOR j := 0 TO fCount - 1 DO | 
|---|
|  | 4023 | with Header.BitmapInfo.bmiColors[j] do | 
|---|
|  | 4024 | begin | 
|---|
|  | 4025 | rgbRed  :=  Owner.GammaTable[PalColor.r]; | 
|---|
|  | 4026 | rgbGreen := Owner.GammaTable[PalColor.g]; | 
|---|
|  | 4027 | rgbBlue :=  Owner.GammaTable[PalColor.b]; | 
|---|
|  | 4028 | rgbReserved := 0; | 
|---|
|  | 4029 | {Move to next palette entry} | 
|---|
|  | 4030 | inc(PalColor); | 
|---|
|  | 4031 | end; | 
|---|
|  | 4032 | end; | 
|---|
|  | 4033 |  | 
|---|
|  | 4034 | {Saves the PLTE chunk to a stream} | 
|---|
|  | 4035 | function TChunkPLTE.SaveToStream(Stream: TStream): Boolean; | 
|---|
|  | 4036 | var | 
|---|
|  | 4037 | J: Integer; | 
|---|
|  | 4038 | DataPtr: pByte; | 
|---|
|  | 4039 | begin | 
|---|
|  | 4040 | {Adjust size to hold all the palette items} | 
|---|
|  | 4041 | ResizeData(fCount * 3); | 
|---|
|  | 4042 | {Copy pointer to data} | 
|---|
|  | 4043 | DataPtr := fData; | 
|---|
|  | 4044 |  | 
|---|
|  | 4045 | {Copy palette items} | 
|---|
|  | 4046 | with Header do | 
|---|
|  | 4047 | FOR j := 0 TO fCount - 1 DO | 
|---|
|  | 4048 | with BitmapInfo.bmiColors[j] do | 
|---|
|  | 4049 | begin | 
|---|
|  | 4050 | DataPtr^ := Owner.InverseGamma[rgbRed]; inc(DataPtr); | 
|---|
|  | 4051 | DataPtr^ := Owner.InverseGamma[rgbGreen]; inc(DataPtr); | 
|---|
|  | 4052 | DataPtr^ := Owner.InverseGamma[rgbBlue]; inc(DataPtr); | 
|---|
|  | 4053 | end {with BitmapInfo}; | 
|---|
|  | 4054 |  | 
|---|
|  | 4055 | {Let ancestor do the rest of the work} | 
|---|
|  | 4056 | Result := inherited SaveToStream(Stream); | 
|---|
|  | 4057 | end; | 
|---|
|  | 4058 |  | 
|---|
|  | 4059 | {Assigns from another PLTE chunk} | 
|---|
|  | 4060 | procedure TChunkPLTE.Assign(Source: TChunk); | 
|---|
|  | 4061 | begin | 
|---|
|  | 4062 | {Copy the number of palette items} | 
|---|
|  | 4063 | if Source is TChunkPLTE then | 
|---|
|  | 4064 | fCount := TChunkPLTE(Source).fCount | 
|---|
|  | 4065 | else | 
|---|
|  | 4066 | Owner.RaiseError(EPNGError, EPNGCannotAssignChunkText); | 
|---|
|  | 4067 | end; | 
|---|
|  | 4068 |  | 
|---|
|  | 4069 | {TChunkgAMA implementation} | 
|---|
|  | 4070 |  | 
|---|
|  | 4071 | {Assigns from another chunk} | 
|---|
|  | 4072 | procedure TChunkgAMA.Assign(Source: TChunk); | 
|---|
|  | 4073 | begin | 
|---|
|  | 4074 | {Copy the gamma value} | 
|---|
|  | 4075 | if Source is TChunkgAMA then | 
|---|
|  | 4076 | Gamma := TChunkgAMA(Source).Gamma | 
|---|
|  | 4077 | else | 
|---|
|  | 4078 | Owner.RaiseError(EPNGError, EPNGCannotAssignChunkText); | 
|---|
|  | 4079 | end; | 
|---|
|  | 4080 |  | 
|---|
|  | 4081 | {Gamma chunk being created} | 
|---|
|  | 4082 | constructor TChunkgAMA.Create(Owner: TPngObject); | 
|---|
|  | 4083 | begin | 
|---|
|  | 4084 | {Call ancestor} | 
|---|
|  | 4085 | inherited Create(Owner); | 
|---|
|  | 4086 | Gamma := 1;  {Initial value} | 
|---|
|  | 4087 | end; | 
|---|
|  | 4088 |  | 
|---|
|  | 4089 | {Returns gamma value} | 
|---|
|  | 4090 | function TChunkgAMA.GetValue: Cardinal; | 
|---|
|  | 4091 | begin | 
|---|
|  | 4092 | {Make sure that the size is four bytes} | 
|---|
|  | 4093 | if DataSize <> 4 then | 
|---|
|  | 4094 | begin | 
|---|
|  | 4095 | {Adjust size and returns 1} | 
|---|
|  | 4096 | ResizeData(4); | 
|---|
|  | 4097 | Result := 1; | 
|---|
|  | 4098 | end | 
|---|
|  | 4099 | {If it's right, read the value} | 
|---|
|  | 4100 | else Result := Cardinal(ByteSwap(pCardinal(Data)^)) | 
|---|
|  | 4101 | end; | 
|---|
|  | 4102 |  | 
|---|
|  | 4103 | function Power(Base, Exponent: Extended): Extended; | 
|---|
|  | 4104 | begin | 
|---|
|  | 4105 | if Exponent = 0.0 then | 
|---|
|  | 4106 | Result := 1.0               {Math rule} | 
|---|
|  | 4107 | else if (Base = 0) or (Exponent = 0) then Result := 0 | 
|---|
|  | 4108 | else | 
|---|
|  | 4109 | Result := Exp(Exponent * Ln(Base)); | 
|---|
|  | 4110 | end; | 
|---|
|  | 4111 |  | 
|---|
|  | 4112 |  | 
|---|
|  | 4113 | {Loading the chunk from a stream} | 
|---|
|  | 4114 | function TChunkgAMA.LoadFromStream(Stream: TStream; | 
|---|
|  | 4115 | const ChunkName: TChunkName; Size: Integer): Boolean; | 
|---|
|  | 4116 | var | 
|---|
|  | 4117 | i: Integer; | 
|---|
|  | 4118 | Value: Cardinal; | 
|---|
|  | 4119 | begin | 
|---|
|  | 4120 | {Call ancestor and test if it went ok} | 
|---|
|  | 4121 | Result := inherited LoadFromStream(Stream, ChunkName, Size); | 
|---|
|  | 4122 | if not Result then exit; | 
|---|
|  | 4123 | Value := Gamma; | 
|---|
|  | 4124 | {Build gamma table and inverse table for saving} | 
|---|
|  | 4125 | if Value <> 0 then | 
|---|
|  | 4126 | with Owner do | 
|---|
|  | 4127 | FOR i := 0 TO 255 DO | 
|---|
|  | 4128 | begin | 
|---|
|  | 4129 | GammaTable[I] := Round(Power((I / 255), 1 / | 
|---|
|  | 4130 | (Value / 100000 * 2.2)) * 255); | 
|---|
|  | 4131 | InverseGamma[Round(Power((I / 255), 1 / | 
|---|
|  | 4132 | (Value / 100000 * 2.2)) * 255)] := I; | 
|---|
|  | 4133 | end | 
|---|
|  | 4134 | end; | 
|---|
|  | 4135 |  | 
|---|
|  | 4136 | {Sets the gamma value} | 
|---|
|  | 4137 | procedure TChunkgAMA.SetValue(const Value: Cardinal); | 
|---|
|  | 4138 | begin | 
|---|
|  | 4139 | {Make sure that the size is four bytes} | 
|---|
|  | 4140 | if DataSize <> 4 then ResizeData(4); | 
|---|
|  | 4141 | {If it's right, set the value} | 
|---|
|  | 4142 | pCardinal(Data)^ := ByteSwap(Value); | 
|---|
|  | 4143 | end; | 
|---|
|  | 4144 |  | 
|---|
|  | 4145 | {TPngObject implementation} | 
|---|
|  | 4146 |  | 
|---|
|  | 4147 | {Assigns from another object} | 
|---|
|  | 4148 | procedure TPngObject.Assign(Source: TPersistent); | 
|---|
|  | 4149 | begin | 
|---|
|  | 4150 | {Being cleared} | 
|---|
|  | 4151 | if Source = nil then | 
|---|
|  | 4152 | ClearChunks | 
|---|
|  | 4153 | {Assigns contents from another TPNGObject} | 
|---|
|  | 4154 | else if Source is TPNGObject then | 
|---|
|  | 4155 | AssignPNG(Source as TPNGObject) | 
|---|
|  | 4156 | {Copy contents from a TBitmap} | 
|---|
|  | 4157 | {$IFDEF UseDelphi}else if Source is TBitmap then | 
|---|
|  | 4158 | with Source as TBitmap do | 
|---|
|  | 4159 | AssignHandle(Handle, Transparent, | 
|---|
|  | 4160 | ColorToRGB(TransparentColor)){$ENDIF} | 
|---|
|  | 4161 | {Unknown source, let ancestor deal with it} | 
|---|
|  | 4162 | else | 
|---|
|  | 4163 | inherited; | 
|---|
|  | 4164 | end; | 
|---|
|  | 4165 |  | 
|---|
|  | 4166 | {Clear all the chunks in the list} | 
|---|
|  | 4167 | procedure TPngObject.ClearChunks; | 
|---|
|  | 4168 | var | 
|---|
|  | 4169 | i: Integer; | 
|---|
|  | 4170 | begin | 
|---|
|  | 4171 | {Initialize gamma} | 
|---|
|  | 4172 | InitializeGamma(); | 
|---|
|  | 4173 | {Free all the objects and memory (0 chunks Bug fixed by Noel Sharpe)} | 
|---|
|  | 4174 | for i := 0 TO Integer(Chunks.Count) - 1 do | 
|---|
|  | 4175 | TChunk(Chunks.Item[i]).Free; | 
|---|
|  | 4176 | Chunks.Count := 0; | 
|---|
|  | 4177 | end; | 
|---|
|  | 4178 |  | 
|---|
|  | 4179 | {Portable Network Graphics object being created} | 
|---|
|  | 4180 | constructor TPngObject.Create; | 
|---|
|  | 4181 | begin | 
|---|
|  | 4182 | {Let it be created} | 
|---|
|  | 4183 | inherited Create; | 
|---|
|  | 4184 |  | 
|---|
|  | 4185 | {Initial properties} | 
|---|
|  | 4186 | TempPalette := 0; | 
|---|
|  | 4187 | fFilters := [pfSub]; | 
|---|
|  | 4188 | fCompressionLevel := 7; | 
|---|
|  | 4189 | fInterlaceMethod := imNone; | 
|---|
|  | 4190 | fMaxIdatSize := High(Word); | 
|---|
|  | 4191 | {Create chunklist object} | 
|---|
|  | 4192 | fChunkList := TPngList.Create(Self); | 
|---|
|  | 4193 | end; | 
|---|
|  | 4194 |  | 
|---|
|  | 4195 | {Portable Network Graphics object being destroyed} | 
|---|
|  | 4196 | destructor TPngObject.Destroy; | 
|---|
|  | 4197 | begin | 
|---|
|  | 4198 | {Free object list} | 
|---|
|  | 4199 | ClearChunks; | 
|---|
|  | 4200 | fChunkList.Free; | 
|---|
|  | 4201 | {Free the temporary palette} | 
|---|
|  | 4202 | if TempPalette <> 0 then DeleteObject(TempPalette); | 
|---|
|  | 4203 |  | 
|---|
|  | 4204 | {Call ancestor destroy} | 
|---|
|  | 4205 | inherited Destroy; | 
|---|
|  | 4206 | end; | 
|---|
|  | 4207 |  | 
|---|
|  | 4208 | {Returns linesize and byte offset for pixels} | 
|---|
|  | 4209 | procedure TPngObject.GetPixelInfo(var LineSize, Offset: Cardinal); | 
|---|
|  | 4210 | begin | 
|---|
|  | 4211 | {There must be an Header chunk to calculate size} | 
|---|
|  | 4212 | if HeaderPresent then | 
|---|
|  | 4213 | begin | 
|---|
|  | 4214 | {Calculate number of bytes for each line} | 
|---|
|  | 4215 | LineSize := BytesForPixels(Header.Width, Header.ColorType, Header.BitDepth); | 
|---|
|  | 4216 |  | 
|---|
|  | 4217 | {Calculates byte offset} | 
|---|
|  | 4218 | Case Header.ColorType of | 
|---|
|  | 4219 | {Grayscale} | 
|---|
|  | 4220 | COLOR_GRAYSCALE: | 
|---|
|  | 4221 | If Header.BitDepth = 16 Then | 
|---|
|  | 4222 | Offset := 2 | 
|---|
|  | 4223 | Else | 
|---|
|  | 4224 | Offset := 1 ; | 
|---|
|  | 4225 | {It always smaller or equal one byte, so it occupes one byte} | 
|---|
|  | 4226 | COLOR_PALETTE: | 
|---|
|  | 4227 | offset := 1; | 
|---|
|  | 4228 | {It might be 3 or 6 bytes} | 
|---|
|  | 4229 | COLOR_RGB: | 
|---|
|  | 4230 | offset := 3 * Header.BitDepth Div 8; | 
|---|
|  | 4231 | {It might be 2 or 4 bytes} | 
|---|
|  | 4232 | COLOR_GRAYSCALEALPHA: | 
|---|
|  | 4233 | offset := 2 * Header.BitDepth Div 8; | 
|---|
|  | 4234 | {4 or 8 bytes} | 
|---|
|  | 4235 | COLOR_RGBALPHA: | 
|---|
|  | 4236 | offset := 4 * Header.BitDepth Div 8; | 
|---|
|  | 4237 | else | 
|---|
|  | 4238 | Offset := 0; | 
|---|
|  | 4239 | End ; | 
|---|
|  | 4240 |  | 
|---|
|  | 4241 | end | 
|---|
|  | 4242 | else | 
|---|
|  | 4243 | begin | 
|---|
|  | 4244 | {In case if there isn't any Header chunk} | 
|---|
|  | 4245 | Offset := 0; | 
|---|
|  | 4246 | LineSize := 0; | 
|---|
|  | 4247 | end; | 
|---|
|  | 4248 |  | 
|---|
|  | 4249 | end; | 
|---|
|  | 4250 |  | 
|---|
|  | 4251 | {Returns image height} | 
|---|
|  | 4252 | function TPngObject.GetHeight: Integer; | 
|---|
|  | 4253 | begin | 
|---|
|  | 4254 | {There must be a Header chunk to get the size, otherwise returns 0} | 
|---|
|  | 4255 | if HeaderPresent then | 
|---|
|  | 4256 | Result := TChunkIHDR(Chunks.Item[0]).Height | 
|---|
|  | 4257 | else Result := 0; | 
|---|
|  | 4258 | end; | 
|---|
|  | 4259 |  | 
|---|
|  | 4260 | {Returns image width} | 
|---|
|  | 4261 | function TPngObject.GetWidth: Integer; | 
|---|
|  | 4262 | begin | 
|---|
|  | 4263 | {There must be a Header chunk to get the size, otherwise returns 0} | 
|---|
|  | 4264 | if HeaderPresent then | 
|---|
|  | 4265 | Result := Header.Width | 
|---|
|  | 4266 | else Result := 0; | 
|---|
|  | 4267 | end; | 
|---|
|  | 4268 |  | 
|---|
|  | 4269 | {Returns if the image is empty} | 
|---|
|  | 4270 | function TPngObject.GetEmpty: Boolean; | 
|---|
|  | 4271 | begin | 
|---|
|  | 4272 | Result := (Chunks.Count = 0); | 
|---|
|  | 4273 | end; | 
|---|
|  | 4274 |  | 
|---|
|  | 4275 | {Raises an error} | 
|---|
|  | 4276 | procedure TPngObject.RaiseError(ExceptionClass: ExceptClass; Text: String); | 
|---|
|  | 4277 | begin | 
|---|
|  | 4278 | raise ExceptionClass.Create(Text); | 
|---|
|  | 4279 | end; | 
|---|
|  | 4280 |  | 
|---|
|  | 4281 | {Set the maximum size for IDAT chunk} | 
|---|
|  | 4282 | procedure TPngObject.SetMaxIdatSize(const Value: Integer); | 
|---|
|  | 4283 | begin | 
|---|
|  | 4284 | {Make sure the size is at least 65535} | 
|---|
|  | 4285 | if Value < High(Word) then | 
|---|
|  | 4286 | fMaxIdatSize := High(Word) else fMaxIdatSize := Value; | 
|---|
|  | 4287 | end; | 
|---|
|  | 4288 |  | 
|---|
|  | 4289 | {$IFNDEF UseDelphi} | 
|---|
|  | 4290 | {Creates a file stream reading from the filename in the parameter and load} | 
|---|
|  | 4291 | procedure TPngObject.LoadFromFile(const Filename: String); | 
|---|
|  | 4292 | var | 
|---|
|  | 4293 | FileStream: TFileStream; | 
|---|
|  | 4294 | begin | 
|---|
|  | 4295 | {Test if the file exists} | 
|---|
|  | 4296 | if not FileExists(Filename) then | 
|---|
|  | 4297 | begin | 
|---|
|  | 4298 | {In case it does not exists, raise error} | 
|---|
|  | 4299 | RaiseError(EPNGNotExists, EPNGNotExistsText); | 
|---|
|  | 4300 | exit; | 
|---|
|  | 4301 | end; | 
|---|
|  | 4302 |  | 
|---|
|  | 4303 | {Creates the file stream to read} | 
|---|
|  | 4304 | FileStream := TFileStream.Create(Filename, [fsmRead]); | 
|---|
|  | 4305 | LoadFromStream(FileStream);  {Loads the data} | 
|---|
|  | 4306 | FileStream.Free;             {Free file stream} | 
|---|
|  | 4307 | end; | 
|---|
|  | 4308 |  | 
|---|
|  | 4309 | {Saves the current png image to a file} | 
|---|
|  | 4310 | procedure TPngObject.SaveToFile(const Filename: String); | 
|---|
|  | 4311 | var | 
|---|
|  | 4312 | FileStream: TFileStream; | 
|---|
|  | 4313 | begin | 
|---|
|  | 4314 | {Creates the file stream to write} | 
|---|
|  | 4315 | FileStream := TFileStream.Create(Filename, [fsmWrite]); | 
|---|
|  | 4316 | SaveToStream(FileStream);    {Saves the data} | 
|---|
|  | 4317 | FileStream.Free;             {Free file stream} | 
|---|
|  | 4318 | end; | 
|---|
|  | 4319 |  | 
|---|
|  | 4320 | {$ENDIF} | 
|---|
|  | 4321 |  | 
|---|
|  | 4322 | {Returns pointer to the chunk TChunkIHDR which should be the first} | 
|---|
|  | 4323 | function TPngObject.GetHeader: TChunkIHDR; | 
|---|
|  | 4324 | begin | 
|---|
|  | 4325 | {If there is a TChunkIHDR returns it, otherwise returns nil} | 
|---|
|  | 4326 | if (Chunks.Count <> 0) and (Chunks.Item[0] is TChunkIHDR) then | 
|---|
|  | 4327 | Result := Chunks.Item[0] as TChunkIHDR | 
|---|
|  | 4328 | else | 
|---|
|  | 4329 | begin | 
|---|
|  | 4330 | {No header, throw error message} | 
|---|
|  | 4331 | RaiseError(EPNGHeaderNotPresent, EPNGHeaderNotPresentText); | 
|---|
|  | 4332 | Result := nil | 
|---|
|  | 4333 | end | 
|---|
|  | 4334 | end; | 
|---|
|  | 4335 |  | 
|---|
|  | 4336 | {Draws using partial transparency} | 
|---|
|  | 4337 | procedure TPngObject.DrawPartialTrans(DC: HDC; Rect: TRect); | 
|---|
|  | 4338 | {Adjust the rectangle structure} | 
|---|
|  | 4339 | procedure AdjustRect(var Rect: TRect); | 
|---|
|  | 4340 | var | 
|---|
|  | 4341 | t: Integer; | 
|---|
|  | 4342 | begin | 
|---|
|  | 4343 | if Rect.Right < Rect.Left then | 
|---|
|  | 4344 | begin | 
|---|
|  | 4345 | t := Rect.Right; | 
|---|
|  | 4346 | Rect.Right := Rect.Left; | 
|---|
|  | 4347 | Rect.Left := t; | 
|---|
|  | 4348 | end; | 
|---|
|  | 4349 | if Rect.Bottom < Rect.Top then | 
|---|
|  | 4350 | begin | 
|---|
|  | 4351 | t := Rect.Bottom; | 
|---|
|  | 4352 | Rect.Bottom := Rect.Top; | 
|---|
|  | 4353 | Rect.Top := t; | 
|---|
|  | 4354 | end | 
|---|
|  | 4355 | end; | 
|---|
|  | 4356 |  | 
|---|
|  | 4357 | type | 
|---|
|  | 4358 | {Access to pixels} | 
|---|
|  | 4359 | TPixelLine = Array[Word] of TRGBQuad; | 
|---|
|  | 4360 | pPixelLine = ^TPixelLine; | 
|---|
|  | 4361 | const | 
|---|
|  | 4362 | {Structure used to create the bitmap} | 
|---|
|  | 4363 | BitmapInfoHeader: TBitmapInfoHeader = | 
|---|
|  | 4364 | (biSize: sizeof(TBitmapInfoHeader); | 
|---|
|  | 4365 | biWidth: 100; | 
|---|
|  | 4366 | biHeight: 100; | 
|---|
|  | 4367 | biPlanes: 1; | 
|---|
|  | 4368 | biBitCount: 32; | 
|---|
|  | 4369 | biCompression: BI_RGB; | 
|---|
|  | 4370 | biSizeImage: 0; | 
|---|
|  | 4371 | biXPelsPerMeter: 0; | 
|---|
|  | 4372 | biYPelsPerMeter: 0; | 
|---|
|  | 4373 | biClrUsed: 0; | 
|---|
|  | 4374 | biClrImportant: 0); | 
|---|
|  | 4375 | var | 
|---|
|  | 4376 | {Buffer bitmap creation} | 
|---|
|  | 4377 | BitmapInfo  : TBitmapInfo; | 
|---|
|  | 4378 | BufferDC    : HDC; | 
|---|
|  | 4379 | BufferBits  : Pointer; | 
|---|
|  | 4380 | OldBitmap, | 
|---|
|  | 4381 | BufferBitmap: HBitmap; | 
|---|
|  | 4382 | Header: TChunkIHDR; | 
|---|
|  | 4383 |  | 
|---|
|  | 4384 | {Transparency/palette chunks} | 
|---|
|  | 4385 | TransparencyChunk: TChunktRNS; | 
|---|
|  | 4386 | PaletteChunk: TChunkPLTE; | 
|---|
|  | 4387 | TransValue, PaletteIndex: Byte; | 
|---|
|  | 4388 | CurBit: Integer; | 
|---|
|  | 4389 | Data: PByte; | 
|---|
|  | 4390 |  | 
|---|
|  | 4391 | {Buffer bitmap modification} | 
|---|
|  | 4392 | BytesPerRowDest, | 
|---|
|  | 4393 | BytesPerRowSrc, | 
|---|
|  | 4394 | BytesPerRowAlpha: Integer; | 
|---|
|  | 4395 | ImageSource, ImageSourceOrg, | 
|---|
|  | 4396 | AlphaSource     : pByteArray; | 
|---|
|  | 4397 | ImageData       : pPixelLine; | 
|---|
|  | 4398 | i, j, i2, j2    : Integer; | 
|---|
|  | 4399 |  | 
|---|
|  | 4400 | {For bitmap stretching} | 
|---|
|  | 4401 | W, H            : Cardinal; | 
|---|
|  | 4402 | Stretch         : Boolean; | 
|---|
|  | 4403 | FactorX, FactorY: Double; | 
|---|
|  | 4404 | begin | 
|---|
|  | 4405 | {Prepares the rectangle structure to stretch draw} | 
|---|
|  | 4406 | if (Rect.Right = Rect.Left) or (Rect.Bottom = Rect.Top) then exit; | 
|---|
|  | 4407 | AdjustRect(Rect); | 
|---|
|  | 4408 | {Gets the width and height} | 
|---|
|  | 4409 | W := Rect.Right - Rect.Left; | 
|---|
|  | 4410 | H := Rect.Bottom - Rect.Top; | 
|---|
|  | 4411 | Header := Self.Header; {Fast access to header} | 
|---|
|  | 4412 | Stretch := (W <> Header.Width) or (H <> Header.Height); | 
|---|
|  | 4413 | if Stretch then FactorX := W / Header.Width else FactorX := 1; | 
|---|
|  | 4414 | if Stretch then FactorY := H / Header.Height else FactorY := 1; | 
|---|
|  | 4415 |  | 
|---|
|  | 4416 | {Prepare to create the bitmap} | 
|---|
|  | 4417 | Fillchar(BitmapInfo, sizeof(BitmapInfo), #0); | 
|---|
|  | 4418 | BitmapInfoHeader.biWidth := W; | 
|---|
|  | 4419 | BitmapInfoHeader.biHeight := -Integer(H); | 
|---|
|  | 4420 | BitmapInfo.bmiHeader := BitmapInfoHeader; | 
|---|
|  | 4421 |  | 
|---|
|  | 4422 | {Create the bitmap which will receive the background, the applied} | 
|---|
|  | 4423 | {alpha blending and then will be painted on the background} | 
|---|
|  | 4424 | BufferDC := CreateCompatibleDC(0); | 
|---|
|  | 4425 | {In case BufferDC could not be created} | 
|---|
|  | 4426 | if (BufferDC = 0) then RaiseError(EPNGOutMemory, EPNGOutMemoryText); | 
|---|
|  | 4427 | BufferBitmap := CreateDIBSection(BufferDC, BitmapInfo, DIB_RGB_COLORS, | 
|---|
|  | 4428 | BufferBits, 0, 0); | 
|---|
|  | 4429 | {In case buffer bitmap could not be created} | 
|---|
|  | 4430 | if (BufferBitmap = 0) or (BufferBits = Nil) then | 
|---|
|  | 4431 | begin | 
|---|
|  | 4432 | if BufferBitmap <> 0 then DeleteObject(BufferBitmap); | 
|---|
|  | 4433 | DeleteDC(BufferDC); | 
|---|
|  | 4434 | RaiseError(EPNGOutMemory, EPNGOutMemoryText); | 
|---|
|  | 4435 | end; | 
|---|
|  | 4436 |  | 
|---|
|  | 4437 | {Selects new bitmap and release old bitmap} | 
|---|
|  | 4438 | OldBitmap := SelectObject(BufferDC, BufferBitmap); | 
|---|
|  | 4439 |  | 
|---|
|  | 4440 | {Draws the background on the buffer image} | 
|---|
|  | 4441 | BitBlt(BufferDC, 0, 0, W, H, DC, Rect.Left, Rect.Top, SRCCOPY); | 
|---|
|  | 4442 |  | 
|---|
|  | 4443 | {Obtain number of bytes for each row} | 
|---|
|  | 4444 | BytesPerRowAlpha := Header.Width; | 
|---|
|  | 4445 | BytesPerRowDest := (((BitmapInfo.bmiHeader.biBitCount * W) + 31) | 
|---|
|  | 4446 | and not 31) div 8; {Number of bytes for each image row in destination} | 
|---|
|  | 4447 | BytesPerRowSrc := (((Header.BitmapInfo.bmiHeader.biBitCount * Header.Width) + | 
|---|
|  | 4448 | 31) and not 31) div 8; {Number of bytes for each image row in source} | 
|---|
|  | 4449 |  | 
|---|
|  | 4450 | {Obtains image pointers} | 
|---|
|  | 4451 | ImageData := BufferBits; | 
|---|
|  | 4452 | AlphaSource := Header.ImageAlpha; | 
|---|
|  | 4453 | Longint(ImageSource) := Longint(Header.ImageData) + | 
|---|
|  | 4454 | Header.BytesPerRow * Longint(Header.Height - 1); | 
|---|
|  | 4455 | ImageSourceOrg := ImageSource; | 
|---|
|  | 4456 |  | 
|---|
|  | 4457 | case Header.BitmapInfo.bmiHeader.biBitCount of | 
|---|
|  | 4458 | {R, G, B images} | 
|---|
|  | 4459 | 24: | 
|---|
|  | 4460 | FOR j := 1 TO H DO | 
|---|
|  | 4461 | begin | 
|---|
|  | 4462 | {Process all the pixels in this line} | 
|---|
|  | 4463 | FOR i := 0 TO W - 1 DO | 
|---|
|  | 4464 | begin | 
|---|
|  | 4465 | if Stretch then i2 := trunc(i / FactorX) else i2 := i; | 
|---|
|  | 4466 | {Optmize when we don´t have transparency} | 
|---|
|  | 4467 | if (AlphaSource[i2] <> 0) then | 
|---|
|  | 4468 | if (AlphaSource[i2] = 255) then | 
|---|
|  | 4469 | ImageData[i] := pRGBQuad(@ImageSource[i2 * 3])^ | 
|---|
|  | 4470 | else | 
|---|
|  | 4471 | with ImageData[i] do | 
|---|
|  | 4472 | begin | 
|---|
|  | 4473 | rgbRed := (255+ImageSource[2+i2*3] * AlphaSource[i2] + rgbRed * | 
|---|
|  | 4474 | (not AlphaSource[i2])) shr 8; | 
|---|
|  | 4475 | rgbGreen := (255+ImageSource[1+i2*3] * AlphaSource[i2] + | 
|---|
|  | 4476 | rgbGreen * (not AlphaSource[i2])) shr 8; | 
|---|
|  | 4477 | rgbBlue := (255+ImageSource[i2*3] * AlphaSource[i2] + rgbBlue * | 
|---|
|  | 4478 | (not AlphaSource[i2])) shr 8; | 
|---|
|  | 4479 | end; | 
|---|
|  | 4480 | end; | 
|---|
|  | 4481 |  | 
|---|
|  | 4482 | {Move pointers} | 
|---|
|  | 4483 | inc(Longint(ImageData), BytesPerRowDest); | 
|---|
|  | 4484 | if Stretch then j2 := trunc(j / FactorY) else j2 := j; | 
|---|
|  | 4485 | Longint(ImageSource) := Longint(ImageSourceOrg) - BytesPerRowSrc * j2; | 
|---|
|  | 4486 | Longint(AlphaSource) := Longint(Header.ImageAlpha) + | 
|---|
|  | 4487 | BytesPerRowAlpha * j2; | 
|---|
|  | 4488 | end; | 
|---|
|  | 4489 | {Palette images with 1 byte for each pixel} | 
|---|
|  | 4490 | 1,4,8: if Header.ColorType = COLOR_GRAYSCALEALPHA then | 
|---|
|  | 4491 | FOR j := 1 TO H DO | 
|---|
|  | 4492 | begin | 
|---|
|  | 4493 | {Process all the pixels in this line} | 
|---|
|  | 4494 | FOR i := 0 TO W - 1 DO | 
|---|
|  | 4495 | with ImageData[i], Header.BitmapInfo do begin | 
|---|
|  | 4496 | if Stretch then i2 := trunc(i / FactorX) else i2 := i; | 
|---|
|  | 4497 | rgbRed := (255 + ImageSource[i2] * AlphaSource[i2] + | 
|---|
|  | 4498 | rgbRed * (255 - AlphaSource[i2])) shr 8; | 
|---|
|  | 4499 | rgbGreen := (255 + ImageSource[i2] * AlphaSource[i2] + | 
|---|
|  | 4500 | rgbGreen * (255 - AlphaSource[i2])) shr 8; | 
|---|
|  | 4501 | rgbBlue := (255 + ImageSource[i2] * AlphaSource[i2] + | 
|---|
|  | 4502 | rgbBlue * (255 - AlphaSource[i2])) shr 8; | 
|---|
|  | 4503 | end; | 
|---|
|  | 4504 |  | 
|---|
|  | 4505 | {Move pointers} | 
|---|
|  | 4506 | Longint(ImageData) := Longint(ImageData) + BytesPerRowDest; | 
|---|
|  | 4507 | if Stretch then j2 := trunc(j / FactorY) else j2 := j; | 
|---|
|  | 4508 | Longint(ImageSource) := Longint(ImageSourceOrg) - BytesPerRowSrc * j2; | 
|---|
|  | 4509 | Longint(AlphaSource) := Longint(Header.ImageAlpha) + | 
|---|
|  | 4510 | BytesPerRowAlpha * j2; | 
|---|
|  | 4511 | end | 
|---|
|  | 4512 | else {Palette images} | 
|---|
|  | 4513 | begin | 
|---|
|  | 4514 | {Obtain pointer to the transparency chunk} | 
|---|
|  | 4515 | TransparencyChunk := TChunktRNS(Chunks.ItemFromClass(TChunktRNS)); | 
|---|
|  | 4516 | PaletteChunk := TChunkPLTE(Chunks.ItemFromClass(TChunkPLTE)); | 
|---|
|  | 4517 |  | 
|---|
|  | 4518 | FOR j := 1 TO H DO | 
|---|
|  | 4519 | begin | 
|---|
|  | 4520 | {Process all the pixels in this line} | 
|---|
|  | 4521 | i := 0; | 
|---|
|  | 4522 | repeat | 
|---|
|  | 4523 | CurBit := 0; | 
|---|
|  | 4524 | if Stretch then i2 := trunc(i / FactorX) else i2 := i; | 
|---|
|  | 4525 | Data := @ImageSource[i2]; | 
|---|
|  | 4526 |  | 
|---|
|  | 4527 | repeat | 
|---|
|  | 4528 | {Obtains the palette index} | 
|---|
|  | 4529 | case Header.BitDepth of | 
|---|
|  | 4530 | 1: PaletteIndex := (Data^ shr (7-(I Mod 8))) and 1; | 
|---|
|  | 4531 | 2,4: PaletteIndex := (Data^ shr ((1-(I Mod 2))*4)) and $0F; | 
|---|
|  | 4532 | else PaletteIndex := Data^; | 
|---|
|  | 4533 | end; | 
|---|
|  | 4534 |  | 
|---|
|  | 4535 | {Updates the image with the new pixel} | 
|---|
|  | 4536 | with ImageData[i] do | 
|---|
|  | 4537 | begin | 
|---|
|  | 4538 | TransValue := TransparencyChunk.PaletteValues[PaletteIndex]; | 
|---|
|  | 4539 | rgbRed := (255 + PaletteChunk.Item[PaletteIndex].rgbRed * | 
|---|
|  | 4540 | TransValue + rgbRed * (255 - TransValue)) shr 8; | 
|---|
|  | 4541 | rgbGreen := (255 + PaletteChunk.Item[PaletteIndex].rgbGreen * | 
|---|
|  | 4542 | TransValue + rgbGreen * (255 - TransValue)) shr 8; | 
|---|
|  | 4543 | rgbBlue := (255 + PaletteChunk.Item[PaletteIndex].rgbBlue * | 
|---|
|  | 4544 | TransValue + rgbBlue * (255 - TransValue)) shr 8; | 
|---|
|  | 4545 | end; | 
|---|
|  | 4546 |  | 
|---|
|  | 4547 | {Move to next data} | 
|---|
|  | 4548 | inc(i); inc(CurBit, Header.BitmapInfo.bmiHeader.biBitCount); | 
|---|
|  | 4549 | until CurBit >= 8; | 
|---|
|  | 4550 | {Move to next source data} | 
|---|
|  | 4551 | //inc(Data); | 
|---|
|  | 4552 | until i >= Integer(W); | 
|---|
|  | 4553 |  | 
|---|
|  | 4554 | {Move pointers} | 
|---|
|  | 4555 | Longint(ImageData) := Longint(ImageData) + BytesPerRowDest; | 
|---|
|  | 4556 | if Stretch then j2 := trunc(j / FactorY) else j2 := j; | 
|---|
|  | 4557 | Longint(ImageSource) := Longint(ImageSourceOrg) - BytesPerRowSrc * j2; | 
|---|
|  | 4558 | end | 
|---|
|  | 4559 | end {Palette images} | 
|---|
|  | 4560 | end {case Header.BitmapInfo.bmiHeader.biBitCount}; | 
|---|
|  | 4561 |  | 
|---|
|  | 4562 | {Draws the new bitmap on the foreground} | 
|---|
|  | 4563 | BitBlt(DC, Rect.Left, Rect.Top, W, H, BufferDC, 0, 0, SRCCOPY); | 
|---|
|  | 4564 |  | 
|---|
|  | 4565 | {Free bitmap} | 
|---|
|  | 4566 | SelectObject(BufferDC, OldBitmap); | 
|---|
|  | 4567 | DeleteObject(BufferBitmap); | 
|---|
|  | 4568 | DeleteDC(BufferDC); | 
|---|
|  | 4569 | end; | 
|---|
|  | 4570 |  | 
|---|
|  | 4571 | {Draws the image into a canvas} | 
|---|
|  | 4572 | procedure TPngObject.Draw(ACanvas: TCanvas; const Rect: TRect); | 
|---|
|  | 4573 | var | 
|---|
|  | 4574 | Header: TChunkIHDR; | 
|---|
|  | 4575 | begin | 
|---|
|  | 4576 | {Quit in case there is no header, otherwise obtain it} | 
|---|
|  | 4577 | if Empty then Exit; | 
|---|
|  | 4578 | Header := Chunks.GetItem(0) as TChunkIHDR; | 
|---|
|  | 4579 |  | 
|---|
|  | 4580 | {Copy the data to the canvas} | 
|---|
|  | 4581 | case Self.TransparencyMode of | 
|---|
|  | 4582 | {$IFDEF PartialTransparentDraw} | 
|---|
|  | 4583 | ptmPartial: | 
|---|
|  | 4584 | DrawPartialTrans(ACanvas{$IFDEF UseDelphi}.Handle{$ENDIF}, Rect); | 
|---|
|  | 4585 | {$ENDIF} | 
|---|
|  | 4586 | ptmBit: DrawTransparentBitmap(ACanvas{$IFDEF UseDelphi}.Handle{$ENDIF}, | 
|---|
|  | 4587 | Header.ImageData, Header.BitmapInfo.bmiHeader, | 
|---|
|  | 4588 | pBitmapInfo(@Header.BitmapInfo), Rect, | 
|---|
|  | 4589 | {$IFDEF UseDelphi}ColorToRGB({$ENDIF}TransparentColor) | 
|---|
|  | 4590 | {$IFDEF UseDelphi}){$ENDIF} | 
|---|
|  | 4591 | else | 
|---|
|  | 4592 | begin | 
|---|
|  | 4593 | SetStretchBltMode(ACanvas{$IFDEF UseDelphi}.Handle{$ENDIF}, COLORONCOLOR); | 
|---|
|  | 4594 | StretchDiBits(ACanvas{$IFDEF UseDelphi}.Handle{$ENDIF}, Rect.Left, | 
|---|
|  | 4595 | Rect.Top, Rect.Right - Rect.Left, Rect.Bottom - Rect.Top, 0, 0, | 
|---|
|  | 4596 | Header.Width, Header.Height, Header.ImageData, | 
|---|
|  | 4597 | pBitmapInfo(@Header.BitmapInfo)^, DIB_RGB_COLORS, SRCCOPY) | 
|---|
|  | 4598 | end | 
|---|
|  | 4599 | end {case} | 
|---|
|  | 4600 | end; | 
|---|
|  | 4601 |  | 
|---|
|  | 4602 | {Characters for the header} | 
|---|
|  | 4603 | const | 
|---|
|  | 4604 | PngHeader: Array[0..7] of Char = (#137, #80, #78, #71, #13, #10, #26, #10); | 
|---|
|  | 4605 |  | 
|---|
|  | 4606 | {Loads the image from a stream of data} | 
|---|
|  | 4607 | procedure TPngObject.LoadFromStream(Stream: TStream); | 
|---|
|  | 4608 | var | 
|---|
|  | 4609 | Header    : Array[0..7] of Char; | 
|---|
|  | 4610 | HasIDAT   : Boolean; | 
|---|
|  | 4611 |  | 
|---|
|  | 4612 | {Chunks reading} | 
|---|
|  | 4613 | ChunkCount : Cardinal; | 
|---|
|  | 4614 | ChunkLength: Cardinal; | 
|---|
|  | 4615 | ChunkName  : TChunkName; | 
|---|
|  | 4616 | begin | 
|---|
|  | 4617 | {Initialize before start loading chunks} | 
|---|
|  | 4618 | ChunkCount := 0; | 
|---|
|  | 4619 | ClearChunks(); | 
|---|
|  | 4620 | {Reads the header} | 
|---|
|  | 4621 | Stream.Read(Header[0], 8); | 
|---|
|  | 4622 |  | 
|---|
|  | 4623 | {Test if the header matches} | 
|---|
|  | 4624 | if Header <> PngHeader then | 
|---|
|  | 4625 | begin | 
|---|
|  | 4626 | RaiseError(EPNGInvalidFileHeader, EPNGInvalidFileHeaderText); | 
|---|
|  | 4627 | Exit; | 
|---|
|  | 4628 | end; | 
|---|
|  | 4629 |  | 
|---|
|  | 4630 |  | 
|---|
|  | 4631 | HasIDAT := FALSE; | 
|---|
|  | 4632 | Chunks.Count := 10; | 
|---|
|  | 4633 |  | 
|---|
|  | 4634 | {Load chunks} | 
|---|
|  | 4635 | repeat | 
|---|
|  | 4636 | inc(ChunkCount);  {Increment number of chunks} | 
|---|
|  | 4637 | if Chunks.Count < ChunkCount then  {Resize the chunks list if needed} | 
|---|
|  | 4638 | Chunks.Count := Chunks.Count + 10; | 
|---|
|  | 4639 |  | 
|---|
|  | 4640 | {Reads chunk length and invert since it is in network order} | 
|---|
|  | 4641 | {also checks the Read method return, if it returns 0, it} | 
|---|
|  | 4642 | {means that no bytes was readed, probably because it reached} | 
|---|
|  | 4643 | {the end of the file} | 
|---|
|  | 4644 | if Stream.Read(ChunkLength, 4) = 0 then | 
|---|
|  | 4645 | begin | 
|---|
|  | 4646 | {In case it found the end of the file here} | 
|---|
|  | 4647 | Chunks.Count := ChunkCount - 1; | 
|---|
|  | 4648 | RaiseError(EPNGUnexpectedEnd, EPNGUnexpectedEndText); | 
|---|
|  | 4649 | end; | 
|---|
|  | 4650 |  | 
|---|
|  | 4651 | ChunkLength := ByteSwap(ChunkLength); | 
|---|
|  | 4652 | {Reads chunk name} | 
|---|
|  | 4653 | Stream.Read(Chunkname, 4); | 
|---|
|  | 4654 |  | 
|---|
|  | 4655 | {Here we check if the first chunk is the Header which is necessary} | 
|---|
|  | 4656 | {to the file in order to be a valid Portable Network Graphics image} | 
|---|
|  | 4657 | if (ChunkCount = 1) and (ChunkName <> 'IHDR') then | 
|---|
|  | 4658 | begin | 
|---|
|  | 4659 | Chunks.Count := ChunkCount - 1; | 
|---|
|  | 4660 | RaiseError(EPNGIHDRNotFirst, EPNGIHDRNotFirstText); | 
|---|
|  | 4661 | exit; | 
|---|
|  | 4662 | end; | 
|---|
|  | 4663 |  | 
|---|
|  | 4664 | {Has a previous IDAT} | 
|---|
|  | 4665 | if (HasIDAT and (ChunkName = 'IDAT')) or (ChunkName = 'cHRM') then | 
|---|
|  | 4666 | begin | 
|---|
|  | 4667 | dec(ChunkCount); | 
|---|
|  | 4668 | Stream.Seek(ChunkLength + 4, soFromCurrent); | 
|---|
|  | 4669 | Continue; | 
|---|
|  | 4670 | end; | 
|---|
|  | 4671 | {Tell it has an IDAT chunk} | 
|---|
|  | 4672 | if ChunkName = 'IDAT' then HasIDAT := TRUE; | 
|---|
|  | 4673 |  | 
|---|
|  | 4674 | {Creates object for this chunk} | 
|---|
|  | 4675 | Chunks.SetItem(ChunkCount - 1, CreateClassChunk(Self, ChunkName)); | 
|---|
|  | 4676 |  | 
|---|
|  | 4677 | {Check if the chunk is critical and unknown} | 
|---|
|  | 4678 | {$IFDEF ErrorOnUnknownCritical} | 
|---|
|  | 4679 | if (TChunk(Chunks.Item[ChunkCount - 1]).ClassType = TChunk) and | 
|---|
|  | 4680 | ((Byte(ChunkName[0]) AND $20) = 0) and (ChunkName <> '') then | 
|---|
|  | 4681 | begin | 
|---|
|  | 4682 | Chunks.Count := ChunkCount; | 
|---|
|  | 4683 | RaiseError(EPNGUnknownCriticalChunk, EPNGUnknownCriticalChunkText); | 
|---|
|  | 4684 | end; | 
|---|
|  | 4685 | {$ENDIF} | 
|---|
|  | 4686 |  | 
|---|
|  | 4687 | {Loads it} | 
|---|
|  | 4688 | try if not TChunk(Chunks.Item[ChunkCount - 1]).LoadFromStream(Stream, | 
|---|
|  | 4689 | ChunkName, ChunkLength) then break; | 
|---|
|  | 4690 | except | 
|---|
|  | 4691 | Chunks.Count := ChunkCount; | 
|---|
|  | 4692 | raise; | 
|---|
|  | 4693 | end; | 
|---|
|  | 4694 |  | 
|---|
|  | 4695 | {Terminates when it reaches the IEND chunk} | 
|---|
|  | 4696 | until (ChunkName = 'IEND'); | 
|---|
|  | 4697 |  | 
|---|
|  | 4698 | {Resize the list to the appropriate size} | 
|---|
|  | 4699 | Chunks.Count := ChunkCount; | 
|---|
|  | 4700 |  | 
|---|
|  | 4701 | {Check if there is data} | 
|---|
|  | 4702 | if not HasIDAT then | 
|---|
|  | 4703 | RaiseError(EPNGNoImageData, EPNGNoImageDataText); | 
|---|
|  | 4704 | end; | 
|---|
|  | 4705 |  | 
|---|
|  | 4706 | {Changing height is not supported} | 
|---|
|  | 4707 | procedure TPngObject.SetHeight(Value: Integer); | 
|---|
|  | 4708 | begin | 
|---|
|  | 4709 | RaiseError(EPNGError, EPNGCannotChangeSizeText); | 
|---|
|  | 4710 | end; | 
|---|
|  | 4711 |  | 
|---|
|  | 4712 | {Changing width is not supported} | 
|---|
|  | 4713 | procedure TPngObject.SetWidth(Value: Integer); | 
|---|
|  | 4714 | begin | 
|---|
|  | 4715 | RaiseError(EPNGError, EPNGCannotChangeSizeText); | 
|---|
|  | 4716 | end; | 
|---|
|  | 4717 |  | 
|---|
|  | 4718 | {$IFDEF UseDelphi} | 
|---|
|  | 4719 | {Saves to clipboard format (thanks to Antoine Pottern)} | 
|---|
|  | 4720 | procedure TPNGObject.SaveToClipboardFormat(var AFormat: Word; | 
|---|
|  | 4721 | var AData: THandle; var APalette: HPalette); | 
|---|
|  | 4722 | begin | 
|---|
|  | 4723 | with TBitmap.Create do | 
|---|
|  | 4724 | try | 
|---|
|  | 4725 | Width := Self.Width; | 
|---|
|  | 4726 | Height := Self.Height; | 
|---|
|  | 4727 | Self.Draw(Canvas, Rect(0, 0, Width, Height)); | 
|---|
|  | 4728 | SaveToClipboardFormat(AFormat, AData, APalette); | 
|---|
|  | 4729 | finally | 
|---|
|  | 4730 | Free; | 
|---|
|  | 4731 | end {try} | 
|---|
|  | 4732 | end; | 
|---|
|  | 4733 |  | 
|---|
|  | 4734 | {Loads data from clipboard} | 
|---|
|  | 4735 | procedure TPngObject.LoadFromClipboardFormat(AFormat: Word; | 
|---|
|  | 4736 | AData: THandle; APalette: HPalette); | 
|---|
|  | 4737 | begin | 
|---|
|  | 4738 | with TBitmap.Create do | 
|---|
|  | 4739 | try | 
|---|
|  | 4740 | LoadFromClipboardFormat(AFormat, AData, APalette); | 
|---|
|  | 4741 | Self.AssignHandle(Handle, False, 0); | 
|---|
|  | 4742 | finally | 
|---|
|  | 4743 | Free; | 
|---|
|  | 4744 | end {try} | 
|---|
|  | 4745 | end; | 
|---|
|  | 4746 |  | 
|---|
|  | 4747 | {Returns if the image is transparent} | 
|---|
|  | 4748 | function TPngObject.GetTransparent: Boolean; | 
|---|
|  | 4749 | begin | 
|---|
|  | 4750 | Result := (TransparencyMode <> ptmNone); | 
|---|
|  | 4751 | end; | 
|---|
|  | 4752 |  | 
|---|
|  | 4753 | {$ENDIF} | 
|---|
|  | 4754 |  | 
|---|
|  | 4755 | {Saving the PNG image to a stream of data} | 
|---|
|  | 4756 | procedure TPngObject.SaveToStream(Stream: TStream); | 
|---|
|  | 4757 | var | 
|---|
|  | 4758 | j: Integer; | 
|---|
|  | 4759 | begin | 
|---|
|  | 4760 | {Reads the header} | 
|---|
|  | 4761 | Stream.Write(PNGHeader[0], 8); | 
|---|
|  | 4762 | {Write each chunk} | 
|---|
|  | 4763 | FOR j := 0 TO Chunks.Count - 1 DO | 
|---|
|  | 4764 | Chunks.Item[j].SaveToStream(Stream) | 
|---|
|  | 4765 | end; | 
|---|
|  | 4766 |  | 
|---|
|  | 4767 | {Prepares the Header chunk} | 
|---|
|  | 4768 | procedure BuildHeader(Header: TChunkIHDR; Handle: HBitmap; Info: pBitmap; | 
|---|
|  | 4769 | HasPalette: Boolean); | 
|---|
|  | 4770 | var | 
|---|
|  | 4771 | DC: HDC; | 
|---|
|  | 4772 | begin | 
|---|
|  | 4773 | {Set width and height} | 
|---|
|  | 4774 | Header.Width := Info.bmWidth; | 
|---|
|  | 4775 | Header.Height := abs(Info.bmHeight); | 
|---|
|  | 4776 | {Set bit depth} | 
|---|
|  | 4777 | if Info.bmBitsPixel >= 16 then | 
|---|
|  | 4778 | Header.BitDepth := 8 else Header.BitDepth := Info.bmBitsPixel; | 
|---|
|  | 4779 | {Set color type} | 
|---|
|  | 4780 | if Info.bmBitsPixel >= 16 then | 
|---|
|  | 4781 | Header.ColorType := COLOR_RGB else Header.ColorType := COLOR_PALETTE; | 
|---|
|  | 4782 | {Set other info} | 
|---|
|  | 4783 | Header.CompressionMethod := 0;  {deflate/inflate} | 
|---|
|  | 4784 | Header.InterlaceMethod := 0;    {no interlace} | 
|---|
|  | 4785 |  | 
|---|
|  | 4786 | {Prepares bitmap headers to hold data} | 
|---|
|  | 4787 | Header.PrepareImageData(); | 
|---|
|  | 4788 | {Copy image data} | 
|---|
|  | 4789 | DC := CreateCompatibleDC(0); | 
|---|
|  | 4790 | GetDIBits(DC, Handle, 0, Header.Height, Header.ImageData, | 
|---|
|  | 4791 | pBitmapInfo(@Header.BitmapInfo)^, DIB_RGB_COLORS); | 
|---|
|  | 4792 | DeleteDC(DC); | 
|---|
|  | 4793 | end; | 
|---|
|  | 4794 |  | 
|---|
|  | 4795 | {Loads the image from a resource} | 
|---|
|  | 4796 | procedure TPngObject.LoadFromResourceName(Instance: HInst; | 
|---|
|  | 4797 | const Name: String); | 
|---|
|  | 4798 | var | 
|---|
|  | 4799 | ResStream: TResourceStream; | 
|---|
|  | 4800 | begin | 
|---|
|  | 4801 | {Creates an especial stream to load from the resource} | 
|---|
|  | 4802 | try ResStream := TResourceStream.Create(Instance, Name, RT_RCDATA); | 
|---|
|  | 4803 | except RaiseError(EPNGCouldNotLoadResource, EPNGCouldNotLoadResourceText); | 
|---|
|  | 4804 | exit; end; | 
|---|
|  | 4805 |  | 
|---|
|  | 4806 | {Loads the png image from the resource} | 
|---|
|  | 4807 | try | 
|---|
|  | 4808 | LoadFromStream(ResStream); | 
|---|
|  | 4809 | finally | 
|---|
|  | 4810 | ResStream.Free; | 
|---|
|  | 4811 | end; | 
|---|
|  | 4812 | end; | 
|---|
|  | 4813 |  | 
|---|
|  | 4814 | {Loads the png from a resource ID} | 
|---|
|  | 4815 | procedure TPngObject.LoadFromResourceID(Instance: HInst; ResID: Integer); | 
|---|
|  | 4816 | begin | 
|---|
|  | 4817 | LoadFromResourceName(Instance, String(ResID)); | 
|---|
|  | 4818 | end; | 
|---|
|  | 4819 |  | 
|---|
|  | 4820 | {Assigns this tpngobject to another object} | 
|---|
|  | 4821 | procedure TPngObject.AssignTo(Dest: TPersistent); | 
|---|
|  | 4822 | {$IFDEF UseDelphi} | 
|---|
|  | 4823 | var | 
|---|
|  | 4824 | DeskDC: HDC; | 
|---|
|  | 4825 | TRNS: TChunkTRNS; | 
|---|
|  | 4826 | {$ENDIF} | 
|---|
|  | 4827 | begin | 
|---|
|  | 4828 | {If the destination is also a TPNGObject make it assign} | 
|---|
|  | 4829 | {this one} | 
|---|
|  | 4830 | if Dest is TPNGObject then | 
|---|
|  | 4831 | TPNGObject(Dest).AssignPNG(Self) | 
|---|
|  | 4832 | {$IFDEF UseDelphi} | 
|---|
|  | 4833 | {In case the destination is a bitmap} | 
|---|
|  | 4834 | else if (Dest is TBitmap) and HeaderPresent then | 
|---|
|  | 4835 | begin | 
|---|
|  | 4836 | {Tests for the best pixelformat | 
|---|
|  | 4837 | case Header.BitmapInfo.bmiHeader.biBitCount of | 
|---|
|  | 4838 | 1: TBitmap(Dest).PixelFormat := pf1Bit; | 
|---|
|  | 4839 | 4: TBitmap(Dest).PixelFormat := pf4Bit; | 
|---|
|  | 4840 | 8: TBitmap(Dest).PixelFormat := pf8Bit; | 
|---|
|  | 4841 | 24: TBitmap(Dest).PixelFormat := pf24Bit; | 
|---|
|  | 4842 | 32: TBitmap(Dest).PixelFormat := pf32Bit; | 
|---|
|  | 4843 | end {case Header.BitmapInfo.bmiHeader.biBitCount}; | 
|---|
|  | 4844 |  | 
|---|
|  | 4845 | {Device context} | 
|---|
|  | 4846 | DeskDC := GetDC(0); | 
|---|
|  | 4847 | {Copy the data} | 
|---|
|  | 4848 | TBitmap(Dest).Handle := CreateDIBitmap(DeskDC, | 
|---|
|  | 4849 | Header.BitmapInfo.bmiHeader, CBM_INIT, Header.ImageData, | 
|---|
|  | 4850 | pBitmapInfo(@Header.BitmapInfo)^, DIB_RGB_COLORS); | 
|---|
|  | 4851 | ReleaseDC(0, DeskDC); | 
|---|
|  | 4852 |  | 
|---|
|  | 4853 | {Copy transparency mode} | 
|---|
|  | 4854 | if (TransparencyMode = ptmBit) then | 
|---|
|  | 4855 | begin | 
|---|
|  | 4856 | TRNS := Chunks.ItemFromClass(TChunkTRNS) as TChunkTRNS; | 
|---|
|  | 4857 | TBitmap(Dest).TransparentColor := TRNS.TransparentColor; | 
|---|
|  | 4858 | TBitmap(Dest).Transparent := True | 
|---|
|  | 4859 | end {if (TransparencyMode = ptmBit)} | 
|---|
|  | 4860 |  | 
|---|
|  | 4861 | end | 
|---|
|  | 4862 | else | 
|---|
|  | 4863 | {Unknown destination kind, } | 
|---|
|  | 4864 | inherited AssignTo(Dest); | 
|---|
|  | 4865 | {$ENDIF} | 
|---|
|  | 4866 | end; | 
|---|
|  | 4867 |  | 
|---|
|  | 4868 | {Assigns from a bitmap object} | 
|---|
|  | 4869 | procedure TPngObject.AssignHandle(Handle: HBitmap; Transparent: Boolean; | 
|---|
|  | 4870 | TransparentColor: ColorRef); | 
|---|
|  | 4871 | var | 
|---|
|  | 4872 | BitmapInfo: Windows.TBitmap; | 
|---|
|  | 4873 | HasPalette: Boolean; | 
|---|
|  | 4874 |  | 
|---|
|  | 4875 | {Chunks} | 
|---|
|  | 4876 | Header: TChunkIHDR; | 
|---|
|  | 4877 | PLTE: TChunkPLTE; | 
|---|
|  | 4878 | IDAT: TChunkIDAT; | 
|---|
|  | 4879 | IEND: TChunkIEND; | 
|---|
|  | 4880 | TRNS: TChunkTRNS; | 
|---|
|  | 4881 | begin | 
|---|
|  | 4882 | {Obtain bitmap info} | 
|---|
|  | 4883 | GetObject(Handle, SizeOf(BitmapInfo), @BitmapInfo); | 
|---|
|  | 4884 |  | 
|---|
|  | 4885 | {Only bit depths 1, 4 and 8 needs a palette} | 
|---|
|  | 4886 | HasPalette := (BitmapInfo.bmBitsPixel < 16); | 
|---|
|  | 4887 |  | 
|---|
|  | 4888 | {Clear old chunks and prepare} | 
|---|
|  | 4889 | ClearChunks(); | 
|---|
|  | 4890 |  | 
|---|
|  | 4891 | {Create the chunks} | 
|---|
|  | 4892 | Header := TChunkIHDR.Create(Self); | 
|---|
|  | 4893 | if HasPalette then PLTE := TChunkPLTE.Create(Self) else PLTE := nil; | 
|---|
|  | 4894 | if Transparent then TRNS := TChunkTRNS.Create(Self) else TRNS := nil; | 
|---|
|  | 4895 | IDAT := TChunkIDAT.Create(Self); | 
|---|
|  | 4896 | IEND := TChunkIEND.Create(Self); | 
|---|
|  | 4897 |  | 
|---|
|  | 4898 | {Add chunks} | 
|---|
|  | 4899 | TPNGPointerList(Chunks).Add(Header); | 
|---|
|  | 4900 | if HasPalette then TPNGPointerList(Chunks).Add(PLTE); | 
|---|
|  | 4901 | if Transparent then TPNGPointerList(Chunks).Add(TRNS); | 
|---|
|  | 4902 | TPNGPointerList(Chunks).Add(IDAT); | 
|---|
|  | 4903 | TPNGPointerList(Chunks).Add(IEND); | 
|---|
|  | 4904 |  | 
|---|
|  | 4905 | {This method will fill the Header chunk with bitmap information} | 
|---|
|  | 4906 | {and copy the image data} | 
|---|
|  | 4907 | BuildHeader(Header, Handle, @BitmapInfo, HasPalette); | 
|---|
|  | 4908 | {In case there is a image data, set the PLTE chunk fCount variable} | 
|---|
|  | 4909 | {to the actual number of palette colors which is 2^(Bits for each pixel)} | 
|---|
|  | 4910 | if HasPalette then PLTE.fCount := 1 shl BitmapInfo.bmBitsPixel; | 
|---|
|  | 4911 |  | 
|---|
|  | 4912 | {In case it is a transparent bitmap, prepares it} | 
|---|
|  | 4913 | if Transparent then TRNS.TransparentColor := TransparentColor; | 
|---|
|  | 4914 |  | 
|---|
|  | 4915 | end; | 
|---|
|  | 4916 |  | 
|---|
|  | 4917 | {Assigns from another PNG} | 
|---|
|  | 4918 | procedure TPngObject.AssignPNG(Source: TPNGObject); | 
|---|
|  | 4919 | var | 
|---|
|  | 4920 | J: Integer; | 
|---|
|  | 4921 | begin | 
|---|
|  | 4922 | {Copy properties} | 
|---|
|  | 4923 | InterlaceMethod := Source.InterlaceMethod; | 
|---|
|  | 4924 | MaxIdatSize := Source.MaxIdatSize; | 
|---|
|  | 4925 | CompressionLevel := Source.CompressionLevel; | 
|---|
|  | 4926 | Filters := Source.Filters; | 
|---|
|  | 4927 |  | 
|---|
|  | 4928 | {Clear old chunks and prepare} | 
|---|
|  | 4929 | ClearChunks(); | 
|---|
|  | 4930 | Chunks.Count := Source.Chunks.Count; | 
|---|
|  | 4931 | {Create chunks and makes a copy from the source} | 
|---|
|  | 4932 | FOR J := 0 TO Chunks.Count - 1 DO | 
|---|
|  | 4933 | with Source.Chunks do | 
|---|
|  | 4934 | begin | 
|---|
|  | 4935 | Chunks.SetItem(J, TChunkClass(TChunk(Item[J]).ClassType).Create(Self)); | 
|---|
|  | 4936 | TChunk(Chunks.Item[J]).Assign(TChunk(Item[J])); | 
|---|
|  | 4937 | end {with}; | 
|---|
|  | 4938 | end; | 
|---|
|  | 4939 |  | 
|---|
|  | 4940 | {Returns a alpha data scanline} | 
|---|
|  | 4941 | function TPngObject.GetAlphaScanline(const LineIndex: Integer): pByteArray; | 
|---|
|  | 4942 | begin | 
|---|
|  | 4943 | with Header do | 
|---|
|  | 4944 | if (ColorType = COLOR_RGBALPHA) or (ColorType = COLOR_GRAYSCALEALPHA) then | 
|---|
|  | 4945 | Longint(Result) := Longint(ImageAlpha) + (LineIndex * Longint(Width)) | 
|---|
|  | 4946 | else Result := nil;  {In case the image does not use alpha information} | 
|---|
|  | 4947 | end; | 
|---|
|  | 4948 |  | 
|---|
|  | 4949 | {$IFDEF Store16bits} | 
|---|
|  | 4950 | {Returns a png data extra scanline} | 
|---|
|  | 4951 | function TPngObject.GetExtraScanline(const LineIndex: Integer): Pointer; | 
|---|
|  | 4952 | begin | 
|---|
|  | 4953 | with Header do | 
|---|
|  | 4954 | Longint(Result) := (Longint(ExtraImageData) + ((Longint(Height) - 1) * | 
|---|
|  | 4955 | BytesPerRow)) - (LineIndex * BytesPerRow); | 
|---|
|  | 4956 | end; | 
|---|
|  | 4957 | {$ENDIF} | 
|---|
|  | 4958 |  | 
|---|
|  | 4959 | {Returns a png data scanline} | 
|---|
|  | 4960 | function TPngObject.GetScanline(const LineIndex: Integer): Pointer; | 
|---|
|  | 4961 | begin | 
|---|
|  | 4962 | with Header do | 
|---|
|  | 4963 | Longint(Result) := (Longint(ImageData) + ((Longint(Height) - 1) * | 
|---|
|  | 4964 | BytesPerRow)) - (LineIndex * BytesPerRow); | 
|---|
|  | 4965 | end; | 
|---|
|  | 4966 |  | 
|---|
|  | 4967 | {Initialize gamma table} | 
|---|
|  | 4968 | procedure TPngObject.InitializeGamma; | 
|---|
|  | 4969 | var | 
|---|
|  | 4970 | i: Integer; | 
|---|
|  | 4971 | begin | 
|---|
|  | 4972 | {Build gamma table as if there was no gamma} | 
|---|
|  | 4973 | FOR i := 0 to 255 do | 
|---|
|  | 4974 | begin | 
|---|
|  | 4975 | GammaTable[i] := i; | 
|---|
|  | 4976 | InverseGamma[i] := i; | 
|---|
|  | 4977 | end {for i} | 
|---|
|  | 4978 | end; | 
|---|
|  | 4979 |  | 
|---|
|  | 4980 | {Returns the transparency mode used by this png} | 
|---|
|  | 4981 | function TPngObject.GetTransparencyMode: TPNGTransparencyMode; | 
|---|
|  | 4982 | var | 
|---|
|  | 4983 | TRNS: TChunkTRNS; | 
|---|
|  | 4984 | begin | 
|---|
|  | 4985 | with Header do | 
|---|
|  | 4986 | begin | 
|---|
|  | 4987 | Result := ptmNone; {Default result} | 
|---|
|  | 4988 | {Gets the TRNS chunk pointer} | 
|---|
|  | 4989 | TRNS := Chunks.ItemFromClass(TChunkTRNS) as TChunkTRNS; | 
|---|
|  | 4990 |  | 
|---|
|  | 4991 | {Test depending on the color type} | 
|---|
|  | 4992 | case ColorType of | 
|---|
|  | 4993 | {This modes are always partial} | 
|---|
|  | 4994 | COLOR_RGBALPHA, COLOR_GRAYSCALEALPHA: Result := ptmPartial; | 
|---|
|  | 4995 | {This modes support bit transparency} | 
|---|
|  | 4996 | COLOR_RGB, COLOR_GRAYSCALE: if TRNS <> nil then Result := ptmBit; | 
|---|
|  | 4997 | {Supports booth translucid and bit} | 
|---|
|  | 4998 | COLOR_PALETTE: | 
|---|
|  | 4999 | {A TRNS chunk must be present, otherwise it won't support transparency} | 
|---|
|  | 5000 | if TRNS <> nil then | 
|---|
|  | 5001 | if TRNS.BitTransparency then | 
|---|
|  | 5002 | Result := ptmBit else Result := ptmPartial | 
|---|
|  | 5003 | end {case} | 
|---|
|  | 5004 |  | 
|---|
|  | 5005 | end {with Header} | 
|---|
|  | 5006 | end; | 
|---|
|  | 5007 |  | 
|---|
|  | 5008 | {Add a text chunk} | 
|---|
|  | 5009 | procedure TPngObject.AddtEXt(const Keyword, Text: String); | 
|---|
|  | 5010 | var | 
|---|
|  | 5011 | TextChunk: TChunkTEXT; | 
|---|
|  | 5012 | begin | 
|---|
|  | 5013 | TextChunk := Chunks.Add(TChunkText) as TChunkTEXT; | 
|---|
|  | 5014 | TextChunk.Keyword := Keyword; | 
|---|
|  | 5015 | TextChunk.Text := Text; | 
|---|
|  | 5016 | end; | 
|---|
|  | 5017 |  | 
|---|
|  | 5018 | {Add a text chunk} | 
|---|
|  | 5019 | procedure TPngObject.AddzTXt(const Keyword, Text: String); | 
|---|
|  | 5020 | var | 
|---|
|  | 5021 | TextChunk: TChunkzTXt; | 
|---|
|  | 5022 | begin | 
|---|
|  | 5023 | TextChunk := Chunks.Add(TChunkzTXt) as TChunkzTXt; | 
|---|
|  | 5024 | TextChunk.Keyword := Keyword; | 
|---|
|  | 5025 | TextChunk.Text := Text; | 
|---|
|  | 5026 | end; | 
|---|
|  | 5027 |  | 
|---|
|  | 5028 | {Removes the image transparency} | 
|---|
|  | 5029 | procedure TPngObject.RemoveTransparency; | 
|---|
|  | 5030 | var | 
|---|
|  | 5031 | TRNS: TChunkTRNS; | 
|---|
|  | 5032 | begin | 
|---|
|  | 5033 | TRNS := Chunks.ItemFromClass(TChunkTRNS) as TChunkTRNS; | 
|---|
|  | 5034 | if TRNS <> nil then Chunks.RemoveChunk(TRNS) | 
|---|
|  | 5035 | end; | 
|---|
|  | 5036 |  | 
|---|
|  | 5037 | {Generates alpha information} | 
|---|
|  | 5038 | procedure TPngObject.CreateAlpha; | 
|---|
|  | 5039 | var | 
|---|
|  | 5040 | TRNS: TChunkTRNS; | 
|---|
|  | 5041 | begin | 
|---|
|  | 5042 | {Generates depending on the color type} | 
|---|
|  | 5043 | with Header do | 
|---|
|  | 5044 | case ColorType of | 
|---|
|  | 5045 | {Png allocates different memory space to hold alpha information} | 
|---|
|  | 5046 | {for these types} | 
|---|
|  | 5047 | COLOR_GRAYSCALE, COLOR_RGB: | 
|---|
|  | 5048 | begin | 
|---|
|  | 5049 | {Transform into the appropriate color type} | 
|---|
|  | 5050 | if ColorType = COLOR_GRAYSCALE then | 
|---|
|  | 5051 | ColorType := COLOR_GRAYSCALEALPHA | 
|---|
|  | 5052 | else ColorType := COLOR_RGBALPHA; | 
|---|
|  | 5053 | {Allocates memory to hold alpha information} | 
|---|
|  | 5054 | GetMem(ImageAlpha, Integer(Width) * Integer(Height)); | 
|---|
|  | 5055 | FillChar(ImageAlpha^, Integer(Width) * Integer(Height), #255); | 
|---|
|  | 5056 | end; | 
|---|
|  | 5057 | {Palette uses the TChunktRNS to store alpha} | 
|---|
|  | 5058 | COLOR_PALETTE: | 
|---|
|  | 5059 | begin | 
|---|
|  | 5060 | {Gets/creates TRNS chunk} | 
|---|
|  | 5061 | if Chunks.ItemFromClass(TChunkTRNS) = nil then | 
|---|
|  | 5062 | TRNS := Chunks.Add(TChunkTRNS) as TChunkTRNS | 
|---|
|  | 5063 | else | 
|---|
|  | 5064 | TRNS := Chunks.ItemFromClass(TChunkTRNS) as TChunkTRNS; | 
|---|
|  | 5065 |  | 
|---|
|  | 5066 | {Prepares the TRNS chunk} | 
|---|
|  | 5067 | with TRNS do | 
|---|
|  | 5068 | begin | 
|---|
|  | 5069 | Fillchar(PaletteValues[0], 256, 255); | 
|---|
|  | 5070 | fDataSize := 1 shl Header.BitDepth; | 
|---|
|  | 5071 | fBitTransparency := False | 
|---|
|  | 5072 | end {with Chunks.Add}; | 
|---|
|  | 5073 | end; | 
|---|
|  | 5074 | end {case Header.ColorType} | 
|---|
|  | 5075 |  | 
|---|
|  | 5076 | end; | 
|---|
|  | 5077 |  | 
|---|
|  | 5078 | {Returns transparent color} | 
|---|
|  | 5079 | function TPngObject.GetTransparentColor: TColor; | 
|---|
|  | 5080 | var | 
|---|
|  | 5081 | TRNS: TChunkTRNS; | 
|---|
|  | 5082 | begin | 
|---|
|  | 5083 | TRNS := Chunks.ItemFromClass(TChunkTRNS) as TChunkTRNS; | 
|---|
|  | 5084 | {Reads the transparency chunk to get this info} | 
|---|
|  | 5085 | if Assigned(TRNS) then Result := TRNS.TransparentColor | 
|---|
|  | 5086 | else Result := 0 | 
|---|
|  | 5087 | end; | 
|---|
|  | 5088 |  | 
|---|
|  | 5089 | {$OPTIMIZATION OFF} | 
|---|
|  | 5090 | procedure TPngObject.SetTransparentColor(const Value: TColor); | 
|---|
|  | 5091 | var | 
|---|
|  | 5092 | TRNS: TChunkTRNS; | 
|---|
|  | 5093 | begin | 
|---|
|  | 5094 | if HeaderPresent then | 
|---|
|  | 5095 | {Tests the ColorType} | 
|---|
|  | 5096 | case Header.ColorType of | 
|---|
|  | 5097 | {Not allowed for this modes} | 
|---|
|  | 5098 | COLOR_RGBALPHA, COLOR_GRAYSCALEALPHA: Self.RaiseError( | 
|---|
|  | 5099 | EPNGCannotChangeTransparent, EPNGCannotChangeTransparentText); | 
|---|
|  | 5100 | {Allowed} | 
|---|
|  | 5101 | COLOR_PALETTE, COLOR_RGB, COLOR_GRAYSCALE: | 
|---|
|  | 5102 | begin | 
|---|
|  | 5103 | TRNS := Chunks.ItemFromClass(TChunkTRNS) as TChunkTRNS; | 
|---|
|  | 5104 | if not Assigned(TRNS) then TRNS := Chunks.Add(TChunkTRNS) as TChunkTRNS; | 
|---|
|  | 5105 |  | 
|---|
|  | 5106 | {Sets the transparency value from TRNS chunk} | 
|---|
|  | 5107 | TRNS.TransparentColor := {$IFDEF UseDelphi}ColorToRGB({$ENDIF}Value{$IFDEF UseDelphi}){$ENDIF} | 
|---|
|  | 5108 | end {COLOR_PALETTE, COLOR_RGB, COLOR_GRAYSCALE)} | 
|---|
|  | 5109 | end {case} | 
|---|
|  | 5110 | end; | 
|---|
|  | 5111 |  | 
|---|
|  | 5112 | {Returns if header is present} | 
|---|
|  | 5113 | function TPngObject.HeaderPresent: Boolean; | 
|---|
|  | 5114 | begin | 
|---|
|  | 5115 | Result := ((Chunks.Count <> 0) and (Chunks.Item[0] is TChunkIHDR)) | 
|---|
|  | 5116 | end; | 
|---|
|  | 5117 |  | 
|---|
|  | 5118 | {Returns pixel for png using palette and grayscale} | 
|---|
|  | 5119 | function GetByteArrayPixel(const png: TPngObject; const X, Y: Integer): TColor; | 
|---|
|  | 5120 | var | 
|---|
|  | 5121 | ByteData: Byte; | 
|---|
|  | 5122 | DataDepth: Byte; | 
|---|
|  | 5123 | begin | 
|---|
|  | 5124 | with png, Header do | 
|---|
|  | 5125 | begin | 
|---|
|  | 5126 | {Make sure the bitdepth is not greater than 8} | 
|---|
|  | 5127 | DataDepth := BitDepth; | 
|---|
|  | 5128 | if DataDepth > 8 then DataDepth := 8; | 
|---|
|  | 5129 | {Obtains the byte containing this pixel} | 
|---|
|  | 5130 | ByteData := pByteArray(png.Scanline[Y])^[X div (8 div DataDepth)]; | 
|---|
|  | 5131 | {Moves the bits we need to the right} | 
|---|
|  | 5132 | ByteData := (ByteData shr ((8 - DataDepth) - | 
|---|
|  | 5133 | (X mod (8 div DataDepth)) * DataDepth)); | 
|---|
|  | 5134 | {Discard the unwanted pixels} | 
|---|
|  | 5135 | ByteData:= ByteData and ($FF shr (8 - DataDepth)); | 
|---|
|  | 5136 |  | 
|---|
|  | 5137 | {For palette mode map the palette entry and for grayscale convert and | 
|---|
|  | 5138 | returns the intensity} | 
|---|
|  | 5139 | case ColorType of | 
|---|
|  | 5140 | COLOR_PALETTE: | 
|---|
|  | 5141 | with TChunkPLTE(png.Chunks.ItemFromClass(TChunkPLTE)).Item[ByteData] do | 
|---|
|  | 5142 | Result := rgb(GammaTable[rgbRed], GammaTable[rgbGreen], | 
|---|
|  | 5143 | GammaTable[rgbBlue]); | 
|---|
|  | 5144 | COLOR_GRAYSCALE: | 
|---|
|  | 5145 | begin | 
|---|
|  | 5146 | if BitDepth = 1 | 
|---|
|  | 5147 | then ByteData := GammaTable[Byte(ByteData * 255)] | 
|---|
|  | 5148 | else ByteData := GammaTable[Byte(ByteData * ((1 shl DataDepth) + 1))]; | 
|---|
|  | 5149 | Result := rgb(ByteData, ByteData, ByteData); | 
|---|
|  | 5150 | end; | 
|---|
|  | 5151 | else Result := 0; | 
|---|
|  | 5152 | end {case}; | 
|---|
|  | 5153 | end {with} | 
|---|
|  | 5154 | end; | 
|---|
|  | 5155 |  | 
|---|
|  | 5156 | {In case vcl units are not being used} | 
|---|
|  | 5157 | {$IFNDEF UseDelphi} | 
|---|
|  | 5158 | function ColorToRGB(const Color: TColor): COLORREF; | 
|---|
|  | 5159 | begin | 
|---|
|  | 5160 | Result := Color | 
|---|
|  | 5161 | end; | 
|---|
|  | 5162 | {$ENDIF} | 
|---|
|  | 5163 |  | 
|---|
|  | 5164 | {Sets a pixel for grayscale and palette pngs} | 
|---|
|  | 5165 | procedure SetByteArrayPixel(const png: TPngObject; const X, Y: Integer; | 
|---|
|  | 5166 | const Value: TColor); | 
|---|
|  | 5167 | const | 
|---|
|  | 5168 | ClearFlag: Array[1..8] of Integer = (1, 3, 0, 15, 0, 0, 0, $FF); | 
|---|
|  | 5169 | var | 
|---|
|  | 5170 | ByteData: pByte; | 
|---|
|  | 5171 | DataDepth: Byte; | 
|---|
|  | 5172 | ValEntry: Byte; | 
|---|
|  | 5173 | begin | 
|---|
|  | 5174 | with png.Header do | 
|---|
|  | 5175 | begin | 
|---|
|  | 5176 | {Map into a palette entry} | 
|---|
|  | 5177 | ValEntry := GetNearestPaletteIndex(Png.Palette, ColorToRGB(Value)); | 
|---|
|  | 5178 |  | 
|---|
|  | 5179 | {16 bits grayscale extra bits are discarted} | 
|---|
|  | 5180 | DataDepth := BitDepth; | 
|---|
|  | 5181 | if DataDepth > 8 then DataDepth := 8; | 
|---|
|  | 5182 | {Gets a pointer to the byte we intend to change} | 
|---|
|  | 5183 | ByteData := @pByteArray(png.Scanline[Y])^[X div (8 div DataDepth)]; | 
|---|
|  | 5184 | {Clears the old pixel data} | 
|---|
|  | 5185 | ByteData^ := ByteData^ and not (ClearFlag[DataDepth] shl ((8 - DataDepth) - | 
|---|
|  | 5186 | (X mod (8 div DataDepth)) * DataDepth)); | 
|---|
|  | 5187 |  | 
|---|
|  | 5188 | {Setting the new pixel} | 
|---|
|  | 5189 | ByteData^ := ByteData^ or (ValEntry shl ((8 - DataDepth) - | 
|---|
|  | 5190 | (X mod (8 div DataDepth)) * DataDepth)); | 
|---|
|  | 5191 | end {with png.Header} | 
|---|
|  | 5192 | end; | 
|---|
|  | 5193 |  | 
|---|
|  | 5194 | {Returns pixel when png uses RGB} | 
|---|
|  | 5195 | function GetRGBLinePixel(const png: TPngObject; | 
|---|
|  | 5196 | const X, Y: Integer): TColor; | 
|---|
|  | 5197 | begin | 
|---|
|  | 5198 | with pRGBLine(png.Scanline[Y])^[X] do | 
|---|
|  | 5199 | Result := RGB(rgbtRed, rgbtGreen, rgbtBlue) | 
|---|
|  | 5200 | end; | 
|---|
|  | 5201 |  | 
|---|
|  | 5202 | {Sets pixel when png uses RGB} | 
|---|
|  | 5203 | procedure SetRGBLinePixel(const png: TPngObject; | 
|---|
|  | 5204 | const X, Y: Integer; Value: TColor); | 
|---|
|  | 5205 | begin | 
|---|
|  | 5206 | with pRGBLine(png.Scanline[Y])^[X] do | 
|---|
|  | 5207 | begin | 
|---|
|  | 5208 | rgbtRed := GetRValue(Value); | 
|---|
|  | 5209 | rgbtGreen := GetGValue(Value); | 
|---|
|  | 5210 | rgbtBlue := GetBValue(Value) | 
|---|
|  | 5211 | end | 
|---|
|  | 5212 | end; | 
|---|
|  | 5213 |  | 
|---|
|  | 5214 | {Sets a pixel} | 
|---|
|  | 5215 | procedure TPngObject.SetPixels(const X, Y: Integer; const Value: TColor); | 
|---|
|  | 5216 | begin | 
|---|
|  | 5217 | if ((X >= 0) and (X <= Width - 1)) and | 
|---|
|  | 5218 | ((Y >= 0) and (Y <= Height - 1)) then | 
|---|
|  | 5219 | with Header do | 
|---|
|  | 5220 | begin | 
|---|
|  | 5221 | if ColorType in [COLOR_GRAYSCALE, COLOR_PALETTE] then | 
|---|
|  | 5222 | SetByteArrayPixel(Self, X, Y, Value) | 
|---|
|  | 5223 | else | 
|---|
|  | 5224 | SetRGBLinePixel(Self, X, Y, Value) | 
|---|
|  | 5225 | end {with} | 
|---|
|  | 5226 | end; | 
|---|
|  | 5227 |  | 
|---|
|  | 5228 |  | 
|---|
|  | 5229 | {Returns a pixel} | 
|---|
|  | 5230 | function TPngObject.GetPixels(const X, Y: Integer): TColor; | 
|---|
|  | 5231 | begin | 
|---|
|  | 5232 | if ((X >= 0) and (X <= Width - 1)) and | 
|---|
|  | 5233 | ((Y >= 0) and (Y <= Height - 1)) then | 
|---|
|  | 5234 | with Header do | 
|---|
|  | 5235 | begin | 
|---|
|  | 5236 | if ColorType in [COLOR_GRAYSCALE, COLOR_PALETTE] then | 
|---|
|  | 5237 | Result := GetByteArrayPixel(Self, X, Y) | 
|---|
|  | 5238 | else | 
|---|
|  | 5239 | Result := GetRGBLinePixel(Self, X, Y) | 
|---|
|  | 5240 | end {with} | 
|---|
|  | 5241 | else Result := 0 | 
|---|
|  | 5242 | end; | 
|---|
|  | 5243 |  | 
|---|
|  | 5244 | {Returns the image palette} | 
|---|
|  | 5245 | function TPngObject.GetPalette: HPALETTE; | 
|---|
|  | 5246 | var | 
|---|
|  | 5247 | LogPalette: TMaxLogPalette; | 
|---|
|  | 5248 | i: Integer; | 
|---|
|  | 5249 | begin | 
|---|
|  | 5250 | {Palette is avaliable for COLOR_PALETTE and COLOR_GRAYSCALE modes} | 
|---|
|  | 5251 | if (Header.ColorType in [COLOR_PALETTE, COLOR_GRAYSCALE])  then | 
|---|
|  | 5252 | begin | 
|---|
|  | 5253 | {In case the pal} | 
|---|
|  | 5254 | if TempPalette = 0 then | 
|---|
|  | 5255 | with LogPalette do | 
|---|
|  | 5256 | begin | 
|---|
|  | 5257 | {Prepares the new palette} | 
|---|
|  | 5258 | palVersion := $300; | 
|---|
|  | 5259 | palNumEntries := 256; | 
|---|
|  | 5260 | {Copy entries} | 
|---|
|  | 5261 | for i := 0 to LogPalette.palNumEntries - 1 do | 
|---|
|  | 5262 | begin | 
|---|
|  | 5263 | palPalEntry[i].peRed := Header.BitmapInfo.bmiColors[i].rgbRed; | 
|---|
|  | 5264 | palPalEntry[i].peGreen := Header.BitmapInfo.bmiColors[i].rgbGreen; | 
|---|
|  | 5265 | palPalEntry[i].peBlue := Header.BitmapInfo.bmiColors[i].rgbBlue; | 
|---|
|  | 5266 | palPalEntry[i].peFlags := 0; | 
|---|
|  | 5267 | end {for i}; | 
|---|
|  | 5268 | {Creates the palette} | 
|---|
|  | 5269 | TempPalette := CreatePalette(pLogPalette(@LogPalette)^); | 
|---|
|  | 5270 | end {with LogPalette, if Temppalette = 0} | 
|---|
|  | 5271 | end {if Header.ColorType in ...}; | 
|---|
|  | 5272 | Result := TempPalette; | 
|---|
|  | 5273 | end; | 
|---|
|  | 5274 |  | 
|---|
|  | 5275 | initialization | 
|---|
|  | 5276 | {Initialize} | 
|---|
|  | 5277 | ChunkClasses := nil; | 
|---|
|  | 5278 | {crc table has not being computed yet} | 
|---|
|  | 5279 | crc_table_computed := FALSE; | 
|---|
|  | 5280 | {Register the necessary chunks for png} | 
|---|
|  | 5281 | RegisterCommonChunks; | 
|---|
|  | 5282 | {Registers TPNGObject to use with TPicture} | 
|---|
|  | 5283 | {$IFDEF UseDelphi}{$IFDEF RegisterGraphic} | 
|---|
|  | 5284 | TPicture.RegisterFileFormat('PNG', 'Portable Network Graphics', TPNGObject); | 
|---|
|  | 5285 | {$ENDIF}{$ENDIF} | 
|---|
|  | 5286 | finalization | 
|---|
|  | 5287 | {$IFDEF UseDelphi}{$IFDEF RegisterGraphic} | 
|---|
|  | 5288 | TPicture.UnregisterGraphicClass(TPNGObject); | 
|---|
|  | 5289 | {$ENDIF}{$ENDIF} | 
|---|
|  | 5290 | {Free chunk classes} | 
|---|
|  | 5291 | FreeChunkClassList; | 
|---|
|  | 5292 | end. | 
|---|
|  | 5293 |  | 
|---|
|  | 5294 |  | 
|---|