ImagingNetworkGraphics.pas 87 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715
  1. {
  2. Vampyre Imaging Library
  3. by Marek Mauder
  4. http://imaginglib.sourceforge.net
  5. The contents of this file are used with permission, subject to the Mozilla
  6. Public License Version 1.1 (the "License"); you may not use this file except
  7. in compliance with the License. You may obtain a copy of the License at
  8. http://www.mozilla.org/MPL/MPL-1.1.html
  9. Software distributed under the License is distributed on an "AS IS" basis,
  10. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  11. the specific language governing rights and limitations under the License.
  12. Alternatively, the contents of this file may be used under the terms of the
  13. GNU Lesser General Public License (the "LGPL License"), in which case the
  14. provisions of the LGPL License are applicable instead of those above.
  15. If you wish to allow use of your version of this file only under the terms
  16. of the LGPL License and not to allow others to use your version of this file
  17. under the MPL, indicate your decision by deleting the provisions above and
  18. replace them with the notice and other provisions required by the LGPL
  19. License. If you do not delete the provisions above, a recipient may use
  20. your version of this file under either the MPL or the LGPL License.
  21. For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
  22. }
  23. { This unit contains image format loaders/savers for Network Graphics image
  24. file formats PNG, MNG, and JNG.}
  25. unit ImagingNetworkGraphics;
  26. interface
  27. {$I ImagingOptions.inc}
  28. { If MNG support is enabled we must make sure PNG and JNG are enabled too.}
  29. {$IFNDEF DONT_LINK_MNG}
  30. {$UNDEF DONT_LINK_PNG}
  31. {$UNDEF DONT_LINK_JNG}
  32. {$ENDIF}
  33. uses
  34. Types, SysUtils, Classes, ImagingTypes, Imaging, ImagingUtility, ImagingFormats, dzlib;
  35. type
  36. { Basic class for Network Graphics file formats loaders/savers.}
  37. TNetworkGraphicsFileFormat = class(TImageFileFormat)
  38. protected
  39. FSignature: TChar8;
  40. FPreFilter: LongInt;
  41. FCompressLevel: LongInt;
  42. FLossyCompression: LongBool;
  43. FLossyAlpha: LongBool;
  44. FQuality: LongInt;
  45. FProgressive: LongBool;
  46. FZLibStategy: Integer;
  47. function GetSupportedFormats: TImageFormats; override;
  48. procedure ConvertToSupported(var Image: TImageData;
  49. const Info: TImageFormatInfo); override;
  50. procedure Define; override;
  51. public
  52. function TestFormat(Handle: TImagingHandle): Boolean; override;
  53. procedure CheckOptionsValidity; override;
  54. published
  55. { Sets precompression filter used when saving images with lossless compression.
  56. Allowed values are: 0 (none), 1 (sub), 2 (up), 3 (average), 4 (paeth),
  57. 5 (use 0 for indexed/gray images and 4 for RGB/ARGB images),
  58. 6 (adaptive filtering - use best filter for each scanline - very slow).
  59. Note that filters 3 and 4 are much slower than filters 1 and 2.
  60. Default value is 5.}
  61. property PreFilter: LongInt read FPreFilter write FPreFilter;
  62. { Sets ZLib compression level used when saving images with lossless compression.
  63. Allowed values are in range 0 (no compresstion) to 9 (best compression).
  64. Default value is 5.}
  65. property CompressLevel: LongInt read FCompressLevel write FCompressLevel;
  66. { Specifies whether MNG animation frames are saved with lossy or lossless
  67. compression. Lossless frames are saved as PNG images and lossy frames are
  68. saved as JNG images. Allowed values are 0 (False) and 1 (True).
  69. Default value is 0.}
  70. property LossyCompression: LongBool read FLossyCompression write FLossyCompression;
  71. { Defines whether alpha channel of lossy MNG frames or JNG images
  72. is lossy compressed too. Allowed values are 0 (False) and 1 (True).
  73. Default value is 0.}
  74. property LossyAlpha: LongBool read FLossyAlpha write FLossyAlpha;
  75. { Specifies compression quality used when saving lossy MNG frames or JNG images.
  76. For details look at ImagingJpegQuality option.}
  77. property Quality: LongInt read FQuality write FQuality;
  78. { Specifies whether images are saved in progressive format when saving lossy
  79. MNG frames or JNG images. For details look at ImagingJpegProgressive.}
  80. property Progressive: LongBool read FProgressive write FProgressive;
  81. end;
  82. { Class for loading Portable Network Graphics Images.
  83. Loads all types of this image format (all images in png test suite)
  84. and saves all types with bitcount >= 8 (non-interlaced only).
  85. Compression level and filtering can be set by options interface.
  86. Supported ancillary chunks (loading):
  87. tRNS, bKGD
  88. (for indexed images transparency contains alpha values for palette,
  89. RGB/Gray images with transparency are converted to formats with alpha
  90. and pixels with transparent color are replaced with background color
  91. with alpha = 0).}
  92. TPNGFileFormat = class(TNetworkGraphicsFileFormat)
  93. private
  94. FLoadAnimated: LongBool;
  95. protected
  96. procedure Define; override;
  97. function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray;
  98. OnlyFirstLevel: Boolean): Boolean; override;
  99. function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray;
  100. Index: LongInt): Boolean; override;
  101. published
  102. property LoadAnimated: LongBool read FLoadAnimated write FLoadAnimated;
  103. end;
  104. {$IFNDEF DONT_LINK_MNG}
  105. { Class for loading Multiple Network Graphics files.
  106. This format has complex animation capabilities but Imaging only
  107. extracts frames. Individual frames are stored as standard PNG or JNG
  108. images. Loads all types of these frames stored in IHDR-IEND and
  109. JHDR-IEND streams (Note that there are MNG chunks
  110. like BASI which define images but does not contain image data itself,
  111. those are ignored).
  112. Imaging saves MNG files as MNG-VLC (very low complexity) so it is basicaly
  113. an array of image frames without MNG animation chunks. Frames can be saved
  114. as lossless PNG or lossy JNG images (look at TPNGFileFormat and
  115. TJNGFileFormat for info). Every frame can be in different data format.
  116. Many frame compression settings can be modified by options interface.}
  117. TMNGFileFormat = class(TNetworkGraphicsFileFormat)
  118. protected
  119. procedure Define; override;
  120. function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray;
  121. OnlyFirstLevel: Boolean): Boolean; override;
  122. function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray;
  123. Index: LongInt): Boolean; override;
  124. end;
  125. {$ENDIF}
  126. {$IFNDEF DONT_LINK_JNG}
  127. { Class for loading JPEG Network Graphics Images.
  128. Loads all types of this image format (all images in jng test suite)
  129. and saves all types except 12 bit JPEGs.
  130. Alpha channel in JNG images is stored separately from color/gray data and
  131. can be lossy (as JPEG image) or lossless (as PNG image) compressed.
  132. Type of alpha compression, compression level and quality,
  133. and filtering can be set by options interface.
  134. Supported ancillary chunks (loading):
  135. tRNS, bKGD
  136. (Images with transparency are converted to formats with alpha
  137. and pixels with transparent color are replaced with background color
  138. with alpha = 0).}
  139. TJNGFileFormat = class(TNetworkGraphicsFileFormat)
  140. protected
  141. procedure Define; override;
  142. function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray;
  143. OnlyFirstLevel: Boolean): Boolean; override;
  144. function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray;
  145. Index: LongInt): Boolean; override;
  146. end;
  147. {$ENDIF}
  148. implementation
  149. uses
  150. {$IFNDEF DONT_LINK_JNG}
  151. ImagingJpeg, ImagingIO,
  152. {$ENDIF}
  153. ImagingCanvases;
  154. const
  155. NGDefaultPreFilter = 5;
  156. NGDefaultCompressLevel = 5;
  157. NGDefaultLossyAlpha = False;
  158. NGDefaultLossyCompression = False;
  159. NGDefaultProgressive = False;
  160. NGDefaultQuality = 90;
  161. NGLosslessFormats: TImageFormats = [ifIndex8, ifGray8, ifA8Gray8, ifGray16,
  162. ifA16Gray16, ifR8G8B8, ifA8R8G8B8, ifR16G16B16, ifA16R16G16B16, ifB16G16R16,
  163. ifA16B16G16R16, ifBinary];
  164. NGLossyFormats: TImageFormats = [ifGray8, ifA8Gray8, ifR8G8B8, ifA8R8G8B8];
  165. PNGDefaultLoadAnimated = True;
  166. NGDefaultZLibStartegy = 1; // Z_FILTERED
  167. SPNGFormatName = 'Portable Network Graphics';
  168. SPNGMasks = '*.png';
  169. SMNGFormatName = 'Multiple Network Graphics';
  170. SMNGMasks = '*.mng';
  171. SJNGFormatName = 'JPEG Network Graphics';
  172. SJNGMasks = '*.jng';
  173. resourcestring
  174. SErrorLoadingChunk = 'Error when reading %s chunk data. File may be corrupted.';
  175. type
  176. { Chunk header.}
  177. TChunkHeader = packed record
  178. DataSize: UInt32;
  179. ChunkID: TChar4;
  180. end;
  181. { IHDR chunk format - PNG header.}
  182. TIHDR = packed record
  183. Width: UInt32; // Image width
  184. Height: UInt32; // Image height
  185. BitDepth: Byte; // Bits per pixel or bits per sample (for truecolor)
  186. ColorType: Byte; // 0 = grayscale, 2 = truecolor, 3 = palette,
  187. // 4 = gray + alpha, 6 = truecolor + alpha
  188. Compression: Byte; // Compression type: 0 = ZLib
  189. Filter: Byte; // Used precompress filter
  190. Interlacing: Byte; // Used interlacing: 0 = no int, 1 = Adam7
  191. end;
  192. PIHDR = ^TIHDR;
  193. { MHDR chunk format - MNG header.}
  194. TMHDR = packed record
  195. FrameWidth: UInt32; // Frame width
  196. FrameHeight: UInt32; // Frame height
  197. TicksPerSecond: UInt32; // FPS of animation
  198. NominalLayerCount: UInt32; // Number of layers in file
  199. NominalFrameCount: UInt32; // Number of frames in file
  200. NominalPlayTime: UInt32; // Play time of animation in ticks
  201. SimplicityProfile: UInt32; // Defines which MNG features are used in this file
  202. end;
  203. PMHDR = ^TMHDR;
  204. { JHDR chunk format - JNG header.}
  205. TJHDR = packed record
  206. Width: UInt32; // Image width
  207. Height: UInt32; // Image height
  208. ColorType: Byte; // 8 = grayscale (Y), 10 = color (YCbCr),
  209. // 12 = gray + alpha (Y-alpha), 14 = color + alpha (YCbCr-alpha)
  210. SampleDepth: Byte; // 8, 12 or 20 (8 and 12 samples together) bit
  211. Compression: Byte; // Compression type: 8 = Huffman coding
  212. Interlacing: Byte; // 0 = single scan, 8 = progressive
  213. AlphaSampleDepth: Byte; // 0, 1, 2, 4, 8, 16 if alpha compression is 0 (PNG)
  214. // 8 if alpha compression is 8 (JNG)
  215. AlphaCompression: Byte; // 0 = PNG graysscale IDAT, 8 = grayscale 8-bit JPEG
  216. AlphaFilter: Byte; // 0 = PNG filter or no filter (JPEG)
  217. AlphaInterlacing: Byte; // 0 = non interlaced
  218. end;
  219. PJHDR = ^TJHDR;
  220. { acTL chunk format - APNG animation control.}
  221. TacTL = packed record
  222. NumFrames: UInt32; // Number of frames
  223. NumPlay: UInt32; // Number of times to loop the animation (0 = inf)
  224. end;
  225. PacTL =^TacTL;
  226. { fcTL chunk format - APNG frame control.}
  227. TfcTL = packed record
  228. SeqNumber: UInt32; // Sequence number of the animation chunk, starting from 0
  229. Width: UInt32; // Width of the following frame
  230. Height: UInt32; // Height of the following frame
  231. XOffset: UInt32; // X position at which to render the following frame
  232. YOffset: UInt32; // Y position at which to render the following frame
  233. DelayNumer: Word; // Frame delay fraction numerator
  234. DelayDenom: Word; // Frame delay fraction denominator
  235. DisposeOp: Byte; // Type of frame area disposal to be done after rendering this frame
  236. BlendOp: Byte; // Type of frame area rendering for this frame
  237. end;
  238. PfcTL = ^TfcTL;
  239. { pHYs chunk format - encodes the absolute or relative dimensions of pixels.}
  240. TpHYs = packed record
  241. PixelsPerUnitX: UInt32;
  242. PixelsPerUnitY: UInt32;
  243. UnitSpecifier: Byte;
  244. end;
  245. PpHYs = ^TpHYs;
  246. const
  247. { PNG file identifier.}
  248. PNGSignature: TChar8 = #$89'PNG'#$0D#$0A#$1A#$0A;
  249. { MNG file identifier.}
  250. MNGSignature: TChar8 = #$8A'MNG'#$0D#$0A#$1A#$0A;
  251. { JNG file identifier.}
  252. JNGSignature: TChar8 = #$8B'JNG'#$0D#$0A#$1A#$0A;
  253. { Constants for chunk identifiers and signature identifiers.
  254. They are in big-endian format.}
  255. IHDRChunk: TChar4 = 'IHDR';
  256. IENDChunk: TChar4 = 'IEND';
  257. MHDRChunk: TChar4 = 'MHDR';
  258. MENDChunk: TChar4 = 'MEND';
  259. JHDRChunk: TChar4 = 'JHDR';
  260. IDATChunk: TChar4 = 'IDAT';
  261. JDATChunk: TChar4 = 'JDAT';
  262. JDAAChunk: TChar4 = 'JDAA';
  263. JSEPChunk: TChar4 = 'JSEP';
  264. PLTEChunk: TChar4 = 'PLTE';
  265. BACKChunk: TChar4 = 'BACK';
  266. DEFIChunk: TChar4 = 'DEFI';
  267. TERMChunk: TChar4 = 'TERM';
  268. tRNSChunk: TChar4 = 'tRNS';
  269. bKGDChunk: TChar4 = 'bKGD';
  270. gAMAChunk: TChar4 = 'gAMA';
  271. acTLChunk: TChar4 = 'acTL';
  272. fcTLChunk: TChar4 = 'fcTL';
  273. fdATChunk: TChar4 = 'fdAT';
  274. pHYsChunk: TChar4 = 'pHYs';
  275. { APNG frame dispose operations.}
  276. DisposeOpNone = 0;
  277. DisposeOpBackground = 1;
  278. DisposeOpPrevious = 2;
  279. { APNG frame blending modes}
  280. BlendOpSource = 0;
  281. BlendOpOver = 1;
  282. { Interlace start and offsets.}
  283. RowStart: array[0..6] of LongInt = (0, 0, 4, 0, 2, 0, 1);
  284. ColumnStart: array[0..6] of LongInt = (0, 4, 0, 2, 0, 1, 0);
  285. RowIncrement: array[0..6] of LongInt = (8, 8, 8, 4, 4, 2, 2);
  286. ColumnIncrement: array[0..6] of LongInt = (8, 8, 4, 4, 2, 2, 1);
  287. type
  288. { Helper class that holds information about MNG frame in PNG or JNG format.}
  289. TFrameInfo = class
  290. public
  291. Index: Integer;
  292. FrameWidth, FrameHeight: LongInt;
  293. IsJpegFrame: Boolean;
  294. IHDR: TIHDR;
  295. JHDR: TJHDR;
  296. fcTL: TfcTL;
  297. pHYs: TpHYs;
  298. Palette: PPalette24;
  299. PaletteEntries: LongInt;
  300. Transparency: Pointer;
  301. TransparencySize: LongInt;
  302. Background: Pointer;
  303. BackgroundSize: LongInt;
  304. IDATMemory: TMemoryStream;
  305. JDATMemory: TMemoryStream;
  306. JDAAMemory: TMemoryStream;
  307. constructor Create(AIndex: Integer);
  308. destructor Destroy; override;
  309. procedure AssignSharedProps(Source: TFrameInfo);
  310. end;
  311. { Defines type of Network Graphics file.}
  312. TNGFileType = (ngPNG, ngAPNG, ngMNG, ngJNG);
  313. TNGFileHandler = class
  314. public
  315. FileFormat: TNetworkGraphicsFileFormat;
  316. FileType: TNGFileType;
  317. Frames: array of TFrameInfo;
  318. MHDR: TMHDR; // Main header for MNG files
  319. acTL: TacTL; // Global anim control for APNG files
  320. GlobalPalette: PPalette24;
  321. GlobalPaletteEntries: LongInt;
  322. GlobalTransparency: Pointer;
  323. GlobalTransparencySize: LongInt;
  324. constructor Create(AFileFormat: TNetworkGraphicsFileFormat);
  325. destructor Destroy; override;
  326. procedure Clear;
  327. function GetLastFrame: TFrameInfo;
  328. function AddFrameInfo: TFrameInfo;
  329. procedure LoadMetaData;
  330. end;
  331. { Network Graphics file parser and frame converter.}
  332. TNGFileLoader = class(TNGFileHandler)
  333. public
  334. function LoadFile(Handle: TImagingHandle): Boolean;
  335. procedure LoadImageFromPNGFrame(FrameWidth, FrameHeight: LongInt; const IHDR: TIHDR; IDATStream: TMemoryStream; var Image: TImageData);
  336. {$IFNDEF DONT_LINK_JNG}
  337. procedure LoadImageFromJNGFrame(FrameWidth, FrameHeight: LongInt; const JHDR: TJHDR; IDATStream, JDATStream, JDAAStream: TMemoryStream; var Image: TImageData);
  338. {$ENDIF}
  339. procedure ApplyFrameSettings(Frame: TFrameInfo; var Image: TImageData);
  340. end;
  341. TNGFileSaver = class(TNGFileHandler)
  342. public
  343. PreFilter: LongInt;
  344. CompressLevel: LongInt;
  345. LossyAlpha: Boolean;
  346. Quality: LongInt;
  347. Progressive: Boolean;
  348. ZLibStrategy: Integer;
  349. function SaveFile(Handle: TImagingHandle): Boolean;
  350. procedure AddFrame(const Image: TImageData; IsJpegFrame: Boolean);
  351. procedure StoreImageToPNGFrame(const IHDR: TIHDR; Bits: Pointer; FmtInfo: TImageFormatInfo; IDATStream: TMemoryStream);
  352. {$IFNDEF DONT_LINK_JNG}
  353. procedure StoreImageToJNGFrame(const JHDR: TJHDR; const Image: TImageData; IDATStream, JDATStream, JDAAStream: TMemoryStream);
  354. {$ENDIF}
  355. procedure SetFileOptions;
  356. end;
  357. {$IFNDEF DONT_LINK_JNG}
  358. TCustomIOJpegFileFormat = class(TJpegFileFormat)
  359. protected
  360. FCustomIO: TIOFunctions;
  361. procedure SetJpegIO(const JpegIO: TIOFunctions); override;
  362. procedure SetCustomIO(const CustomIO: TIOFunctions);
  363. end;
  364. {$ENDIF}
  365. TAPNGAnimator = class
  366. public
  367. class procedure Animate(var Images: TDynImageDataArray; const acTL: TacTL; const SrcFrames: array of TFrameInfo);
  368. end;
  369. { Helper routines }
  370. function PaethPredictor(A, B, C: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  371. var
  372. P, PA, PB, PC: LongInt;
  373. begin
  374. P := A + B - C;
  375. PA := Abs(P - A);
  376. PB := Abs(P - B);
  377. PC := Abs(P - C);
  378. if (PA <= PB) and (PA <= PC) then
  379. Result := A
  380. else
  381. if PB <= PC then
  382. Result := B
  383. else
  384. Result := C;
  385. end;
  386. procedure SwapRGB(Line: PByte; Width, SampleDepth, BytesPerPixel: LongInt);
  387. var
  388. I: LongInt;
  389. Tmp: Word;
  390. begin
  391. case SampleDepth of
  392. 8:
  393. for I := 0 to Width - 1 do
  394. with PColor24Rec(Line)^ do
  395. begin
  396. Tmp := R;
  397. R := B;
  398. B := Tmp;
  399. Inc(Line, BytesPerPixel);
  400. end;
  401. 16:
  402. for I := 0 to Width - 1 do
  403. with PColor48Rec(Line)^ do
  404. begin
  405. Tmp := R;
  406. R := B;
  407. B := Tmp;
  408. Inc(Line, BytesPerPixel);
  409. end;
  410. end;
  411. end;
  412. {$IFNDEF DONT_LINK_JNG}
  413. { TCustomIOJpegFileFormat class implementation }
  414. procedure TCustomIOJpegFileFormat.SetCustomIO(const CustomIO: TIOFunctions);
  415. begin
  416. FCustomIO := CustomIO;
  417. end;
  418. procedure TCustomIOJpegFileFormat.SetJpegIO(const JpegIO: TIOFunctions);
  419. begin
  420. inherited SetJpegIO(FCustomIO);
  421. end;
  422. {$ENDIF}
  423. { TFrameInfo class implementation }
  424. constructor TFrameInfo.Create(AIndex: Integer);
  425. begin
  426. Index := AIndex;
  427. IDATMemory := TMemoryStream.Create;
  428. JDATMemory := TMemoryStream.Create;
  429. JDAAMemory := TMemoryStream.Create;
  430. end;
  431. destructor TFrameInfo.Destroy;
  432. begin
  433. FreeMem(Palette);
  434. FreeMem(Transparency);
  435. FreeMem(Background);
  436. IDATMemory.Free;
  437. JDATMemory.Free;
  438. JDAAMemory.Free;
  439. inherited Destroy;
  440. end;
  441. procedure TFrameInfo.AssignSharedProps(Source: TFrameInfo);
  442. begin
  443. IHDR := Source.IHDR;
  444. JHDR := Source.JHDR;
  445. PaletteEntries := Source.PaletteEntries;
  446. GetMem(Palette, PaletteEntries * SizeOf(TColor24Rec));
  447. Move(Source.Palette^, Palette^, PaletteEntries * SizeOf(TColor24Rec));
  448. TransparencySize := Source.TransparencySize;
  449. GetMem(Transparency, TransparencySize);
  450. Move(Source.Transparency^, Transparency^, TransparencySize);
  451. end;
  452. { TNGFileHandler class implementation}
  453. destructor TNGFileHandler.Destroy;
  454. begin
  455. Clear;
  456. inherited Destroy;
  457. end;
  458. procedure TNGFileHandler.Clear;
  459. var
  460. I: LongInt;
  461. begin
  462. for I := 0 to Length(Frames) - 1 do
  463. Frames[I].Free;
  464. SetLength(Frames, 0);
  465. FreeMemNil(GlobalPalette);
  466. GlobalPaletteEntries := 0;
  467. FreeMemNil(GlobalTransparency);
  468. GlobalTransparencySize := 0;
  469. end;
  470. constructor TNGFileHandler.Create(AFileFormat: TNetworkGraphicsFileFormat);
  471. begin
  472. FileFormat := AFileFormat;
  473. end;
  474. function TNGFileHandler.GetLastFrame: TFrameInfo;
  475. var
  476. Len: LongInt;
  477. begin
  478. Len := Length(Frames);
  479. if Len > 0 then
  480. Result := Frames[Len - 1]
  481. else
  482. Result := nil;
  483. end;
  484. procedure TNGFileHandler.LoadMetaData;
  485. var
  486. I: Integer;
  487. Delay, Denom: Integer;
  488. begin
  489. if FileType = ngAPNG then
  490. begin
  491. // Num plays of APNG animation
  492. FileFormat.FMetadata.SetMetaItem(SMetaAnimationLoops, acTL.NumPlay);
  493. end;
  494. for I := 0 to High(Frames) do
  495. begin
  496. if Frames[I].pHYs.UnitSpecifier = 1 then
  497. begin
  498. // Store physical pixel dimensions, in PNG stored as pixels per meter DPM
  499. FileFormat.FMetadata.SetPhysicalPixelSize(ruDpm, Frames[I].pHYs.PixelsPerUnitX,
  500. Frames[I].pHYs.PixelsPerUnitY);
  501. end;
  502. if FileType = ngAPNG then
  503. begin
  504. // Store frame delay of APNG file frame
  505. Denom := Frames[I].fcTL.DelayDenom;
  506. if Denom = 0 then
  507. Denom := 100;
  508. Delay := Round(1000 * (Frames[I].fcTL.DelayNumer / Denom));
  509. FileFormat.FMetadata.SetMetaItem(SMetaFrameDelay, Delay, I);
  510. end;
  511. end;
  512. end;
  513. function TNGFileHandler.AddFrameInfo: TFrameInfo;
  514. var
  515. Len: LongInt;
  516. begin
  517. Len := Length(Frames);
  518. SetLength(Frames, Len + 1);
  519. Result := TFrameInfo.Create(Len);
  520. Frames[Len] := Result;
  521. end;
  522. { TNGFileLoader class implementation}
  523. function TNGFileLoader.LoadFile(Handle: TImagingHandle): Boolean;
  524. var
  525. Sig: TChar8;
  526. Chunk: TChunkHeader;
  527. ChunkData: Pointer;
  528. ChunkCrc: UInt32;
  529. procedure ReadChunk;
  530. begin
  531. GetIO.Read(Handle, @Chunk, SizeOf(Chunk));
  532. Chunk.DataSize := SwapEndianUInt32(Chunk.DataSize);
  533. end;
  534. procedure ReadChunkData;
  535. var
  536. ReadBytes: UInt32;
  537. begin
  538. FreeMemNil(ChunkData);
  539. GetMem(ChunkData, Chunk.DataSize);
  540. ReadBytes := GetIO.Read(Handle, ChunkData, Chunk.DataSize);
  541. GetIO.Read(Handle, @ChunkCrc, SizeOf(ChunkCrc));
  542. if ReadBytes <> Chunk.DataSize then
  543. RaiseImaging(SErrorLoadingChunk, [string(Chunk.ChunkID)]);
  544. end;
  545. procedure SkipChunkData;
  546. begin
  547. GetIO.Seek(Handle, Chunk.DataSize + SizeOf(ChunkCrc), smFromCurrent);
  548. end;
  549. procedure StartNewPNGImage;
  550. var
  551. Frame: TFrameInfo;
  552. begin
  553. ReadChunkData;
  554. if Chunk.ChunkID = fcTLChunk then
  555. begin
  556. if (Length(Frames) = 1) and (Frames[0].IDATMemory.Size = 0) then
  557. begin
  558. // First fcTL chunk maybe for first IDAT frame which is alredy created
  559. Frame := Frames[0];
  560. end
  561. else
  562. begin
  563. // Subsequent APNG frames with data in fdAT
  564. Frame := AddFrameInfo;
  565. // Copy some shared props from first frame (IHDR is the same for all APNG frames, palette etc)
  566. Frame.AssignSharedProps(Frames[0]);
  567. end;
  568. Frame.fcTL := PfcTL(ChunkData)^;
  569. SwapEndianUInt32(@Frame.fcTL, 5);
  570. Frame.fcTL.DelayNumer := SwapEndianWord(Frame.fcTL.DelayNumer);
  571. Frame.fcTL.DelayDenom := SwapEndianWord(Frame.fcTL.DelayDenom);
  572. Frame.FrameWidth := Frame.fcTL.Width;
  573. Frame.FrameHeight := Frame.fcTL.Height;
  574. end
  575. else
  576. begin
  577. // This is frame defined by IHDR chunk
  578. Frame := AddFrameInfo;
  579. Frame.IHDR := PIHDR(ChunkData)^;
  580. SwapEndianUInt32(@Frame.IHDR, 2);
  581. Frame.FrameWidth := Frame.IHDR.Width;
  582. Frame.FrameHeight := Frame.IHDR.Height;
  583. end;
  584. Frame.IsJpegFrame := False;
  585. end;
  586. procedure StartNewJNGImage;
  587. var
  588. Frame: TFrameInfo;
  589. begin
  590. ReadChunkData;
  591. Frame := AddFrameInfo;
  592. Frame.IsJpegFrame := True;
  593. Frame.JHDR := PJHDR(ChunkData)^;
  594. SwapEndianUInt32(@Frame.JHDR, 2);
  595. Frame.FrameWidth := Frame.JHDR.Width;
  596. Frame.FrameHeight := Frame.JHDR.Height;
  597. end;
  598. procedure AppendIDAT;
  599. begin
  600. ReadChunkData;
  601. // Append current IDAT/fdAT chunk to storage stream
  602. if Chunk.ChunkID = IDATChunk then
  603. GetLastFrame.IDATMemory.Write(ChunkData^, Chunk.DataSize)
  604. else if Chunk.ChunkID = fdATChunk then
  605. GetLastFrame.IDATMemory.Write(PByteArray(ChunkData)[4], Chunk.DataSize - SizeOf(UInt32));
  606. end;
  607. procedure AppendJDAT;
  608. begin
  609. ReadChunkData;
  610. // Append current JDAT chunk to storage stream
  611. GetLastFrame.JDATMemory.Write(ChunkData^, Chunk.DataSize);
  612. end;
  613. procedure AppendJDAA;
  614. begin
  615. ReadChunkData;
  616. // Append current JDAA chunk to storage stream
  617. GetLastFrame.JDAAMemory.Write(ChunkData^, Chunk.DataSize);
  618. end;
  619. procedure LoadPLTE;
  620. begin
  621. ReadChunkData;
  622. if GetLastFrame = nil then
  623. begin
  624. // Load global palette
  625. GetMem(GlobalPalette, Chunk.DataSize);
  626. Move(ChunkData^, GlobalPalette^, Chunk.DataSize);
  627. GlobalPaletteEntries := Chunk.DataSize div 3;
  628. end
  629. else if GetLastFrame.Palette = nil then
  630. begin
  631. if (Chunk.DataSize = 0) and (GlobalPalette <> nil) then
  632. begin
  633. // Use global palette
  634. GetMem(GetLastFrame.Palette, GlobalPaletteEntries * SizeOf(TColor24Rec));
  635. Move(GlobalPalette^, GetLastFrame.Palette^, GlobalPaletteEntries * SizeOf(TColor24Rec));
  636. GetLastFrame.PaletteEntries := GlobalPaletteEntries;
  637. end
  638. else
  639. begin
  640. // Load pal from PLTE chunk
  641. GetMem(GetLastFrame.Palette, Chunk.DataSize);
  642. Move(ChunkData^, GetLastFrame.Palette^, Chunk.DataSize);
  643. GetLastFrame.PaletteEntries := Chunk.DataSize div 3;
  644. end;
  645. end;
  646. end;
  647. procedure LoadtRNS;
  648. begin
  649. ReadChunkData;
  650. if GetLastFrame = nil then
  651. begin
  652. // Load global transparency
  653. GetMem(GlobalTransparency, Chunk.DataSize);
  654. Move(ChunkData^, GlobalTransparency^, Chunk.DataSize);
  655. GlobalTransparencySize := Chunk.DataSize;
  656. end
  657. else if GetLastFrame.Transparency = nil then
  658. begin
  659. if (Chunk.DataSize = 0) and (GlobalTransparency <> nil) then
  660. begin
  661. // Use global transparency
  662. GetMem(GetLastFrame.Transparency, GlobalTransparencySize);
  663. Move(GlobalTransparency^, GetLastFrame.Transparency^, Chunk.DataSize);
  664. GetLastFrame.TransparencySize := GlobalTransparencySize;
  665. end
  666. else
  667. begin
  668. // Load pal from tRNS chunk
  669. GetMem(GetLastFrame.Transparency, Chunk.DataSize);
  670. Move(ChunkData^, GetLastFrame.Transparency^, Chunk.DataSize);
  671. GetLastFrame.TransparencySize := Chunk.DataSize;
  672. end;
  673. end;
  674. end;
  675. procedure LoadbKGD;
  676. begin
  677. ReadChunkData;
  678. if GetLastFrame.Background = nil then
  679. begin
  680. GetMem(GetLastFrame.Background, Chunk.DataSize);
  681. Move(ChunkData^, GetLastFrame.Background^, Chunk.DataSize);
  682. GetLastFrame.BackgroundSize := Chunk.DataSize;
  683. end;
  684. end;
  685. procedure HandleacTL;
  686. begin
  687. FileType := ngAPNG;
  688. ReadChunkData;
  689. acTL := PacTL(ChunkData)^;
  690. SwapEndianUInt32(@acTL, SizeOf(acTL) div SizeOf(UInt32));
  691. end;
  692. procedure LoadpHYs;
  693. begin
  694. ReadChunkData;
  695. with GetLastFrame do
  696. begin
  697. pHYs := PpHYs(ChunkData)^;
  698. SwapEndianUInt32(@pHYs, SizeOf(pHYs) div SizeOf(UInt32));
  699. end;
  700. end;
  701. begin
  702. Result := False;
  703. Clear;
  704. ChunkData := nil;
  705. with GetIO do
  706. try
  707. Read(Handle, @Sig, SizeOf(Sig));
  708. // Set file type according to the signature
  709. if Sig = PNGSignature then FileType := ngPNG
  710. else if Sig = MNGSignature then FileType := ngMNG
  711. else if Sig = JNGSignature then FileType := ngJNG
  712. else Exit;
  713. if FileType = ngMNG then
  714. begin
  715. // Store MNG header if present
  716. ReadChunk;
  717. ReadChunkData;
  718. MHDR := PMHDR(ChunkData)^;
  719. SwapEndianUInt32(@MHDR, SizeOf(MHDR) div SizeOf(UInt32));
  720. end;
  721. // Read chunks until ending chunk or EOF is reached
  722. repeat
  723. ReadChunk;
  724. if (Chunk.ChunkID = IHDRChunk) or (Chunk.ChunkID = fcTLChunk) then StartNewPNGImage
  725. else if Chunk.ChunkID = JHDRChunk then StartNewJNGImage
  726. else if (Chunk.ChunkID = IDATChunk) or (Chunk.ChunkID = fdATChunk) then AppendIDAT
  727. else if Chunk.ChunkID = JDATChunk then AppendJDAT
  728. else if Chunk.ChunkID = JDAAChunk then AppendJDAA
  729. else if Chunk.ChunkID = PLTEChunk then LoadPLTE
  730. else if Chunk.ChunkID = tRNSChunk then LoadtRNS
  731. else if Chunk.ChunkID = bKGDChunk then LoadbKGD
  732. else if Chunk.ChunkID = acTLChunk then HandleacTL
  733. else if Chunk.ChunkID = pHYsChunk then LoadpHYs
  734. else SkipChunkData;
  735. until Eof(Handle) or (Chunk.ChunkID = MENDChunk) or
  736. ((FileType <> ngMNG) and (Chunk.ChunkID = IENDChunk));
  737. Result := True;
  738. finally
  739. FreeMemNil(ChunkData);
  740. end;
  741. end;
  742. procedure TNGFileLoader.LoadImageFromPNGFrame(FrameWidth, FrameHeight: LongInt; const IHDR: TIHDR;
  743. IDATStream: TMemoryStream; var Image: TImageData);
  744. type
  745. TGetPixelFunc = function(Line: PByteArray; X: LongInt): Byte;
  746. var
  747. LineBuffer: array[Boolean] of PByteArray;
  748. ActLine: Boolean;
  749. Data, TotalBuffer, ZeroLine, PrevLine: Pointer;
  750. BitCount, TotalPos, BytesPerPixel, I, Pass,
  751. SrcDataSize, BytesPerLine, InterlaceLineBytes, InterlaceWidth: LongInt;
  752. TotalSize: Integer;
  753. Info: TImageFormatInfo;
  754. procedure DecodeAdam7;
  755. const
  756. BitTable: array[1..8] of LongInt = ($1, $3, 0, $F, 0, 0, 0, $FF);
  757. StartBit: array[1..8] of LongInt = (7, 6, 0, 4, 0, 0, 0, 0);
  758. var
  759. Src, Dst, Dst2: PByte;
  760. CurBit, Col: LongInt;
  761. begin
  762. Src := @LineBuffer[ActLine][1];
  763. Col := ColumnStart[Pass];
  764. with Image do
  765. case BitCount of
  766. 1, 2, 4:
  767. begin
  768. Dst := @PByteArray(Data)[I * BytesPerLine];
  769. repeat
  770. CurBit := StartBit[BitCount];
  771. repeat
  772. Dst2 := @PByteArray(Dst)[(BitCount * Col) shr 3];
  773. Dst2^ := Dst2^ or ((Src^ shr CurBit) and BitTable[BitCount])
  774. shl (StartBit[BitCount] - (Col * BitCount mod 8));
  775. Inc(Col, ColumnIncrement[Pass]);
  776. Dec(CurBit, BitCount);
  777. until CurBit < 0;
  778. Inc(Src);
  779. until Col >= Width;
  780. end;
  781. else
  782. begin
  783. Dst := @PByteArray(Data)[I * BytesPerLine + Col * BytesPerPixel];
  784. repeat
  785. CopyPixel(Src, Dst, BytesPerPixel);
  786. Inc(Dst, BytesPerPixel);
  787. Inc(Src, BytesPerPixel);
  788. Inc(Dst, ColumnIncrement[Pass] * BytesPerPixel - BytesPerPixel);
  789. Inc(Col, ColumnIncrement[Pass]);
  790. until Col >= Width;
  791. end;
  792. end;
  793. end;
  794. procedure FilterScanline(Filter: Byte; BytesPerPixel: LongInt; Line, PrevLine, Target: PByteArray;
  795. BytesPerLine: LongInt);
  796. var
  797. I: LongInt;
  798. begin
  799. case Filter of
  800. 0:
  801. begin
  802. // No filter
  803. Move(Line^, Target^, BytesPerLine);
  804. end;
  805. 1:
  806. begin
  807. // Sub filter
  808. Move(Line^, Target^, BytesPerPixel);
  809. for I := BytesPerPixel to BytesPerLine - 1 do
  810. Target[I] := (Line[I] + Target[I - BytesPerPixel]) and $FF;
  811. end;
  812. 2:
  813. begin
  814. // Up filter
  815. for I := 0 to BytesPerLine - 1 do
  816. Target[I] := (Line[I] + PrevLine[I]) and $FF;
  817. end;
  818. 3:
  819. begin
  820. // Average filter
  821. for I := 0 to BytesPerPixel - 1 do
  822. Target[I] := (Line[I] + PrevLine[I] shr 1) and $FF;
  823. for I := BytesPerPixel to BytesPerLine - 1 do
  824. Target[I] := (Line[I] + (Target[I - BytesPerPixel] + PrevLine[I]) shr 1) and $FF;
  825. end;
  826. 4:
  827. begin
  828. // Paeth filter
  829. for I := 0 to BytesPerPixel - 1 do
  830. Target[I] := (Line[I] + PaethPredictor(0, PrevLine[I], 0)) and $FF;
  831. for I := BytesPerPixel to BytesPerLine - 1 do
  832. Target[I] := (Line[I] + PaethPredictor(Target[I - BytesPerPixel], PrevLine[I], PrevLine[I - BytesPerPixel])) and $FF;
  833. end;
  834. end;
  835. end;
  836. procedure TransformLOCOToRGB(Data: PByte; NumPixels, BytesPerPixel: LongInt);
  837. var
  838. I: LongInt;
  839. begin
  840. for I := 0 to NumPixels - 1 do
  841. begin
  842. if IHDR.BitDepth = 8 then
  843. begin
  844. PColor32Rec(Data).R := Byte(PColor32Rec(Data).R + PColor32Rec(Data).G);
  845. PColor32Rec(Data).B := Byte(PColor32Rec(Data).B + PColor32Rec(Data).G);
  846. end
  847. else
  848. begin
  849. PColor64Rec(Data).R := Word(PColor64Rec(Data).R + PColor64Rec(Data).G);
  850. PColor64Rec(Data).B := Word(PColor64Rec(Data).B + PColor64Rec(Data).G);
  851. end;
  852. Inc(Data, BytesPerPixel);
  853. end;
  854. end;
  855. function CheckBinaryPalette: Boolean;
  856. begin
  857. with GetLastFrame do
  858. Result := (PaletteEntries = 2) and
  859. (Palette[0].R = 0) and (Palette[0].G = 0) and (Palette[0].B = 0) and
  860. (Palette[1].R = 255) and (Palette[1].G = 255) and (Palette[1].B = 255);
  861. end;
  862. begin
  863. Image.Width := FrameWidth;
  864. Image.Height := FrameHeight;
  865. Image.Format := ifUnknown;
  866. case IHDR.ColorType of
  867. 0:
  868. begin
  869. // Gray scale image
  870. case IHDR.BitDepth of
  871. 1: Image.Format := ifBinary;
  872. 2, 4, 8: Image.Format := ifGray8;
  873. 16: Image.Format := ifGray16;
  874. end;
  875. BitCount := IHDR.BitDepth;
  876. end;
  877. 2:
  878. begin
  879. // RGB image
  880. case IHDR.BitDepth of
  881. 8: Image.Format := ifR8G8B8;
  882. 16: Image.Format := ifR16G16B16;
  883. end;
  884. BitCount := IHDR.BitDepth * 3;
  885. end;
  886. 3:
  887. begin
  888. // Indexed image
  889. if (IHDR.BitDepth = 1) and CheckBinaryPalette then
  890. Image.Format := ifBinary
  891. else
  892. Image.Format := ifIndex8;
  893. BitCount := IHDR.BitDepth;
  894. end;
  895. 4:
  896. begin
  897. // Grayscale + alpha image
  898. case IHDR.BitDepth of
  899. 8: Image.Format := ifA8Gray8;
  900. 16: Image.Format := ifA16Gray16;
  901. end;
  902. BitCount := IHDR.BitDepth * 2;
  903. end;
  904. 6:
  905. begin
  906. // ARGB image
  907. case IHDR.BitDepth of
  908. 8: Image.Format := ifA8R8G8B8;
  909. 16: Image.Format := ifA16R16G16B16;
  910. end;
  911. BitCount := IHDR.BitDepth * 4;
  912. end;
  913. end;
  914. GetImageFormatInfo(Image.Format, Info);
  915. BytesPerPixel := (BitCount + 7) div 8;
  916. LineBuffer[True] := nil;
  917. LineBuffer[False] := nil;
  918. TotalBuffer := nil;
  919. ZeroLine := nil;
  920. ActLine := True;
  921. // Start decoding
  922. with Image do
  923. try
  924. BytesPerLine := (Width * BitCount + 7) div 8;
  925. SrcDataSize := Height * BytesPerLine;
  926. GetMem(Data, SrcDataSize);
  927. FillChar(Data^, SrcDataSize, 0);
  928. GetMem(ZeroLine, BytesPerLine);
  929. FillChar(ZeroLine^, BytesPerLine, 0);
  930. if IHDR.Interlacing = 1 then
  931. begin
  932. // Decode interlaced images
  933. TotalPos := 0;
  934. DecompressBuf(IDATStream.Memory, IDATStream.Size, 0,
  935. Pointer(TotalBuffer), TotalSize);
  936. GetMem(LineBuffer[True], BytesPerLine + 1);
  937. GetMem(LineBuffer[False], BytesPerLine + 1);
  938. for Pass := 0 to 6 do
  939. begin
  940. // Prepare next interlace run
  941. if Width <= ColumnStart[Pass] then
  942. Continue;
  943. InterlaceWidth := (Width + ColumnIncrement[Pass] - 1 -
  944. ColumnStart[Pass]) div ColumnIncrement[Pass];
  945. InterlaceLineBytes := (InterlaceWidth * BitCount + 7) shr 3;
  946. I := RowStart[Pass];
  947. FillChar(LineBuffer[True][0], BytesPerLine + 1, 0);
  948. FillChar(LineBuffer[False][0], BytesPerLine + 1, 0);
  949. while I < Height do
  950. begin
  951. // Copy line from decompressed data to working buffer
  952. Move(PByteArray(TotalBuffer)[TotalPos],
  953. LineBuffer[ActLine][0], InterlaceLineBytes + 1);
  954. Inc(TotalPos, InterlaceLineBytes + 1);
  955. // Swap red and blue channels if necessary
  956. if (IHDR.ColorType in [2, 6]) then
  957. SwapRGB(@LineBuffer[ActLine][1], InterlaceWidth, IHDR.BitDepth, BytesPerPixel);
  958. // Reverse-filter current scanline
  959. FilterScanline(LineBuffer[ActLine][0], BytesPerPixel,
  960. @LineBuffer[ActLine][1], @LineBuffer[not ActLine][1],
  961. @LineBuffer[ActLine][1], InterlaceLineBytes);
  962. // Decode Adam7 interlacing
  963. DecodeAdam7;
  964. ActLine := not ActLine;
  965. // Continue with next row in interlaced order
  966. Inc(I, RowIncrement[Pass]);
  967. end;
  968. end;
  969. end
  970. else
  971. begin
  972. // Decode non-interlaced images
  973. PrevLine := ZeroLine;
  974. DecompressBuf(IDATStream.Memory, IDATStream.Size, SrcDataSize + Height,
  975. Pointer(TotalBuffer), TotalSize);
  976. for I := 0 to Height - 1 do
  977. begin
  978. // Swap red and blue channels if necessary
  979. if IHDR.ColorType in [2, 6] then
  980. SwapRGB(@PByteArray(TotalBuffer)[I * (BytesPerLine + 1) + 1], Width,
  981. IHDR.BitDepth, BytesPerPixel);
  982. // reverse-filter current scanline
  983. FilterScanline(PByteArray(TotalBuffer)[I * (BytesPerLine + 1)],
  984. BytesPerPixel, @PByteArray(TotalBuffer)[I * (BytesPerLine + 1) + 1],
  985. PrevLine, @PByteArray(Data)[I * BytesPerLine], BytesPerLine);
  986. PrevLine := @PByteArray(Data)[I * BytesPerLine];
  987. end;
  988. end;
  989. Size := Info.GetPixelsSize(Info.Format, Width, Height);
  990. if Size <> SrcDataSize then
  991. begin
  992. // If source data size is different from size of image in assigned
  993. // format we must convert it (it is in 1/2/4 bit count)
  994. GetMem(Bits, Size);
  995. case IHDR.BitDepth of
  996. 1:
  997. begin
  998. // Convert only indexed, keep black and white in ifBinary
  999. if IHDR.ColorType <> 0 then
  1000. Convert1To8(Data, Bits, Width, Height, BytesPerLine, False);
  1001. end;
  1002. 2: Convert2To8(Data, Bits, Width, Height, BytesPerLine, IHDR.ColorType = 0);
  1003. 4: Convert4To8(Data, Bits, Width, Height, BytesPerLine, IHDR.ColorType = 0);
  1004. end;
  1005. FreeMem(Data);
  1006. end
  1007. else
  1008. begin
  1009. // If source data size is the same as size of
  1010. // image Bits in assigned format we simply copy pointer reference
  1011. Bits := Data;
  1012. end;
  1013. // LOCO transformation was used too (only for color types 2 and 6)
  1014. if (IHDR.Filter = 64) and (IHDR.ColorType in [2, 6]) then
  1015. TransformLOCOToRGB(Bits, Width * Height, BytesPerPixel);
  1016. // Images with 16 bit channels must be swapped because of PNG's big endianity
  1017. if IHDR.BitDepth = 16 then
  1018. SwapEndianWord(Bits, Width * Height * BytesPerPixel div SizeOf(Word));
  1019. finally
  1020. FreeMem(LineBuffer[True]);
  1021. FreeMem(LineBuffer[False]);
  1022. FreeMem(TotalBuffer);
  1023. FreeMem(ZeroLine);
  1024. end;
  1025. end;
  1026. {$IFNDEF DONT_LINK_JNG}
  1027. procedure TNGFileLoader.LoadImageFromJNGFrame(FrameWidth, FrameHeight: LongInt; const JHDR: TJHDR; IDATStream,
  1028. JDATStream, JDAAStream: TMemoryStream; var Image: TImageData);
  1029. var
  1030. AlphaImage: TImageData;
  1031. FakeIHDR: TIHDR;
  1032. FmtInfo: TImageFormatInfo;
  1033. I: LongInt;
  1034. AlphaPtr: PByte;
  1035. GrayPtr: PWordRec;
  1036. ColorPtr: PColor32Rec;
  1037. procedure LoadJpegFromStream(Stream: TStream; var DestImage: TImageData);
  1038. var
  1039. JpegFormat: TCustomIOJpegFileFormat;
  1040. Handle: TImagingHandle;
  1041. DynImages: TDynImageDataArray;
  1042. begin
  1043. if JHDR.SampleDepth <> 12 then
  1044. begin
  1045. JpegFormat := TCustomIOJpegFileFormat.Create;
  1046. JpegFormat.SetCustomIO(StreamIO);
  1047. Stream.Position := 0;
  1048. Handle := StreamIO.Open(Pointer(Stream), omReadOnly);
  1049. try
  1050. JpegFormat.LoadData(Handle, DynImages, True);
  1051. DestImage := DynImages[0];
  1052. finally
  1053. StreamIO.Close(Handle);
  1054. JpegFormat.Free;
  1055. SetLength(DynImages, 0);
  1056. end;
  1057. end
  1058. else
  1059. NewImage(FrameWidth, FrameHeight, ifR8G8B8, DestImage);
  1060. end;
  1061. begin
  1062. LoadJpegFromStream(JDATStream, Image);
  1063. // If present separate alpha channel is processed
  1064. if (JHDR.ColorType in [12, 14]) and (Image.Format in [ifGray8, ifR8G8B8]) then
  1065. begin
  1066. InitImage(AlphaImage);
  1067. if JHDR.AlphaCompression = 0 then
  1068. begin
  1069. // Alpha channel is PNG compressed
  1070. FakeIHDR.Width := JHDR.Width;
  1071. FakeIHDR.Height := JHDR.Height;
  1072. FakeIHDR.ColorType := 0;
  1073. FakeIHDR.BitDepth := JHDR.AlphaSampleDepth;
  1074. FakeIHDR.Filter := JHDR.AlphaFilter;
  1075. FakeIHDR.Interlacing := JHDR.AlphaInterlacing;
  1076. LoadImageFromPNGFrame(FrameWidth, FrameHeight, FakeIHDR, IDATStream, AlphaImage);
  1077. end
  1078. else
  1079. begin
  1080. // Alpha channel is JPEG compressed
  1081. LoadJpegFromStream(JDAAStream, AlphaImage);
  1082. end;
  1083. // Check if alpha channel is the same size as image
  1084. if (Image.Width <> AlphaImage.Width) and (Image.Height <> AlphaImage.Height) then
  1085. ResizeImage(AlphaImage, Image.Width, Image.Height, rfNearest);
  1086. // Check alpha channels data format
  1087. GetImageFormatInfo(AlphaImage.Format, FmtInfo);
  1088. if (FmtInfo.BytesPerPixel > 1) or (not FmtInfo.HasGrayChannel) then
  1089. ConvertImage(AlphaImage, ifGray8);
  1090. // Convert image to fromat with alpha channel
  1091. if Image.Format = ifGray8 then
  1092. ConvertImage(Image, ifA8Gray8)
  1093. else
  1094. ConvertImage(Image, ifA8R8G8B8);
  1095. // Combine alpha channel with image
  1096. AlphaPtr := AlphaImage.Bits;
  1097. if Image.Format = ifA8Gray8 then
  1098. begin
  1099. GrayPtr := Image.Bits;
  1100. for I := 0 to Image.Width * Image.Height - 1 do
  1101. begin
  1102. GrayPtr.High := AlphaPtr^;
  1103. Inc(GrayPtr);
  1104. Inc(AlphaPtr);
  1105. end;
  1106. end
  1107. else
  1108. begin
  1109. ColorPtr := Image.Bits;
  1110. for I := 0 to Image.Width * Image.Height - 1 do
  1111. begin
  1112. ColorPtr.A := AlphaPtr^;
  1113. Inc(ColorPtr);
  1114. Inc(AlphaPtr);
  1115. end;
  1116. end;
  1117. FreeImage(AlphaImage);
  1118. end;
  1119. end;
  1120. {$ENDIF}
  1121. procedure TNGFileLoader.ApplyFrameSettings(Frame: TFrameInfo; var Image: TImageData);
  1122. var
  1123. FmtInfo: TImageFormatInfo;
  1124. BackGroundColor: TColor64Rec;
  1125. ColorKey: TColor64Rec;
  1126. Alphas: PByteArray;
  1127. AlphasSize: LongInt;
  1128. IsColorKeyPresent: Boolean;
  1129. IsBackGroundPresent: Boolean;
  1130. IsColorFormat: Boolean;
  1131. procedure ConverttRNS;
  1132. begin
  1133. if FmtInfo.IsIndexed then
  1134. begin
  1135. if Alphas = nil then
  1136. begin
  1137. GetMem(Alphas, Frame.TransparencySize);
  1138. Move(Frame.Transparency^, Alphas^, Frame.TransparencySize);
  1139. AlphasSize := Frame.TransparencySize;
  1140. end;
  1141. end
  1142. else if not FmtInfo.HasAlphaChannel then
  1143. begin
  1144. FillChar(ColorKey, SizeOf(ColorKey), 0);
  1145. Move(Frame.Transparency^, ColorKey, Min(Frame.TransparencySize, SizeOf(ColorKey)));
  1146. if IsColorFormat then
  1147. SwapValues(ColorKey.R, ColorKey.B);
  1148. SwapEndianWord(@ColorKey, 3);
  1149. // 1/2/4 bit images were converted to 8 bit so we must convert color key too
  1150. if (not Frame.IsJpegFrame) and (Frame.IHDR.ColorType in [0, 4]) then
  1151. case Frame.IHDR.BitDepth of
  1152. 1: ColorKey.B := Word(ColorKey.B * 255);
  1153. 2: ColorKey.B := Word(ColorKey.B * 85);
  1154. 4: ColorKey.B := Word(ColorKey.B * 17);
  1155. end;
  1156. IsColorKeyPresent := True;
  1157. end;
  1158. end;
  1159. procedure ConvertbKGD;
  1160. begin
  1161. FillChar(BackGroundColor, SizeOf(BackGroundColor), 0);
  1162. Move(Frame.Background^, BackGroundColor, Min(Frame.BackgroundSize, SizeOf(BackGroundColor)));
  1163. if IsColorFormat then
  1164. SwapValues(BackGroundColor.R, BackGroundColor.B);
  1165. SwapEndianWord(@BackGroundColor, 3);
  1166. // 1/2/4 bit images were converted to 8 bit so we must convert back color too
  1167. if (not Frame.IsJpegFrame) and (Frame.IHDR.ColorType in [0, 4]) then
  1168. case Frame.IHDR.BitDepth of
  1169. 1: BackGroundColor.B := Word(BackGroundColor.B * 255);
  1170. 2: BackGroundColor.B := Word(BackGroundColor.B * 85);
  1171. 4: BackGroundColor.B := Word(BackGroundColor.B * 17);
  1172. end;
  1173. IsBackGroundPresent := True;
  1174. end;
  1175. procedure ReconstructPalette;
  1176. var
  1177. I: LongInt;
  1178. begin
  1179. with Image do
  1180. begin
  1181. GetMem(Palette, FmtInfo.PaletteEntries * SizeOf(TColor32Rec));
  1182. FillChar(Palette^, FmtInfo.PaletteEntries * SizeOf(TColor32Rec), $FF);
  1183. // if RGB palette was loaded from file then use it
  1184. if Frame.Palette <> nil then
  1185. for I := 0 to Min(Frame.PaletteEntries, FmtInfo.PaletteEntries) - 1 do
  1186. with Palette[I] do
  1187. begin
  1188. R := Frame.Palette[I].B;
  1189. G := Frame.Palette[I].G;
  1190. B := Frame.Palette[I].R;
  1191. end;
  1192. // if palette alphas were loaded from file then use them
  1193. if Alphas <> nil then
  1194. begin
  1195. for I := 0 to Min(AlphasSize, FmtInfo.PaletteEntries) - 1 do
  1196. Palette[I].A := Alphas[I];
  1197. end;
  1198. end;
  1199. end;
  1200. procedure ApplyColorKey;
  1201. var
  1202. DestFmt: TImageFormat;
  1203. Col32, Bkg32: TColor32Rec;
  1204. OldPixel, NewPixel: Pointer;
  1205. begin
  1206. case Image.Format of
  1207. ifGray8: DestFmt := ifA8Gray8;
  1208. ifGray16: DestFmt := ifA16Gray16;
  1209. ifR8G8B8: DestFmt := ifA8R8G8B8;
  1210. ifR16G16B16: DestFmt := ifA16R16G16B16;
  1211. else
  1212. DestFmt := ifUnknown;
  1213. end;
  1214. if DestFmt <> ifUnknown then
  1215. begin
  1216. if not IsBackGroundPresent then
  1217. BackGroundColor := ColorKey;
  1218. ConvertImage(Image, DestFmt);
  1219. // Now back color and color key must be converted to image's data format, looks ugly
  1220. case Image.Format of
  1221. ifA8Gray8:
  1222. begin
  1223. Col32 := Color32(0, 0, $FF, Byte(ColorKey.B));
  1224. Bkg32 := Color32(0, 0, 0, Byte(BackGroundColor.B));
  1225. end;
  1226. ifA16Gray16:
  1227. begin
  1228. ColorKey.G := $FFFF;
  1229. end;
  1230. ifA8R8G8B8:
  1231. begin
  1232. Col32 := Color32($FF, Byte(ColorKey.R), Byte(ColorKey.G), Byte(ColorKey.B));
  1233. Bkg32 := Color32(0, Byte(BackGroundColor.R), Byte(BackGroundColor.G), Byte(BackGroundColor.B));
  1234. end;
  1235. ifA16R16G16B16:
  1236. begin
  1237. ColorKey.A := $FFFF;
  1238. end;
  1239. end;
  1240. if Image.Format in [ifA8Gray8, ifA8R8G8B8] then
  1241. begin
  1242. OldPixel := @Col32;
  1243. NewPixel := @Bkg32;
  1244. end
  1245. else
  1246. begin
  1247. OldPixel := @ColorKey;
  1248. NewPixel := @BackGroundColor;
  1249. end;
  1250. ReplaceColor(Image, 0, 0, Image.Width, Image.Height, OldPixel, NewPixel);
  1251. end;
  1252. end;
  1253. begin
  1254. Alphas := nil;
  1255. IsColorKeyPresent := False;
  1256. IsBackGroundPresent := False;
  1257. GetImageFormatInfo(Image.Format, FmtInfo);
  1258. IsColorFormat := (Frame.IsJpegFrame and (Frame.JHDR.ColorType in [10, 14])) or
  1259. (not Frame.IsJpegFrame and (Frame.IHDR.ColorType in [2, 6]));
  1260. // Convert some chunk data to useful format
  1261. if Frame.TransparencySize > 0 then
  1262. ConverttRNS;
  1263. if Frame.BackgroundSize > 0 then
  1264. ConvertbKGD;
  1265. // Build palette for indexed images
  1266. if FmtInfo.IsIndexed then
  1267. ReconstructPalette;
  1268. // Apply color keying
  1269. if IsColorKeyPresent and not FmtInfo.HasAlphaChannel then
  1270. ApplyColorKey;
  1271. FreeMemNil(Alphas);
  1272. end;
  1273. { TNGFileSaver class implementation }
  1274. procedure TNGFileSaver.StoreImageToPNGFrame(const IHDR: TIHDR; Bits: Pointer;
  1275. FmtInfo: TImageFormatInfo; IDATStream: TMemoryStream);
  1276. var
  1277. TotalBuffer, CompBuffer, ZeroLine, PrevLine: Pointer;
  1278. FilterLines: array[0..4] of PByteArray;
  1279. TotalSize, CompSize, I, BytesPerLine, BytesPerPixel: Integer;
  1280. Filter: Byte;
  1281. Adaptive: Boolean;
  1282. procedure FilterScanline(Filter: Byte; BytesPerPixel: LongInt; Line, PrevLine, Target: PByteArray);
  1283. var
  1284. I: LongInt;
  1285. begin
  1286. case Filter of
  1287. 0:
  1288. begin
  1289. // No filter
  1290. Move(Line^, Target^, BytesPerLine);
  1291. end;
  1292. 1:
  1293. begin
  1294. // Sub filter
  1295. Move(Line^, Target^, BytesPerPixel);
  1296. for I := BytesPerPixel to BytesPerLine - 1 do
  1297. Target[I] := (Line[I] - Line[I - BytesPerPixel]) and $FF;
  1298. end;
  1299. 2:
  1300. begin
  1301. // Up filter
  1302. for I := 0 to BytesPerLine - 1 do
  1303. Target[I] := (Line[I] - PrevLine[I]) and $FF;
  1304. end;
  1305. 3:
  1306. begin
  1307. // Average filter
  1308. for I := 0 to BytesPerPixel - 1 do
  1309. Target[I] := (Line[I] - PrevLine[I] shr 1) and $FF;
  1310. for I := BytesPerPixel to BytesPerLine - 1 do
  1311. Target[I] := (Line[I] - (Line[I - BytesPerPixel] + PrevLine[I]) shr 1) and $FF;
  1312. end;
  1313. 4:
  1314. begin
  1315. // Paeth filter
  1316. for I := 0 to BytesPerPixel - 1 do
  1317. Target[I] := (Line[I] - PaethPredictor(0, PrevLine[I], 0)) and $FF;
  1318. for I := BytesPerPixel to BytesPerLine - 1 do
  1319. Target[I] := (Line[I] - PaethPredictor(Line[I - BytesPerPixel], PrevLine[I], PrevLine[I - BytesPerPixel])) and $FF;
  1320. end;
  1321. end;
  1322. end;
  1323. procedure AdaptiveFilter(var Filter: Byte; BytesPerPixel: LongInt; Line, PrevLine, Target: PByteArray);
  1324. var
  1325. I, J, BestTest: LongInt;
  1326. Sums: array[0..4] of LongInt;
  1327. begin
  1328. // Compute the output scanline using all five filters,
  1329. // and select the filter that gives the smallest sum of
  1330. // absolute values of outputs
  1331. FillChar(Sums, SizeOf(Sums), 0);
  1332. BestTest := MaxInt;
  1333. for I := 0 to 4 do
  1334. begin
  1335. FilterScanline(I, BytesPerPixel, Line, PrevLine, FilterLines[I]);
  1336. for J := 0 to BytesPerLine - 1 do
  1337. Sums[I] := Sums[I] + Abs(ShortInt(FilterLines[I][J]));
  1338. if Sums[I] < BestTest then
  1339. begin
  1340. Filter := I;
  1341. BestTest := Sums[I];
  1342. end;
  1343. end;
  1344. Move(FilterLines[Filter]^, Target^, BytesPerLine);
  1345. end;
  1346. begin
  1347. // Select precompression filter and compression level
  1348. Adaptive := False;
  1349. Filter := 0;
  1350. case PreFilter of
  1351. 6:
  1352. if not ((IHDR.BitDepth < 8) or (IHDR.ColorType = 3)) then
  1353. Adaptive := True;
  1354. 0..4: Filter := PreFilter;
  1355. else
  1356. if IHDR.ColorType in [2, 6] then
  1357. Filter := 4
  1358. end;
  1359. // Prepare data for compression
  1360. CompBuffer := nil;
  1361. FillChar(FilterLines, SizeOf(FilterLines), 0);
  1362. BytesPerPixel := Max(1, FmtInfo.BytesPerPixel);
  1363. BytesPerLine := FmtInfo.GetPixelsSize(FmtInfo.Format, LongInt(IHDR.Width), 1);
  1364. TotalSize := (BytesPerLine + 1) * LongInt(IHDR.Height);
  1365. GetMem(TotalBuffer, TotalSize);
  1366. GetMem(ZeroLine, BytesPerLine);
  1367. FillChar(ZeroLine^, BytesPerLine, 0);
  1368. PrevLine := ZeroLine;
  1369. if Adaptive then
  1370. begin
  1371. for I := 0 to 4 do
  1372. GetMem(FilterLines[I], BytesPerLine);
  1373. end;
  1374. try
  1375. // Process next scanlines
  1376. for I := 0 to IHDR.Height - 1 do
  1377. begin
  1378. // Filter scanline
  1379. if Adaptive then
  1380. begin
  1381. AdaptiveFilter(Filter, BytesPerPixel, @PByteArray(Bits)[I * BytesPerLine],
  1382. PrevLine, @PByteArray(TotalBuffer)[I * (BytesPerLine + 1) + 1]);
  1383. end
  1384. else
  1385. begin
  1386. FilterScanline(Filter, BytesPerPixel, @PByteArray(Bits)[I * BytesPerLine],
  1387. PrevLine, @PByteArray(TotalBuffer)[I * (BytesPerLine + 1) + 1]);
  1388. end;
  1389. PrevLine := @PByteArray(Bits)[I * BytesPerLine];
  1390. // Swap red and blue if necessary
  1391. if (IHDR.ColorType in [2, 6]) and not FmtInfo.IsRBSwapped then
  1392. begin
  1393. SwapRGB(@PByteArray(TotalBuffer)[I * (BytesPerLine + 1) + 1],
  1394. IHDR.Width, IHDR.BitDepth, BytesPerPixel);
  1395. end;
  1396. // Images with 16 bit channels must be swapped because of PNG's big endianess
  1397. if IHDR.BitDepth = 16 then
  1398. begin
  1399. SwapEndianWord(@PByteArray(TotalBuffer)[I * (BytesPerLine + 1) + 1],
  1400. BytesPerLine div SizeOf(Word));
  1401. end;
  1402. // Set filter used for this scanline
  1403. PByteArray(TotalBuffer)[I * (BytesPerLine + 1)] := Filter;
  1404. end;
  1405. // Compress IDAT data
  1406. CompressBuf(TotalBuffer, TotalSize, CompBuffer, CompSize,
  1407. CompressLevel, ZLibStrategy);
  1408. // Write IDAT data to stream
  1409. IDATStream.WriteBuffer(CompBuffer^, CompSize);
  1410. finally
  1411. FreeMem(TotalBuffer);
  1412. FreeMem(CompBuffer);
  1413. FreeMem(ZeroLine);
  1414. if Adaptive then
  1415. for I := 0 to 4 do
  1416. FreeMem(FilterLines[I]);
  1417. end;
  1418. end;
  1419. {$IFNDEF DONT_LINK_JNG}
  1420. procedure TNGFileSaver.StoreImageToJNGFrame(const JHDR: TJHDR;
  1421. const Image: TImageData; IDATStream, JDATStream,
  1422. JDAAStream: TMemoryStream);
  1423. var
  1424. ColorImage, AlphaImage: TImageData;
  1425. FmtInfo: TImageFormatInfo;
  1426. AlphaPtr: PByte;
  1427. GrayPtr: PWordRec;
  1428. ColorPtr: PColor32Rec;
  1429. I: LongInt;
  1430. FakeIHDR: TIHDR;
  1431. procedure SaveJpegToStream(Stream: TStream; const Image: TImageData);
  1432. var
  1433. JpegFormat: TCustomIOJpegFileFormat;
  1434. Handle: TImagingHandle;
  1435. DynImages: TDynImageDataArray;
  1436. begin
  1437. JpegFormat := TCustomIOJpegFileFormat.Create;
  1438. JpegFormat.SetCustomIO(StreamIO);
  1439. // Only JDAT stream can be saved progressive
  1440. if Stream = JDATStream then
  1441. JpegFormat.FProgressive := Progressive
  1442. else
  1443. JpegFormat.FProgressive := False;
  1444. JpegFormat.FQuality := Quality;
  1445. SetLength(DynImages, 1);
  1446. DynImages[0] := Image;
  1447. Handle := StreamIO.Open(Pointer(Stream), omCreate);
  1448. try
  1449. JpegFormat.SaveData(Handle, DynImages, 0);
  1450. finally
  1451. StreamIO.Close(Handle);
  1452. SetLength(DynImages, 0);
  1453. JpegFormat.Free;
  1454. end;
  1455. end;
  1456. begin
  1457. GetImageFormatInfo(Image.Format, FmtInfo);
  1458. InitImage(ColorImage);
  1459. InitImage(AlphaImage);
  1460. if FmtInfo.HasAlphaChannel then
  1461. begin
  1462. // Create new image for alpha channel and color image without alpha
  1463. CloneImage(Image, ColorImage);
  1464. NewImage(Image.Width, Image.Height, ifGray8, AlphaImage);
  1465. case Image.Format of
  1466. ifA8Gray8: ConvertImage(ColorImage, ifGray8);
  1467. ifA8R8G8B8: ConvertImage(ColorImage, ifR8G8B8);
  1468. end;
  1469. // Store source image's alpha to separate image
  1470. AlphaPtr := AlphaImage.Bits;
  1471. if Image.Format = ifA8Gray8 then
  1472. begin
  1473. GrayPtr := Image.Bits;
  1474. for I := 0 to Image.Width * Image.Height - 1 do
  1475. begin
  1476. AlphaPtr^ := GrayPtr.High;
  1477. Inc(GrayPtr);
  1478. Inc(AlphaPtr);
  1479. end;
  1480. end
  1481. else
  1482. begin
  1483. ColorPtr := Image.Bits;
  1484. for I := 0 to Image.Width * Image.Height - 1 do
  1485. begin
  1486. AlphaPtr^ := ColorPtr.A;
  1487. Inc(ColorPtr);
  1488. Inc(AlphaPtr);
  1489. end;
  1490. end;
  1491. // Write color image to stream as JPEG
  1492. SaveJpegToStream(JDATStream, ColorImage);
  1493. if LossyAlpha then
  1494. begin
  1495. // Write alpha image to stream as JPEG
  1496. SaveJpegToStream(JDAAStream, AlphaImage);
  1497. end
  1498. else
  1499. begin
  1500. // Alpha channel is PNG compressed
  1501. FakeIHDR.Width := JHDR.Width;
  1502. FakeIHDR.Height := JHDR.Height;
  1503. FakeIHDR.ColorType := 0;
  1504. FakeIHDR.BitDepth := JHDR.AlphaSampleDepth;
  1505. FakeIHDR.Filter := JHDR.AlphaFilter;
  1506. FakeIHDR.Interlacing := JHDR.AlphaInterlacing;
  1507. GetImageFormatInfo(AlphaImage.Format, FmtInfo);
  1508. StoreImageToPNGFrame(FakeIHDR, AlphaImage.Bits, FmtInfo, IDATStream);
  1509. end;
  1510. FreeImage(ColorImage);
  1511. FreeImage(AlphaImage);
  1512. end
  1513. else
  1514. begin
  1515. // Simply write JPEG to stream
  1516. SaveJpegToStream(JDATStream, Image);
  1517. end;
  1518. end;
  1519. {$ENDIF}
  1520. procedure TNGFileSaver.AddFrame(const Image: TImageData; IsJpegFrame: Boolean);
  1521. var
  1522. Frame: TFrameInfo;
  1523. FmtInfo: TImageFormatInfo;
  1524. Index: Integer;
  1525. procedure StorePalette;
  1526. var
  1527. Pal: PPalette24;
  1528. Alphas: PByteArray;
  1529. I, PalBytes: LongInt;
  1530. AlphasDiffer: Boolean;
  1531. begin
  1532. // Fill and save RGB part of palette to PLTE chunk
  1533. PalBytes := FmtInfo.PaletteEntries * SizeOf(TColor24Rec);
  1534. GetMem(Pal, PalBytes);
  1535. AlphasDiffer := False;
  1536. for I := 0 to FmtInfo.PaletteEntries - 1 do
  1537. begin
  1538. Pal[I].B := Image.Palette[I].R;
  1539. Pal[I].G := Image.Palette[I].G;
  1540. Pal[I].R := Image.Palette[I].B;
  1541. if Image.Palette[I].A < 255 then
  1542. AlphasDiffer := True;
  1543. end;
  1544. Frame.Palette := Pal;
  1545. Frame.PaletteEntries := FmtInfo.PaletteEntries;
  1546. // Fill and save alpha part (if there are any alphas < 255) of palette to tRNS chunk
  1547. if AlphasDiffer then
  1548. begin
  1549. PalBytes := FmtInfo.PaletteEntries * SizeOf(Byte);
  1550. GetMem(Alphas, PalBytes);
  1551. for I := 0 to FmtInfo.PaletteEntries - 1 do
  1552. Alphas[I] := Image.Palette[I].A;
  1553. Frame.Transparency := Alphas;
  1554. Frame.TransparencySize := PalBytes;
  1555. end;
  1556. end;
  1557. procedure FillFrameControlChunk(const IHDR: TIHDR; var fcTL: TfcTL);
  1558. var
  1559. Delay: Integer;
  1560. begin
  1561. fcTL.SeqNumber := 0; // Decided when writing to file
  1562. fcTL.Width := IHDR.Width;
  1563. fcTL.Height := IHDR.Height;
  1564. fcTL.XOffset := 0;
  1565. fcTL.YOffset := 0;
  1566. fcTL.DelayNumer := 1;
  1567. fcTL.DelayDenom := 3;
  1568. if FileFormat.FMetadata.HasMetaItemForSaving(SMetaFrameDelay, Index) then
  1569. begin
  1570. // Metadata contains frame delay information in milliseconds
  1571. Delay := FileFormat.FMetadata.MetaItemsForSavingMulti[SMetaFrameDelay, Index];
  1572. fcTL.DelayNumer := Delay;
  1573. fcTL.DelayDenom := 1000;
  1574. end;
  1575. fcTL.DisposeOp := DisposeOpNone;
  1576. fcTL.BlendOp := BlendOpSource;
  1577. SwapEndianUInt32(@fcTL, 5);
  1578. fcTL.DelayNumer := SwapEndianWord(fcTL.DelayNumer);
  1579. fcTL.DelayDenom := SwapEndianWord(fcTL.DelayDenom);
  1580. end;
  1581. begin
  1582. // Add new frame
  1583. Frame := AddFrameInfo;
  1584. Frame.IsJpegFrame := IsJpegFrame;
  1585. Index := Length(Frames) - 1;
  1586. with Frame do
  1587. begin
  1588. GetImageFormatInfo(Image.Format, FmtInfo);
  1589. if IsJpegFrame then
  1590. begin
  1591. {$IFNDEF DONT_LINK_JNG}
  1592. // Fill JNG header
  1593. JHDR.Width := Image.Width;
  1594. JHDR.Height := Image.Height;
  1595. case Image.Format of
  1596. ifGray8: JHDR.ColorType := 8;
  1597. ifR8G8B8: JHDR.ColorType := 10;
  1598. ifA8Gray8: JHDR.ColorType := 12;
  1599. ifA8R8G8B8: JHDR.ColorType := 14;
  1600. end;
  1601. JHDR.SampleDepth := 8; // 8-bit samples and quantization tables
  1602. JHDR.Compression := 8; // Huffman coding
  1603. JHDR.Interlacing := Iff(Progressive, 8, 0);
  1604. JHDR.AlphaSampleDepth := Iff(FmtInfo.HasAlphaChannel, 8, 0);
  1605. JHDR.AlphaCompression := Iff(LossyAlpha, 8, 0);
  1606. JHDR.AlphaFilter := 0;
  1607. JHDR.AlphaInterlacing := 0;
  1608. StoreImageToJNGFrame(JHDR, Image, IDATMemory, JDATMemory, JDAAMemory);
  1609. // Finally swap endian
  1610. SwapEndianUInt32(@JHDR, 2);
  1611. {$ENDIF}
  1612. end
  1613. else
  1614. begin
  1615. // Fill PNG header
  1616. IHDR.Width := Image.Width;
  1617. IHDR.Height := Image.Height;
  1618. IHDR.Compression := 0;
  1619. IHDR.Filter := 0;
  1620. IHDR.Interlacing := 0;
  1621. IHDR.BitDepth := FmtInfo.BytesPerPixel * 8;
  1622. // Select appropiate PNG color type and modify bitdepth
  1623. if FmtInfo.HasGrayChannel then
  1624. begin
  1625. IHDR.ColorType := 0;
  1626. if FmtInfo.HasAlphaChannel then
  1627. begin
  1628. IHDR.ColorType := 4;
  1629. IHDR.BitDepth := IHDR.BitDepth div 2;
  1630. end;
  1631. end
  1632. else if FmtInfo.Format = ifBinary then
  1633. begin
  1634. IHDR.ColorType := 0;
  1635. IHDR.BitDepth := 1;
  1636. end
  1637. else if FmtInfo.IsIndexed then
  1638. IHDR.ColorType := 3
  1639. else if FmtInfo.HasAlphaChannel then
  1640. begin
  1641. IHDR.ColorType := 6;
  1642. IHDR.BitDepth := IHDR.BitDepth div 4;
  1643. end
  1644. else
  1645. begin
  1646. IHDR.ColorType := 2;
  1647. IHDR.BitDepth := IHDR.BitDepth div 3;
  1648. end;
  1649. if FileType = ngAPNG then
  1650. begin
  1651. // Fill fcTL chunk of APNG file
  1652. FillFrameControlChunk(IHDR, fcTL);
  1653. end;
  1654. // Compress PNG image and store it to stream
  1655. StoreImageToPNGFrame(IHDR, Image.Bits, FmtInfo, IDATMemory);
  1656. // Store palette if necesary
  1657. if FmtInfo.IsIndexed then
  1658. StorePalette;
  1659. // Finally swap endian
  1660. SwapEndianUInt32(@IHDR, 2);
  1661. end;
  1662. end;
  1663. end;
  1664. function TNGFileSaver.SaveFile(Handle: TImagingHandle): Boolean;
  1665. var
  1666. I: LongInt;
  1667. Chunk: TChunkHeader;
  1668. SeqNo: UInt32;
  1669. function GetNextSeqNo: UInt32;
  1670. begin
  1671. // Seq numbers of fcTL and fdAT are "interleaved" as they share the counter.
  1672. // Example: first fcTL for IDAT has seq=0, next is fcTL for seond frame with
  1673. // seq=1, then first fdAT with seq=2, fcTL seq=3, fdAT=4, ...
  1674. Result := SwapEndianUInt32(SeqNo);
  1675. Inc(SeqNo);
  1676. end;
  1677. function CalcChunkCrc(const ChunkHdr: TChunkHeader; Data: Pointer;
  1678. Size: LongInt): UInt32;
  1679. begin
  1680. Result := $FFFFFFFF;
  1681. CalcCrc32(Result, @ChunkHdr.ChunkID, SizeOf(ChunkHdr.ChunkID));
  1682. CalcCrc32(Result, Data, Size);
  1683. Result := SwapEndianUInt32(Result xor $FFFFFFFF);
  1684. end;
  1685. procedure WriteChunk(var Chunk: TChunkHeader; ChunkData: Pointer);
  1686. var
  1687. ChunkCrc: UInt32;
  1688. SizeToWrite: LongInt;
  1689. begin
  1690. SizeToWrite := Chunk.DataSize;
  1691. Chunk.DataSize := SwapEndianUInt32(Chunk.DataSize);
  1692. ChunkCrc := CalcChunkCrc(Chunk, ChunkData, SizeToWrite);
  1693. GetIO.Write(Handle, @Chunk, SizeOf(Chunk));
  1694. if SizeToWrite <> 0 then
  1695. GetIO.Write(Handle, ChunkData, SizeToWrite);
  1696. GetIO.Write(Handle, @ChunkCrc, SizeOf(ChunkCrc));
  1697. end;
  1698. procedure WritefdAT(Frame: TFrameInfo);
  1699. var
  1700. ChunkCrc: UInt32;
  1701. ChunkSeqNo: UInt32;
  1702. begin
  1703. Chunk.ChunkID := fdATChunk;
  1704. ChunkSeqNo := GetNextSeqNo;
  1705. // fdAT saves seq number UInt32 before compressed pixels
  1706. Chunk.DataSize := Frame.IDATMemory.Size + SizeOf(UInt32);
  1707. Chunk.DataSize := SwapEndianUInt32(Chunk.DataSize);
  1708. // Calc CRC
  1709. ChunkCrc := $FFFFFFFF;
  1710. CalcCrc32(ChunkCrc, @Chunk.ChunkID, SizeOf(Chunk.ChunkID));
  1711. CalcCrc32(ChunkCrc, @ChunkSeqNo, SizeOf(ChunkSeqNo));
  1712. CalcCrc32(ChunkCrc, Frame.IDATMemory.Memory, Frame.IDATMemory.Size);
  1713. ChunkCrc := SwapEndianUInt32(ChunkCrc xor $FFFFFFFF);
  1714. // Write out all fdAT data
  1715. GetIO.Write(Handle, @Chunk, SizeOf(Chunk));
  1716. GetIO.Write(Handle, @ChunkSeqNo, SizeOf(ChunkSeqNo));
  1717. GetIO.Write(Handle, Frame.IDATMemory.Memory, Frame.IDATMemory.Size);
  1718. GetIO.Write(Handle, @ChunkCrc, SizeOf(ChunkCrc));
  1719. end;
  1720. procedure WriteGlobalMetaDataChunks(Frame: TFrameInfo);
  1721. var
  1722. XRes, YRes: Single;
  1723. begin
  1724. if FileFormat.FMetadata.GetPhysicalPixelSize(ruDpm, XRes, YRes, True) then
  1725. begin
  1726. // Save pHYs chunk
  1727. Frame.pHYs.UnitSpecifier := 1;
  1728. // PNG stores physical resolution as dots per meter
  1729. Frame.pHYs.PixelsPerUnitX := Round(XRes);
  1730. Frame.pHYs.PixelsPerUnitY := Round(YRes);
  1731. Chunk.DataSize := SizeOf(Frame.pHYs);
  1732. Chunk.ChunkID := pHYsChunk;
  1733. SwapEndianUInt32(@Frame.pHYs, SizeOf(Frame.pHYs) div SizeOf(UInt32));
  1734. WriteChunk(Chunk, @Frame.pHYs);
  1735. end;
  1736. end;
  1737. procedure WritePNGMainImageChunks(Frame: TFrameInfo);
  1738. begin
  1739. with Frame do
  1740. begin
  1741. // Write IHDR chunk
  1742. Chunk.DataSize := SizeOf(IHDR);
  1743. Chunk.ChunkID := IHDRChunk;
  1744. WriteChunk(Chunk, @IHDR);
  1745. // Write PLTE chunk if data is present
  1746. if Palette <> nil then
  1747. begin
  1748. Chunk.DataSize := PaletteEntries * SizeOf(TColor24Rec);
  1749. Chunk.ChunkID := PLTEChunk;
  1750. WriteChunk(Chunk, Palette);
  1751. end;
  1752. // Write tRNS chunk if data is present
  1753. if Transparency <> nil then
  1754. begin
  1755. Chunk.DataSize := TransparencySize;
  1756. Chunk.ChunkID := tRNSChunk;
  1757. WriteChunk(Chunk, Transparency);
  1758. end;
  1759. end;
  1760. // Write metadata related chunks
  1761. WriteGlobalMetaDataChunks(Frame);
  1762. end;
  1763. begin
  1764. Result := False;
  1765. SeqNo := 0;
  1766. case FileType of
  1767. ngPNG, ngAPNG: GetIO.Write(Handle, @PNGSignature, SizeOf(TChar8));
  1768. ngMNG: GetIO.Write(Handle, @MNGSignature, SizeOf(TChar8));
  1769. ngJNG: GetIO.Write(Handle, @JNGSignature, SizeOf(TChar8));
  1770. end;
  1771. if FileType = ngMNG then
  1772. begin
  1773. // MNG - main header before frames
  1774. SwapEndianUInt32(@MHDR, SizeOf(MHDR) div SizeOf(UInt32));
  1775. Chunk.DataSize := SizeOf(MHDR);
  1776. Chunk.ChunkID := MHDRChunk;
  1777. WriteChunk(Chunk, @MHDR);
  1778. end
  1779. else if FileType = ngAPNG then
  1780. begin
  1781. // APNG - IHDR and global chunks for all frames, then acTL chunk, then frames
  1782. // (fcTL+IDAT, fcTL+fdAT, fcTL+fdAT, fcTL+fdAT, ....)
  1783. WritePNGMainImageChunks(Frames[0]);
  1784. // Animation control chunk
  1785. acTL.NumFrames := Length(Frames);
  1786. if FileFormat.FMetadata.HasMetaItemForSaving(SMetaAnimationLoops) then
  1787. begin
  1788. // Number of plays of APNG animation
  1789. acTL.NumPlay:= FileFormat.FMetadata.MetaItemsForSaving[SMetaAnimationLoops];
  1790. end
  1791. else
  1792. acTL.NumPlay := 0;
  1793. SwapEndianUInt32(@acTL, SizeOf(acTL) div SizeOf(UInt32));
  1794. Chunk.DataSize := SizeOf(acTL);
  1795. Chunk.ChunkID := acTLChunk;
  1796. WriteChunk(Chunk, @acTL);
  1797. end;
  1798. for I := 0 to Length(Frames) - 1 do
  1799. with Frames[I] do
  1800. begin
  1801. if IsJpegFrame then
  1802. begin
  1803. // Write JHDR chunk
  1804. Chunk.DataSize := SizeOf(JHDR);
  1805. Chunk.ChunkID := JHDRChunk;
  1806. WriteChunk(Chunk, @JHDR);
  1807. // Write metadata related chunks
  1808. WriteGlobalMetaDataChunks(Frames[I]);
  1809. // Write JNG image data
  1810. Chunk.DataSize := JDATMemory.Size;
  1811. Chunk.ChunkID := JDATChunk;
  1812. WriteChunk(Chunk, JDATMemory.Memory);
  1813. // Write alpha channel if present
  1814. if JHDR.AlphaSampleDepth > 0 then
  1815. begin
  1816. if JHDR.AlphaCompression = 0 then
  1817. begin
  1818. // Alpha is PNG compressed
  1819. Chunk.DataSize := IDATMemory.Size;
  1820. Chunk.ChunkID := IDATChunk;
  1821. WriteChunk(Chunk, IDATMemory.Memory);
  1822. end
  1823. else
  1824. begin
  1825. // Alpha is JNG compressed
  1826. Chunk.DataSize := JDAAMemory.Size;
  1827. Chunk.ChunkID := JDAAChunk;
  1828. WriteChunk(Chunk, JDAAMemory.Memory);
  1829. end;
  1830. end;
  1831. // Write image end
  1832. Chunk.DataSize := 0;
  1833. Chunk.ChunkID := IENDChunk;
  1834. WriteChunk(Chunk, nil);
  1835. end
  1836. else if FileType <> ngAPNG then
  1837. begin
  1838. // Regular PNG frame (single PNG image or MNG frame)
  1839. WritePNGMainImageChunks(Frames[I]);
  1840. // Write PNG image data
  1841. Chunk.DataSize := IDATMemory.Size;
  1842. Chunk.ChunkID := IDATChunk;
  1843. WriteChunk(Chunk, IDATMemory.Memory);
  1844. // Write image end
  1845. Chunk.DataSize := 0;
  1846. Chunk.ChunkID := IENDChunk;
  1847. WriteChunk(Chunk, nil);
  1848. end
  1849. else if FileType = ngAPNG then
  1850. begin
  1851. // APNG frame - Write fcTL before frame data
  1852. Chunk.DataSize := SizeOf(fcTL);
  1853. Chunk.ChunkID := fcTLChunk;
  1854. fcTl.SeqNumber := GetNextSeqNo;
  1855. WriteChunk(Chunk, @fcTL);
  1856. // Write data - IDAT for first frame and fdAT for following ones
  1857. if I = 0 then
  1858. begin
  1859. Chunk.DataSize := IDATMemory.Size;
  1860. Chunk.ChunkID := IDATChunk;
  1861. WriteChunk(Chunk, IDATMemory.Memory);
  1862. end
  1863. else
  1864. WritefdAT(Frames[I]);
  1865. // Write image end after last frame
  1866. if I = Length(Frames) - 1 then
  1867. begin
  1868. Chunk.DataSize := 0;
  1869. Chunk.ChunkID := IENDChunk;
  1870. WriteChunk(Chunk, nil);
  1871. end;
  1872. end;
  1873. end;
  1874. if FileType = ngMNG then
  1875. begin
  1876. Chunk.DataSize := 0;
  1877. Chunk.ChunkID := MENDChunk;
  1878. WriteChunk(Chunk, nil);
  1879. end;
  1880. end;
  1881. procedure TNGFileSaver.SetFileOptions;
  1882. begin
  1883. PreFilter := FileFormat.FPreFilter;
  1884. CompressLevel := FileFormat.FCompressLevel;
  1885. LossyAlpha := FileFormat.FLossyAlpha;
  1886. Quality := FileFormat.FQuality;
  1887. Progressive := FileFormat.FProgressive;
  1888. ZLibStrategy := FileFormat.FZLibStategy;
  1889. end;
  1890. { TAPNGAnimator class implementation }
  1891. class procedure TAPNGAnimator.Animate(var Images: TDynImageDataArray;
  1892. const acTL: TacTL; const SrcFrames: array of TFrameInfo);
  1893. var
  1894. I, SrcIdx, Offset, Len: Integer;
  1895. DestFrames: TDynImageDataArray;
  1896. SrcCanvas, DestCanvas: TImagingCanvas;
  1897. PreviousCache: TImageData;
  1898. DestFormat: TImageFormat;
  1899. FormatInfo: TImageFormatInfo;
  1900. AnimatingNeeded, BlendingNeeded: Boolean;
  1901. procedure CheckFrames;
  1902. var
  1903. I: Integer;
  1904. begin
  1905. for I := 0 to Len - 1 do
  1906. with SrcFrames[I] do
  1907. begin
  1908. if (FrameWidth <> Integer(IHDR.Width)) or (FrameHeight <> Integer(IHDR.Height)) or (Len <> Integer(acTL.NumFrames)) or
  1909. (not ((fcTL.DisposeOp = DisposeOpNone) and (fcTL.BlendOp = BlendOpSource)) and
  1910. not ((fcTL.DisposeOp = DisposeOpBackground) and (fcTL.BlendOp = BlendOpSource)) and
  1911. not ((fcTL.DisposeOp = DisposeOpBackground) and (fcTL.BlendOp = BlendOpOver))) then
  1912. begin
  1913. AnimatingNeeded := True;
  1914. end;
  1915. if fcTL.BlendOp = BlendOpOver then
  1916. BlendingNeeded := True;
  1917. if AnimatingNeeded and BlendingNeeded then
  1918. Exit;
  1919. end;
  1920. end;
  1921. begin
  1922. AnimatingNeeded := False;
  1923. BlendingNeeded := False;
  1924. Len := Length(SrcFrames);
  1925. CheckFrames;
  1926. if (Len = 0) or not AnimatingNeeded then
  1927. Exit;
  1928. if (Len = Integer(acTL.NumFrames) + 1) and (SrcFrames[0].fcTL.Width = 0) then
  1929. begin
  1930. // If default image (stored in IDAT chunk) isn't part of animation we ignore it
  1931. Offset := 1;
  1932. Len := Len - 1;
  1933. end
  1934. else
  1935. Offset := 0;
  1936. DestFormat := Images[0].Format;
  1937. GetImageFormatInfo(DestFormat, FormatInfo);
  1938. if BlendingNeeded and FormatInfo.IsIndexed then // alpha blending needed -> destination cannot be indexed
  1939. DestFormat := ifA8R8G8B8;
  1940. SetLength(DestFrames, Len);
  1941. DestCanvas := ImagingCanvases.FindBestCanvasForImage(DestFormat).Create;
  1942. SrcCanvas := ImagingCanvases.FindBestCanvasForImage(Images[0]).Create;
  1943. InitImage(PreviousCache);
  1944. NewImage(SrcFrames[0].IHDR.Width, SrcFrames[0].IHDR.Height, DestFormat, PreviousCache);
  1945. for I := 0 to Len - 1 do
  1946. begin
  1947. SrcIdx := I + Offset;
  1948. NewImage(SrcFrames[SrcIdx].IHDR.Width, SrcFrames[SrcIdx].IHDR.Height,
  1949. DestFormat, DestFrames[I]);
  1950. if DestFrames[I].Format = ifIndex8 then
  1951. Move(Images[SrcIdx].Palette^, DestFrames[I].Palette^, 256 * SizeOf(TColor32));
  1952. DestCanvas.CreateForData(@DestFrames[I]);
  1953. if (SrcFrames[SrcIdx].fcTL.DisposeOp = DisposeOpPrevious) and (SrcFrames[SrcIdx - 1].fcTL.DisposeOp <> DisposeOpPrevious) then
  1954. begin
  1955. // Cache current output buffer so we may return to it later (previous dispose op)
  1956. CopyRect(DestFrames[I - 1], 0, 0, DestFrames[I - 1].Width, DestFrames[I - 1].Height,
  1957. PreviousCache, 0, 0);
  1958. end;
  1959. if (I = 0) or (SrcIdx = 0) then
  1960. begin
  1961. // Clear whole frame with transparent black color (default for first frame)
  1962. DestCanvas.FillColor32 := pcClear;
  1963. DestCanvas.Clear;
  1964. end
  1965. else if SrcFrames[SrcIdx - 1].fcTL.DisposeOp = DisposeOpBackground then
  1966. begin
  1967. // Restore background color (clear) on previous frame's area and leave previous content outside of it
  1968. CopyRect(DestFrames[I - 1], 0, 0, DestFrames[I - 1].Width, DestFrames[I - 1].Height,
  1969. DestFrames[I], 0, 0);
  1970. DestCanvas.FillColor32 := pcClear;
  1971. DestCanvas.FillRect(BoundsToRect(SrcFrames[SrcIdx - 1].fcTL.XOffset, SrcFrames[SrcIdx - 1].fcTL.YOffset,
  1972. SrcFrames[SrcIdx - 1].FrameWidth, SrcFrames[SrcIdx - 1].FrameHeight));
  1973. end
  1974. else if SrcFrames[SrcIdx - 1].fcTL.DisposeOp = DisposeOpNone then
  1975. begin
  1976. // Clone previous frame - no change to output buffer
  1977. CopyRect(DestFrames[I - 1], 0, 0, DestFrames[I - 1].Width, DestFrames[I - 1].Height,
  1978. DestFrames[I], 0, 0);
  1979. end
  1980. else if SrcFrames[SrcIdx - 1].fcTL.DisposeOp = DisposeOpPrevious then
  1981. begin
  1982. // Revert to previous frame (cached, can't just restore DestFrames[I - 2])
  1983. CopyRect(PreviousCache, 0, 0, PreviousCache.Width, PreviousCache.Height,
  1984. DestFrames[I], 0, 0);
  1985. end;
  1986. // Copy pixels or alpha blend them over
  1987. if SrcFrames[SrcIdx].fcTL.BlendOp = BlendOpSource then
  1988. begin
  1989. CopyRect(Images[SrcIdx], 0, 0, Images[SrcIdx].Width, Images[SrcIdx].Height,
  1990. DestFrames[I], SrcFrames[SrcIdx].fcTL.XOffset, SrcFrames[SrcIdx].fcTL.YOffset);
  1991. end
  1992. else if SrcFrames[SrcIdx].fcTL.BlendOp = BlendOpOver then
  1993. begin
  1994. SrcCanvas.CreateForData(@Images[SrcIdx]);
  1995. SrcCanvas.DrawAlpha(SrcCanvas.ClipRect, DestCanvas,
  1996. SrcFrames[SrcIdx].fcTL.XOffset, SrcFrames[SrcIdx].fcTL.YOffset);
  1997. end;
  1998. FreeImage(Images[SrcIdx]);
  1999. end;
  2000. DestCanvas.Free;
  2001. SrcCanvas.Free;
  2002. FreeImage(PreviousCache);
  2003. // Assign dest frames to final output images
  2004. Images := DestFrames;
  2005. end;
  2006. { TNetworkGraphicsFileFormat class implementation }
  2007. procedure TNetworkGraphicsFileFormat.Define;
  2008. begin
  2009. inherited;
  2010. FFeatures := [ffLoad, ffSave];
  2011. FPreFilter := NGDefaultPreFilter;
  2012. FCompressLevel := NGDefaultCompressLevel;
  2013. FLossyAlpha := NGDefaultLossyAlpha;
  2014. FLossyCompression := NGDefaultLossyCompression;
  2015. FQuality := NGDefaultQuality;
  2016. FProgressive := NGDefaultProgressive;
  2017. FZLibStategy := NGDefaultZLibStartegy;
  2018. end;
  2019. procedure TNetworkGraphicsFileFormat.CheckOptionsValidity;
  2020. begin
  2021. // Just check if save options has valid values
  2022. if not (FPreFilter in [0..6]) then
  2023. FPreFilter := NGDefaultPreFilter;
  2024. if not (FCompressLevel in [0..9]) then
  2025. FCompressLevel := NGDefaultCompressLevel;
  2026. if not (FQuality in [1..100]) then
  2027. FQuality := NGDefaultQuality;
  2028. end;
  2029. function TNetworkGraphicsFileFormat.GetSupportedFormats: TImageFormats;
  2030. begin
  2031. if FLossyCompression then
  2032. Result := NGLossyFormats
  2033. else
  2034. Result := NGLosslessFormats;
  2035. end;
  2036. procedure TNetworkGraphicsFileFormat.ConvertToSupported(var Image: TImageData;
  2037. const Info: TImageFormatInfo);
  2038. var
  2039. ConvFormat: TImageFormat;
  2040. begin
  2041. if not FLossyCompression then
  2042. begin
  2043. // Convert formats for lossless compression
  2044. if Info.HasGrayChannel then
  2045. begin
  2046. if Info.HasAlphaChannel then
  2047. begin
  2048. if Info.BytesPerPixel <= 2 then
  2049. // Convert <= 16bit grayscale images with alpha to ifA8Gray8
  2050. ConvFormat := ifA8Gray8
  2051. else
  2052. // Convert > 16bit grayscale images with alpha to ifA16Gray16
  2053. ConvFormat := ifA16Gray16
  2054. end
  2055. else
  2056. // Convert grayscale images without alpha to ifGray16
  2057. ConvFormat := ifGray16;
  2058. end
  2059. else
  2060. if Info.IsFloatingPoint then
  2061. // Convert floating point images to 64 bit ARGB (or RGB if no alpha)
  2062. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16B16G16R16, ifB16G16R16)
  2063. else if Info.HasAlphaChannel or Info.IsSpecial then
  2064. // Convert all other images with alpha or special images to A8R8G8B8
  2065. ConvFormat := ifA8R8G8B8
  2066. else
  2067. // Convert images without alpha to R8G8B8
  2068. ConvFormat := ifR8G8B8;
  2069. end
  2070. else
  2071. begin
  2072. // Convert formats for lossy compression
  2073. if Info.HasGrayChannel then
  2074. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8Gray8, ifGray8)
  2075. else
  2076. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifR8G8B8);
  2077. end;
  2078. ConvertImage(Image, ConvFormat);
  2079. end;
  2080. function TNetworkGraphicsFileFormat.TestFormat(Handle: TImagingHandle): Boolean;
  2081. var
  2082. ReadCount: LongInt;
  2083. Sig: TChar8;
  2084. begin
  2085. Result := False;
  2086. if Handle <> nil then
  2087. with GetIO do
  2088. begin
  2089. FillChar(Sig, SizeOf(Sig), 0);
  2090. ReadCount := Read(Handle, @Sig, SizeOf(Sig));
  2091. Seek(Handle, -ReadCount, smFromCurrent);
  2092. Result := (ReadCount = SizeOf(Sig)) and (Sig = FSignature);
  2093. end;
  2094. end;
  2095. { TPNGFileFormat class implementation }
  2096. procedure TPNGFileFormat.Define;
  2097. begin
  2098. inherited;
  2099. FName := SPNGFormatName;
  2100. FFeatures := FFeatures + [ffMultiImage];
  2101. FLoadAnimated := PNGDefaultLoadAnimated;
  2102. AddMasks(SPNGMasks);
  2103. FSignature := PNGSignature;
  2104. RegisterOption(ImagingPNGPreFilter, @FPreFilter);
  2105. RegisterOption(ImagingPNGCompressLevel, @FCompressLevel);
  2106. RegisterOption(ImagingPNGLoadAnimated, @FLoadAnimated);
  2107. RegisterOption(ImagingPNGZLibStrategy, @FZLibStategy);
  2108. end;
  2109. function TPNGFileFormat.LoadData(Handle: TImagingHandle;
  2110. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  2111. var
  2112. I, Len: LongInt;
  2113. NGFileLoader: TNGFileLoader;
  2114. begin
  2115. Result := False;
  2116. NGFileLoader := TNGFileLoader.Create(Self);
  2117. try
  2118. // Use NG file parser to load file
  2119. if NGFileLoader.LoadFile(Handle) and (Length(NGFileLoader.Frames) > 0) then
  2120. begin
  2121. Len := Length(NGFileLoader.Frames);
  2122. SetLength(Images, Len);
  2123. for I := 0 to Len - 1 do
  2124. with NGFileLoader.Frames[I] do
  2125. begin
  2126. // Build actual image bits
  2127. if not IsJpegFrame then
  2128. NGFileLoader.LoadImageFromPNGFrame(FrameWidth, FrameHeight, IHDR, IDATMemory, Images[I]);
  2129. // Build palette, aply color key or background
  2130. NGFileLoader.ApplyFrameSettings(NGFileLoader.Frames[I], Images[I]);
  2131. Result := True;
  2132. end;
  2133. // Animate APNG images
  2134. if (NGFileLoader.FileType = ngAPNG) and FLoadAnimated then
  2135. TAPNGAnimator.Animate(Images, NGFileLoader.acTL, NGFileLoader.Frames);
  2136. end;
  2137. finally
  2138. NGFileLoader.LoadMetaData; // Store metadata
  2139. NGFileLoader.Free;
  2140. end;
  2141. end;
  2142. function TPNGFileFormat.SaveData(Handle: TImagingHandle;
  2143. const Images: TDynImageDataArray; Index: LongInt): Boolean;
  2144. var
  2145. I: Integer;
  2146. ImageToSave: TImageData;
  2147. MustBeFreed: Boolean;
  2148. NGFileSaver: TNGFileSaver;
  2149. DefaultFormat: TImageFormat;
  2150. Screen: TImageData;
  2151. AnimWidth, AnimHeight: Integer;
  2152. begin
  2153. Result := False;
  2154. DefaultFormat := ifDefault;
  2155. AnimWidth := 0;
  2156. AnimHeight := 0;
  2157. NGFileSaver := TNGFileSaver.Create(Self);
  2158. // Save images with more frames as APNG format
  2159. if Length(Images) > 1 then
  2160. begin
  2161. NGFileSaver.FileType := ngAPNG;
  2162. // Get max dimensions of frames
  2163. AnimWidth := Images[FFirstIdx].Width;
  2164. AnimHeight := Images[FFirstIdx].Height;
  2165. for I := FFirstIdx + 1 to FLastIdx do
  2166. begin
  2167. AnimWidth := Max(AnimWidth, Images[I].Width);
  2168. AnimHeight := Max(AnimHeight, Images[I].Height);
  2169. end;
  2170. end
  2171. else
  2172. NGFileSaver.FileType := ngPNG;
  2173. NGFileSaver.SetFileOptions;
  2174. with NGFileSaver do
  2175. try
  2176. // Store all frames to be saved frames file saver
  2177. for I := FFirstIdx to FLastIdx do
  2178. begin
  2179. if MakeCompatible(Images[I], ImageToSave, MustBeFreed) then
  2180. try
  2181. if FileType = ngAPNG then
  2182. begin
  2183. // IHDR chunk is shared for all frames so all frames must have the
  2184. // same data format as the first image.
  2185. if I = FFirstIdx then
  2186. begin
  2187. DefaultFormat := ImageToSave.Format;
  2188. // Subsequenet frames may be bigger than the first one.
  2189. // APNG doens't support this - max allowed size is what's written in
  2190. // IHDR - size of main/default/first image. If some frame is
  2191. // bigger than the first one we need to resize (create empty bigger
  2192. // image and copy) the first frame so all following frames could fit to
  2193. // its area.
  2194. if (ImageToSave.Width <> AnimWidth) or (ImageToSave.Height <> AnimHeight) then
  2195. begin
  2196. InitImage(Screen);
  2197. NewImage(AnimWidth, AnimHeight, ImageToSave.Format, Screen);
  2198. CopyRect(ImageToSave, 0, 0, ImageToSave.Width, ImageToSave.Height, Screen, 0, 0);
  2199. if MustBeFreed then
  2200. FreeImage(ImageToSave);
  2201. ImageToSave := Screen;
  2202. end;
  2203. end
  2204. else if ImageToSave.Format <> DefaultFormat then
  2205. begin
  2206. if MustBeFreed then
  2207. ConvertImage(ImageToSave, DefaultFormat)
  2208. else
  2209. begin
  2210. CloneImage(Images[I], ImageToSave);
  2211. ConvertImage(ImageToSave, DefaultFormat);
  2212. MustBeFreed := True;
  2213. end;
  2214. end;
  2215. end;
  2216. // Add image as PNG frame
  2217. AddFrame(ImageToSave, False);
  2218. finally
  2219. if MustBeFreed then
  2220. FreeImage(ImageToSave);
  2221. end
  2222. else
  2223. Exit;
  2224. end;
  2225. // Finally save PNG file
  2226. SaveFile(Handle);
  2227. Result := True;
  2228. finally
  2229. NGFileSaver.Free;
  2230. end;
  2231. end;
  2232. {$IFNDEF DONT_LINK_MNG}
  2233. { TMNGFileFormat class implementation }
  2234. procedure TMNGFileFormat.Define;
  2235. begin
  2236. inherited;
  2237. FName := SMNGFormatName;
  2238. FFeatures := FFeatures + [ffMultiImage];
  2239. AddMasks(SMNGMasks);
  2240. FSignature := MNGSignature;
  2241. RegisterOption(ImagingMNGLossyCompression, @FLossyCompression);
  2242. RegisterOption(ImagingMNGLossyAlpha, @FLossyAlpha);
  2243. RegisterOption(ImagingMNGPreFilter, @FPreFilter);
  2244. RegisterOption(ImagingMNGCompressLevel, @FCompressLevel);
  2245. RegisterOption(ImagingMNGQuality, @FQuality);
  2246. RegisterOption(ImagingMNGProgressive, @FProgressive);
  2247. end;
  2248. function TMNGFileFormat.LoadData(Handle: TImagingHandle;
  2249. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  2250. var
  2251. NGFileLoader: TNGFileLoader;
  2252. I, Len: LongInt;
  2253. begin
  2254. Result := False;
  2255. NGFileLoader := TNGFileLoader.Create(Self);
  2256. try
  2257. // Use NG file parser to load file
  2258. if NGFileLoader.LoadFile(Handle) then
  2259. begin
  2260. Len := Length(NGFileLoader.Frames);
  2261. if Len > 0 then
  2262. begin
  2263. SetLength(Images, Len);
  2264. for I := 0 to Len - 1 do
  2265. with NGFileLoader.Frames[I] do
  2266. begin
  2267. // Build actual image bits
  2268. if IsJpegFrame then
  2269. NGFileLoader.LoadImageFromJNGFrame(FrameWidth, FrameHeight, JHDR, IDATMemory, JDATMemory, JDAAMemory, Images[I])
  2270. else
  2271. NGFileLoader.LoadImageFromPNGFrame(FrameWidth, FrameHeight, IHDR, IDATMemory, Images[I]);
  2272. // Build palette, aply color key or background
  2273. NGFileLoader.ApplyFrameSettings(NGFileLoader.Frames[I], Images[I]);
  2274. end;
  2275. end
  2276. else
  2277. begin
  2278. // Some MNG files (with BASI-IEND streams) dont have actual pixel data
  2279. SetLength(Images, 1);
  2280. NewImage(NGFileLoader.MHDR.FrameWidth, NGFileLoader.MHDR.FrameWidth, ifDefault, Images[0]);
  2281. end;
  2282. Result := True;
  2283. end;
  2284. finally
  2285. NGFileLoader.LoadMetaData; // Store metadata
  2286. NGFileLoader.Free;
  2287. end;
  2288. end;
  2289. function TMNGFileFormat.SaveData(Handle: TImagingHandle;
  2290. const Images: TDynImageDataArray; Index: LongInt): Boolean;
  2291. var
  2292. NGFileSaver: TNGFileSaver;
  2293. I, LargestWidth, LargestHeight: LongInt;
  2294. ImageToSave: TImageData;
  2295. MustBeFreed: Boolean;
  2296. begin
  2297. Result := False;
  2298. LargestWidth := 0;
  2299. LargestHeight := 0;
  2300. NGFileSaver := TNGFileSaver.Create(Self);
  2301. NGFileSaver.FileType := ngMNG;
  2302. NGFileSaver.SetFileOptions;
  2303. with NGFileSaver do
  2304. try
  2305. // Store all frames to be saved frames file saver
  2306. for I := FFirstIdx to FLastIdx do
  2307. begin
  2308. if MakeCompatible(Images[I], ImageToSave, MustBeFreed) then
  2309. try
  2310. // Add image as PNG or JNG frame
  2311. AddFrame(ImageToSave, FLossyCompression);
  2312. // Remember largest frame width and height
  2313. LargestWidth := Iff(LargestWidth < ImageToSave.Width, ImageToSave.Width, LargestWidth);
  2314. LargestHeight := Iff(LargestHeight < ImageToSave.Height, ImageToSave.Height, LargestHeight);
  2315. finally
  2316. if MustBeFreed then
  2317. FreeImage(ImageToSave);
  2318. end
  2319. else
  2320. Exit;
  2321. end;
  2322. // Fill MNG header
  2323. MHDR.FrameWidth := LargestWidth;
  2324. MHDR.FrameHeight := LargestHeight;
  2325. MHDR.TicksPerSecond := 0;
  2326. MHDR.NominalLayerCount := 0;
  2327. MHDR.NominalFrameCount := Length(Frames);
  2328. MHDR.NominalPlayTime := 0;
  2329. MHDR.SimplicityProfile := 473; // 111011001 binary, defines MNG-VLC with transparency and JNG support
  2330. // Finally save MNG file
  2331. SaveFile(Handle);
  2332. Result := True;
  2333. finally
  2334. NGFileSaver.Free;
  2335. end;
  2336. end;
  2337. {$ENDIF}
  2338. {$IFNDEF DONT_LINK_JNG}
  2339. { TJNGFileFormat class implementation }
  2340. procedure TJNGFileFormat.Define;
  2341. begin
  2342. inherited;
  2343. FName := SJNGFormatName;
  2344. AddMasks(SJNGMasks);
  2345. FSignature := JNGSignature;
  2346. FLossyCompression := True;
  2347. RegisterOption(ImagingJNGLossyAlpha, @FLossyAlpha);
  2348. RegisterOption(ImagingJNGAlphaPreFilter, @FPreFilter);
  2349. RegisterOption(ImagingJNGAlphaCompressLevel, @FCompressLevel);
  2350. RegisterOption(ImagingJNGQuality, @FQuality);
  2351. RegisterOption(ImagingJNGProgressive, @FProgressive);
  2352. end;
  2353. function TJNGFileFormat.LoadData(Handle: TImagingHandle;
  2354. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  2355. var
  2356. NGFileLoader: TNGFileLoader;
  2357. begin
  2358. Result := False;
  2359. NGFileLoader := TNGFileLoader.Create(Self);
  2360. try
  2361. // Use NG file parser to load file
  2362. if NGFileLoader.LoadFile(Handle) and (Length(NGFileLoader.Frames) > 0) then
  2363. with NGFileLoader.Frames[0] do
  2364. begin
  2365. SetLength(Images, 1);
  2366. // Build actual image bits
  2367. if IsJpegFrame then
  2368. NGFileLoader.LoadImageFromJNGFrame(FrameWidth, FrameHeight, JHDR, IDATMemory, JDATMemory, JDAAMemory, Images[0]);
  2369. // Build palette, aply color key or background
  2370. NGFileLoader.ApplyFrameSettings(NGFileLoader.Frames[0], Images[0]);
  2371. Result := True;
  2372. end;
  2373. finally
  2374. NGFileLoader.LoadMetaData; // Store metadata
  2375. NGFileLoader.Free;
  2376. end;
  2377. end;
  2378. function TJNGFileFormat.SaveData(Handle: TImagingHandle;
  2379. const Images: TDynImageDataArray; Index: LongInt): Boolean;
  2380. var
  2381. NGFileSaver: TNGFileSaver;
  2382. ImageToSave: TImageData;
  2383. MustBeFreed: Boolean;
  2384. begin
  2385. // Make image JNG compatible, store it in saver, and save it to file
  2386. Result := MakeCompatible(Images[Index], ImageToSave, MustBeFreed);
  2387. if Result then
  2388. begin
  2389. NGFileSaver := TNGFileSaver.Create(Self);
  2390. with NGFileSaver do
  2391. try
  2392. FileType := ngJNG;
  2393. SetFileOptions;
  2394. AddFrame(ImageToSave, True);
  2395. SaveFile(Handle);
  2396. finally
  2397. // Free NG saver and compatible image
  2398. NGFileSaver.Free;
  2399. if MustBeFreed then
  2400. FreeImage(ImageToSave);
  2401. end;
  2402. end;
  2403. end;
  2404. {$ENDIF}
  2405. initialization
  2406. RegisterImageFileFormat(TPNGFileFormat);
  2407. {$IFNDEF DONT_LINK_MNG}
  2408. RegisterImageFileFormat(TMNGFileFormat);
  2409. {$ENDIF}
  2410. {$IFNDEF DONT_LINK_JNG}
  2411. RegisterImageFileFormat(TJNGFileFormat);
  2412. {$ENDIF}
  2413. finalization
  2414. {
  2415. File Notes:
  2416. -- TODOS ----------------------------------------------------
  2417. - nothing now
  2418. -- 0.77 Changes/Bug Fixes -----------------------------------
  2419. - Reads and writes APNG animation loop count metadata.
  2420. - Writes frame delays of APNG from metadata.
  2421. - Fixed color keys in 8bit depth PNG/MNG loading.
  2422. - Fixed needless (and sometimes buggy) conversion to format with alpha
  2423. channel in FPC (GetMem(0) <> nil!).
  2424. - Added support for optional ZLib compression strategy.
  2425. - Added loading and saving of ifBinary (1bit black and white)
  2426. format images. During loading grayscale 1bpp and indexed 1bpp
  2427. (with only black and white colors in palette) are treated as ifBinary.
  2428. ifBinary are saved as 1bpp grayscale PNGs.
  2429. -- 0.26.5 Changes/Bug Fixes ---------------------------------
  2430. - Reads frame delays from APNG files into metadata.
  2431. - Added loading and saving of metadata from these chunks: pHYs.
  2432. - Simplified decoding of 1/2/4 bit images a bit (less code).
  2433. -- 0.26.3 Changes/Bug Fixes ---------------------------------
  2434. - Added APNG saving support.
  2435. - Added APNG support to NG loader and animating to PNG loader.
  2436. -- 0.26.1 Changes/Bug Fixes ---------------------------------
  2437. - Changed file format conditional compilation to reflect changes
  2438. in LINK symbols.
  2439. -- 0.24.3 Changes/Bug Fixes ---------------------------------
  2440. - Changes for better thread safety.
  2441. -- 0.23 Changes/Bug Fixes -----------------------------------
  2442. - Added loading of global palettes and transparencies in MNG files
  2443. (and by doing so fixed crash when loading images with global PLTE or tRNS).
  2444. -- 0.21 Changes/Bug Fixes -----------------------------------
  2445. - Small changes in converting to supported formats.
  2446. - MakeCompatible method moved to base class, put ConvertToSupported here.
  2447. GetSupportedFormats removed, it is now set in constructor.
  2448. - Made public properties for options registered to SetOption/GetOption
  2449. functions.
  2450. - Changed extensions to filename masks.
  2451. - Changed SaveData, LoadData, and MakeCompatible methods according
  2452. to changes in base class in Imaging unit.
  2453. -- 0.17 Changes/Bug Fixes -----------------------------------
  2454. - MNG and JNG support added, PNG support redesigned to support NG file handlers
  2455. - added classes for working with NG file formats
  2456. - stuff from old ImagingPng unit added and that unit was deleted
  2457. - unit created and initial stuff added
  2458. -- 0.15 Changes/Bug Fixes -----------------------------------
  2459. - when saving indexed images save alpha to tRNS?
  2460. - added some defines and ifdefs to dzlib unit to allow choosing
  2461. impaszlib, fpc's paszlib, zlibex or other zlib implementation
  2462. - added colorkeying support
  2463. - fixed 16bit channel image handling - pixels were not swapped
  2464. - fixed arithmetic overflow (in paeth filter) in FPC
  2465. - data of unknown chunks are skipped and not needlesly loaded
  2466. -- 0.13 Changes/Bug Fixes -----------------------------------
  2467. - adaptive filtering added to PNG saving
  2468. - TPNGFileFormat class added
  2469. }
  2470. end.