Imaging.pas 98 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916
  1. {
  2. $Id: Imaging.pas,v 1.29 2006/10/26 13:29:28 galfar Exp $
  3. Vampyre Imaging Library
  4. by Marek Mauder ([email protected])
  5. http://imaginglib.sourceforge.net
  6. The contents of this file are used with permission, subject to the Mozilla
  7. Public License Version 1.1 (the "License"); you may not use this file except
  8. in compliance with the License. You may obtain a copy of the License at
  9. http://www.mozilla.org/MPL/MPL-1.1.html
  10. Software distributed under the License is distributed on an "AS IS" basis,
  11. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  12. the specific language governing rights and limitations under the License.
  13. Alternatively, the contents of this file may be used under the terms of the
  14. GNU Lesser General Public License (the "LGPL License"), in which case the
  15. provisions of the LGPL License are applicable instead of those above.
  16. If you wish to allow use of your version of this file only under the terms
  17. of the LGPL License and not to allow others to use your version of this file
  18. under the MPL, indicate your decision by deleting the provisions above and
  19. replace them with the notice and other provisions required by the LGPL
  20. License. If you do not delete the provisions above, a recipient may use
  21. your version of this file under either the MPL or the LGPL License.
  22. For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
  23. }
  24. { This unit is heart of Imaging library. It contains basic functions for
  25. manipulating image data as well as various image file format support.}
  26. unit Imaging;
  27. {$I ImagingOptions.inc}
  28. interface
  29. uses
  30. ImagingTypes, SysUtils, Classes;
  31. type
  32. { Default Imaging excepton class.}
  33. EImagingError = class(Exception);
  34. { Dynamic array of TImageData records.}
  35. TDynImageDataArray = array of TImageData;
  36. { ------------------------------------------------------------------------
  37. Low Level Interface Functions
  38. ------------------------------------------------------------------------}
  39. { General Functions }
  40. { Initializes image (all is set to zeroes). Call this for each image
  41. before using it (before calling every other function) to be sure there
  42. are no random-filled bytes (which would cause errors later).}
  43. procedure InitImage(var Image: TImageData);
  44. { Creates empty image of given dimensions and format. Image is filled with
  45. transparent black color (A=0, R=0, G=0, B=0).}
  46. function NewImage(Width, Height: LongInt; Format: TImageFormat;
  47. var Image: TImageData): Boolean;
  48. { Returns True if given TImageData record is valid.}
  49. function TestImage(const Image: TImageData): Boolean;
  50. { Frees given image data. Ater this call image is in the same state
  51. as after calling InitImage.}
  52. function FreeImage(var Image: TImageData): Boolean;
  53. { Call FreeImage() on all images in given dynamic
  54. array.}
  55. function FreeImagesInArray(var Images: TDynImageDataArray): Boolean;
  56. { Returns True if all TImageData records in given array are valid.}
  57. function TestImagesInArray(const Images: TDynImageDataArray): Boolean;
  58. { Checks given file for every supported image file format and if
  59. the file is in one of them returns its string identifier
  60. (which can be used in LoadFromStream/LoadFromMem type functions).
  61. If file is not in any of the supported formats empty string is returned.}
  62. function DetermineFileFormat(const FileName: string): string;
  63. { Checks given stream for every supported image file format and if
  64. the stream is in one of them returns its string identifier
  65. (which can be used in LoadFromStream/LoadFromMem type functions).
  66. If stream is not in any of the supported formats empty string is returned.}
  67. function DetermineStreamFormat(Stream: TStream): string;
  68. { Checks given memory for every supported image file format and if
  69. the memory is in one of them returns its string identifier
  70. (which can be used in LoadFromStream/LoadFromMem type functions).
  71. If memory is not in any of the supported formats empty string is returned.}
  72. function DetermineMemoryFormat(Data: Pointer; Size: LongInt): string;
  73. { Checks that an apropriate file format is supported purely from inspecting
  74. the given file name's extension (not contents of the file itself).
  75. The file need not exist.}
  76. function IsFileFormatSupported(const FileName: string): Boolean;
  77. { Loading Functions }
  78. { Loads single image from given file.}
  79. function LoadImageFromFile(const FileName: string; var Image: TImageData): Boolean;
  80. { Loads single image from given stream. If function fails stream position
  81. is not changed.}
  82. function LoadImageFromStream(Stream: TStream; var Image: TImageData): Boolean;
  83. { Loads single image from given memory location.}
  84. function LoadImageFromMemory(Data: Pointer; Size: LongInt; var Image: TImageData): Boolean;
  85. { Loads multiple images from given file.}
  86. function LoadMultiImageFromFile(const FileName: string;
  87. var Images: TDynImageDataArray): Boolean;
  88. { Loads multiple images from given stream. If function fails stream position
  89. is not changed.}
  90. function LoadMultiImageFromStream(Stream: TStream;
  91. var Images: TDynImageDataArray): Boolean;
  92. { Loads multiple images from given memory location.}
  93. function LoadMultiImageFromMemory(Data: Pointer; Size: LongInt;
  94. var Images: TDynImageDataArray): Boolean;
  95. { Saving Functions }
  96. { Saves single image to given file.}
  97. function SaveImageToFile(const FileName: string; const Image: TImageData): Boolean;
  98. { Saves single image to given stream. If function fails stream position
  99. is not changed. Ext identifies desired image file format (jpg, png, dds, ...).}
  100. function SaveImageToStream(const Ext: string; Stream: TStream;
  101. const Image: TImageData): Boolean;
  102. { Saves single image to given memory location. Memory must be allocated and its
  103. size is passed in Size parameter in which number of written bytes is returned.
  104. Ext identifies desired image file format (jpg, png, dds, ...).}
  105. function SaveImageToMemory(const Ext: string; Data: Pointer; var Size: LongInt;
  106. const Image: TImageData): Boolean;
  107. { Saves multiple images to given file. If format supports
  108. only single level images and there are multiple images to be saved,
  109. they are saved as sequence of files img000.jpg, img001.jpg ....).}
  110. function SaveMultiImageToFile(const FileName: string;
  111. const Images: TDynImageDataArray): Boolean;
  112. { Saves multiple images to given stream. If format supports
  113. only single level images and there are multiple images to be saved,
  114. they are saved one after another to the stream. If function fails stream
  115. position is not changed. Ext identifies desired image file format (jpg, png, dds, ...).}
  116. function SaveMultiImageToStream(const Ext: string; Stream: TStream;
  117. const Images: TDynImageDataArray): Boolean;
  118. { Saves multiple images to given memory location. If format supports
  119. only single level images and there are multiple images to be saved,
  120. they are saved one after another to the memory. Memory must be allocated and
  121. its size is passed in Size parameter in which number of written bytes is returned.
  122. Ext identifies desired image file format (jpg, png, dds, ...).}
  123. function SaveMultiImageToMemory(const Ext: string; Data: Pointer;
  124. var Size: LongInt; const Images: TDynImageDataArray): Boolean;
  125. { Manipulation Functions }
  126. { Creates identical copy of image data. Clone should be initialized
  127. by InitImage or it should be vaild image which will be freed by CloneImage.}
  128. function CloneImage(const Image: TImageData; var Clone: TImageData): Boolean;
  129. { Converts image to the given format.}
  130. function ConvertImage(var Image: TImageData; DestFormat: TImageFormat): Boolean;
  131. { Flips given image. Reverses the image along its horizontal axis — the top
  132. becomes the bottom and vice versa.}
  133. function FlipImage(var Image: TImageData): Boolean;
  134. { Mirrors given image. Reverses the image along its vertical axis — the left
  135. side becomes the right and vice versa.}
  136. function MirrorImage(var Image: TImageData): Boolean;
  137. { Resizes given image to new dimensions. Nearest, bilinear, or bicubic filtering
  138. can be used.}
  139. function ResizeImage(var Image: TImageData; NewWidth, NewHeight: LongInt;
  140. Filter: TResizeFilter): Boolean;
  141. { Swaps SrcChannel and DstChannel color or alpha channels of image.
  142. Use ChannelRed, ChannelBlue, ChannelGreen, ChannelAlpha constants to
  143. identify channels.}
  144. function SwapChannels(var Image: TImageData; SrcChannel, DstChannel: LongInt): Boolean;
  145. { Reduces the number of colors of the Image. Currently MaxColors must be in
  146. range <1, 4096>. Color reduction works also for alpha channel. Note that for
  147. large images and big number of colors it can be very slow.
  148. Output format of the image is the same as input format.}
  149. function ReduceColors(var Image: TImageData; MaxColors: LongInt): Boolean;
  150. { Generates mipmaps for image. Levels is the number of desired mipmaps levels
  151. with zero meaning all possible levels.}
  152. function GenerateMipMaps(const Image: TImageData; Levels: LongInt;
  153. var MipMaps: TDynImageDataArray): Boolean;
  154. { Maps image to existing palette producing image in ifIndex8 format.
  155. Pal must be allocated to at least Entries * SizeOf(TColor32Rec) bytes.}
  156. function MapImageToPalette(var Image: TImageData; Pal: PPalette32;
  157. Entries: LongInt): Boolean;
  158. { Splits image into XChunks x YChunks subimages. Default size of each chunk is
  159. ChunkWidth x ChunkHeight. If PreserveSize si True chunks at the edges of
  160. the image are also ChunkWidth x ChunkHeight sized and empty space is filled
  161. with Fill pixels. After calling this function XChunks contains number of
  162. chunks along x axis and YChunks along y axis. To access chunk [X, Y] use this
  163. index: Chunks[Y * XChunks + X].}
  164. function SplitImage(var Image: TImageData; var Chunks: TDynImageDataArray;
  165. ChunkWidth, ChunkHeight: LongInt; var XChunks, YChunks: LongInt;
  166. PreserveSize: Boolean; Fill: Pointer): Boolean;
  167. { Creates palette with MaxColors based on the colors of images in Images array.
  168. Use it when you want to convert several images to indexed format using
  169. single palette for all of them. If ConvertImages is True images in array
  170. are converted to indexed format using resulting palette. if it is False
  171. images are left intact and only resulting palatte is returned in Pal.
  172. Pal must be allocated to at least MaxColors * SizeOf(TColor32Rec) bytes.}
  173. function MakePaletteForImages(var Images: TDynImageDataArray; Pal: PPalette32;
  174. MaxColors: LongInt; ConvertImages: Boolean): Boolean;
  175. { Rotates image by 90, 180, 270, -90, -180, or -270 degrees counterclockwise.}
  176. function RotateImage(var Image: TImageData; Angle: LongInt): Boolean;
  177. { Drawing/Pixel functions }
  178. { Copies rectangular part of SrcImage to DstImage. No blending is performed -
  179. alpha is simply copied to destination image. Operates also with
  180. negative X and Y coordinates.
  181. Note that copying is fastest for images in the same data format
  182. (and slowest for images in special formats).}
  183. function CopyRect(const SrcImage: TImageData; SrcX, SrcY, Width, Height: LongInt;
  184. var DstImage: TImageData; DstX, DstY: LongInt): Boolean;
  185. { Fills given rectangle of image with given pixel fill data. Fill should point
  186. to the pixel in the same format as the given image is in.}
  187. function FillRect(var Image: TImageData; X, Y, Width, Height: LongInt; FillColor: Pointer): Boolean;
  188. { Replaces pixels with OldPixel in the given rectangle by NewPixel.
  189. OldPixel and NewPixel should point to the pixels in the same format
  190. as the given image is in.}
  191. function ReplaceColor(var Image: TImageData; X, Y, Width, Height: LongInt;
  192. OldColor, NewColor: Pointer): Boolean;
  193. { Stretches the contents of the source rectangle to the destination rectangle
  194. with optional resampling. No blending is performed - alpha is
  195. simply copied/resampled to destination image. Note that stretching is
  196. fastest for images in the same data format (and slowest for
  197. images in special formats).}
  198. function StretchRect(const SrcImage: TImageData; SrcX, SrcY, SrcWidth,
  199. SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth,
  200. DstHeight: LongInt; Filter: TResizeFilter): Boolean;
  201. { Copies pixel of Image at [X, Y] to memory pointed at by Pixel. Doesn't
  202. work with special formats.}
  203. procedure GetPixelDirect(const Image: TImageData; X, Y: LongInt; Pixel: Pointer);
  204. { Copies pixel from memory pointed at by Pixel to Image at position [X, Y].
  205. Doesn't work with special formats.}
  206. procedure SetPixelDirect(const Image: TImageData; X, Y: LongInt; Pixel: Pointer);
  207. { Function for getting pixel colors. Native pixel is read from Image and
  208. then translated to 32 bit ARGB. Works for all image formats (except special)
  209. so it is not very fast.}
  210. function GetPixel32(const Image: TImageData; X, Y: LongInt): TColor32Rec;
  211. { Procedure for setting pixel colors. Input 32 bit ARGB color is translated to
  212. native format and then written to Image. Works for all image formats (except special)
  213. so it is not very fast.}
  214. procedure SetPixel32(const Image: TImageData; X, Y: LongInt; const Color: TColor32Rec);
  215. { Function for getting pixel colors. Native pixel is read from Image and
  216. then translated to FP ARGB. Works for all image formats (except special)
  217. so it is not very fast.}
  218. function GetPixelFP(const Image: TImageData; X, Y: LongInt): TColorFPRec;
  219. { Procedure for setting pixel colors. Input FP ARGB color is translated to
  220. native format and then written to Image. Works for all image formats (except special)
  221. so it is not very fast.}
  222. procedure SetPixelFP(const Image: TImageData; X, Y: LongInt; const Color: TColorFPRec);
  223. { Palette Functions }
  224. { Allocates new palette with Entries ARGB color entries.}
  225. function NewPalette(Entries: LongInt; var Pal: PPalette32): Boolean;
  226. { Frees given palette.}
  227. function FreePalette(var Pal: PPalette32): Boolean;
  228. { Copies Count palette entries from SrcPal starting at index SrcIdx to
  229. DstPal at index DstPal.}
  230. function CopyPalette(SrcPal, DstPal: PPalette32; SrcIdx, DstIdx, Count: LongInt): Boolean;
  231. { Returns index of color in palette or index of nearest color if exact match
  232. is not found. Pal must be allocated to at least Entries * SizeOf(TColor32Rec) bytes.}
  233. function FindColor(Pal: PPalette32; Entries: LongInt; Color: TColor32): LongInt;
  234. { Creates grayscale palette where each color channel has the same value.
  235. Pal must be allocated to at least Entries * SizeOf(TColor32Rec) bytes.}
  236. function FillGrayscalePalette(Pal: PPalette32; Entries: LongInt): Boolean;
  237. { Creates palette with given bitcount for each channel.
  238. 2^(RBits + GBits + BBits) should be equl to Entries. Examples:
  239. (3, 3, 2) will create palette with all possible colors of R3G3B2 format
  240. and (8, 0, 0) will create palette with 256 shades of red.
  241. Pal must be allocated to at least Entries * SizeOf(TColor32Rec) bytes.}
  242. function FillCustomPalette(Pal: PPalette32; Entries: LongInt; RBits, GBits,
  243. BBits: Byte; Alpha: Byte = $FF): Boolean;
  244. { Swaps SrcChannel and DstChannel color or alpha channels of palette.
  245. Use ChannelRed, ChannelBlue, ChannelGreen, ChannelAlpha constants to
  246. identify channels. Pal must be allocated to at least
  247. Entries * SizeOf(TColor32Rec) bytes.}
  248. function SwapChannelsOfPalette(Pal: PPalette32; Entries, SrcChannel,
  249. DstChannel: LongInt): Boolean;
  250. { Options Functions }
  251. { Sets value of integer option specified by OptionId parameter.
  252. Option Ids are constans starting ImagingXXX.}
  253. function SetOption(OptionId, Value: LongInt): Boolean;
  254. { Returns value of integer option specified by OptionId parameter. If OptionId is
  255. invalid, InvalidOption is returned. Option Ids are constans
  256. starting ImagingXXX.}
  257. function GetOption(OptionId: LongInt): LongInt;
  258. { Pushes current values of all options on the stack. Returns True
  259. if successfull (max stack depth is 8 now). }
  260. function PushOptions: Boolean;
  261. { Pops back values of all options from the top of the stack. Returns True
  262. if successfull (max stack depth is 8 now). }
  263. function PopOptions: Boolean;
  264. { Image Format Functions }
  265. { Returns short information about given image format.}
  266. function GetImageFormatInfo(Format: TImageFormat; var Info: TImageFormatInfo): Boolean;
  267. { Returns size in bytes of Width x Height area of pixels. Works for all formats.}
  268. function GetPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt;
  269. { IO Functions }
  270. { User can set his own file IO functions used when loading from/saving to
  271. files by this function.}
  272. procedure SetUserFileIO(OpenReadProc: TOpenReadProc; OpenWriteProc:
  273. TOpenWriteProc; CloseProc: TCloseProc; EofProc: TEofProc; SeekProc:
  274. TSeekProc; TellProc: TTellProc; ReadProc: TReadProc; WriteProc: TWriteProc);
  275. { Sets file IO functions to Imaging default.}
  276. procedure ResetFileIO;
  277. { ------------------------------------------------------------------------
  278. Other Imaging Stuff
  279. ------------------------------------------------------------------------}
  280. type
  281. { Set of TImageFormat enum.}
  282. TImageFormats = set of TImageFormat;
  283. { Record containg set of IO functions internaly used by image loaders/savers.}
  284. TIOFunctions = record
  285. OpenRead: TOpenReadProc;
  286. OpenWrite: TOpenWriteProc;
  287. Close: TCloseProc;
  288. Eof: TEofProc;
  289. Seek: TSeekProc;
  290. Tell: TTellProc;
  291. Read: TReadProc;
  292. Write: TWriteProc;
  293. end;
  294. { Base class for various image file format loaders/savers which
  295. descend from this class.}
  296. TImageFileFormat = class(TObject)
  297. protected
  298. FExtensions: TStringList;
  299. FName: string;
  300. FCanLoad: Boolean;
  301. FCanSave: Boolean;
  302. FIsMultiImageFormat: Boolean;
  303. procedure AddExtensions(const AExtensions: string);
  304. function GetFormatInfo(Format: TImageFormat): PImageFormatInfo;
  305. function GetSupportedFormats: TImageFormats; virtual;
  306. { Method which must be overrided in descendants if they' are be capable
  307. of loading images.}
  308. procedure LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray;
  309. OnlyFirstFrame: Boolean); virtual;
  310. { Method which must be overrided in descendants if they are be capable
  311. of saving images. If Index is MaxInt all images in array are saved, otherwise
  312. only image at index is saved.}
  313. procedure SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray;
  314. Index: LongInt = MaxInt); virtual;
  315. { Creates clone of image compatible with format's saving function.
  316. It can convert image to another format, swap channels of image, ...
  317. Note that if input Image is already compatible it is returned in Comp,
  318. so before freeing Comp you must test if it is not original Image.
  319. Returns True if Comp is returned in compatible format}
  320. function MakeCompatible(const Image: TImageData; var Comp: TImageData): Boolean; virtual;
  321. public
  322. constructor Create; virtual;
  323. destructor Destroy; override;
  324. { Loads images from file source.}
  325. function LoadFromFile(const FileName: string; var Images: TDynImageDataArray;
  326. OnlyFirstLevel: Boolean = False): Boolean;
  327. { Loads images from stream source.}
  328. function LoadFromStream(Stream: TStream; var Images: TDynImageDataArray;
  329. OnlyFirstLevel: Boolean = False): Boolean;
  330. { Loads images from memory source.}
  331. function LoadFromMemory(Data: Pointer; Size: LongInt;
  332. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean = False): Boolean;
  333. { Saves images to file. If format supports only single level images and
  334. there are multiple images to be saved, they are saved as sequence of
  335. independent images (for example SaveToFile saves sequence of
  336. files img000.jpg, img001.jpg ....).}
  337. function SaveToFile(const FileName: string; const Images: TDynImageDataArray;
  338. OnlyFirstLevel: Boolean = False): Boolean;
  339. { Saves images to stream. If format supports only single level images and
  340. there are multiple images to be saved, they are saved as sequence of
  341. independent images.}
  342. function SaveToStream(Stream: TStream; const Images: TDynImageDataArray;
  343. OnlyFirstLevel: Boolean = False): Boolean;
  344. { Saves images to memory. If format supports only single level images and
  345. there are multiple images to be saved, they are saved as sequence of
  346. independent images. Data must be already allocated and their size passed
  347. as Size parameter, number of written bytes is then returned in the same
  348. parameter.}
  349. function SaveToMemory(Data: Pointer; var Size: LongInt;
  350. const Images: TDynImageDataArray; OnlyFirstLevel: Boolean = False): Boolean;
  351. { Returns True if data located in source identified by Handle
  352. represent valid image in current format.}
  353. function TestFormat(Handle: TImagingHandle): Boolean; virtual;
  354. { List of file extensions for this format}
  355. property Extensions: TStringList read FExtensions;
  356. { Description of this format}
  357. property Name: string read FName;
  358. { Indicates whether images in this format can be loaded}
  359. property CanLoad: Boolean read FCanLoad;
  360. { Indicates whether images in this format can be saved}
  361. property CanSave: Boolean read FCanSave;
  362. { Indicates whether images in this format can contain multiple image levels}
  363. property IsMultiImageFormat: Boolean read FIsMultiImageFormat;
  364. { Set of TImageFormats supported by saving/loading functions of this format}
  365. property SupportedFormats: TImageFormats read GetSupportedFormats;
  366. end;
  367. { Class reference for TImageFileFormat class}
  368. TImageFileFormatClass = class of TImageFileFormat;
  369. { Returns symbolic name of given format.}
  370. function GetFormatName(Format: TImageFormat): string;
  371. { Returns string with information about given Image.}
  372. function ImageToStr(const Image: TImageData): string;
  373. { Returns Imaging version string in format 'Major.Minor.Patch'.}
  374. function GetVersionStr: string;
  375. { If Condition is True then TruePart is retured, otherwise FalsePart is returned.}
  376. function IffFormat(Condition: Boolean; const TruePart, FalsePart: TImageFormat): TImageFormat;
  377. { Registers new image loader/saver so it can be used by LoadFrom/SaveTo
  378. functions.}
  379. procedure RegisterImageFileFormat(AClass: TImageFileFormatClass);
  380. { Registers new option so it can be used by Srt/GetOption functions.}
  381. procedure RegisterOption(OptionId: LongInt; Variable: PLongInt);
  382. { Returns image format loader/saver asociated with given file
  383. extension or nil if not found.}
  384. function FindImageFileFormat(const Ext: string): TImageFileFormat; overload;
  385. { Returns image format loader/saver based on its class
  386. or nil if not found or not registered.}
  387. function FindImageFileFormat(AClass: TImageFileFormatClass): TImageFileFormat; overload;
  388. { Returns filter string for usage with open and save picture dialogs
  389. which contains all registered image file formats.
  390. If AllFilter is True filter for all known graphic files
  391. (like All(*.jpg;*.png;....) is added too at the first index
  392. (good for open image dialogs).}
  393. function GetImageFileFormatsFilter(AllFilter: Boolean): string;
  394. { Returns file extension (without dot) of image format selected
  395. by given filter index. Used filter string is defined by GetImageFileFormatsFilter
  396. function. This function can be used with save dialogs (with filters created
  397. by GetImageFileFormatsFilter) to get the extension of file format selected
  398. in dialog quickly. Index is in range 1..N (as FilterIndex property
  399. of TOpenDialog/TSaveDialog)}
  400. function GetFilterIndexExtension(Index: LongInt; AllFilter: Boolean): string;
  401. { Returns filter index of image file format specified by Ext. Used filter
  402. string is defined by GetImageFileFormatsFilter function.
  403. Returned index is in range 1..N (as FilterIndex property of TOpenDialog/TSaveDialog)}
  404. function GetExtensionFilterIndex(const Ext: string; AllFilter: Boolean): LongInt;
  405. { Returns current IO functions.}
  406. function GetIO: TIOFunctions;
  407. { Raises EImagingError with given message.}
  408. procedure RaiseImaging(const Msg: string; const Args: array of const);
  409. implementation
  410. uses
  411. {$IFDEF LINK_BITMAP}
  412. ImagingBitmap,
  413. {$ENDIF}
  414. {$IFDEF LINK_JPEG}
  415. ImagingJpeg,
  416. {$ENDIF}
  417. {$IF Defined(LINK_PNG) or Defined(LINK_MNG) or Defined(LINK_JNG)}
  418. ImagingNetworkGraphics,
  419. {$IFEND}
  420. {$IFDEF LINK_DDS}
  421. ImagingDds,
  422. {$ENDIF}
  423. {$IFDEF LINK_TARGA}
  424. ImagingTarga,
  425. {$ENDIF}
  426. ImagingFormats, ImagingUtility, ImagingIO;
  427. resourcestring
  428. SExceptMsg = 'Exception Message';
  429. SAllFilter = 'All Images';
  430. SUnknownFormat = 'Unknown and unsupported format';
  431. SErrorFreeImage = 'Error while freeing image. %s';
  432. SErrorCloneImage = 'Error while cloning image. %s';
  433. SErrorFlipImage = 'Error while flipping image. %s';
  434. SErrorMirrorImage = 'Error while mirroring image. %s';
  435. SErrorResizeImage = 'Error while resizing image. %s';
  436. SErrorSwapImage = 'Error while swapping channels of image. %s';
  437. SFileFormatCanNotLoad = 'Image Format "%s" does not support loading images.';
  438. SFileFormatCanNotSave = 'Image Format "%s" does not support saving images.';
  439. SErrorNewImage = 'Error while creating image data with params: Width=%d ' +
  440. 'Height=%d Format=%s.';
  441. SErrorConvertImage = 'Error while converting image to format "%s". %s';
  442. SImageInfo = 'Image @%p info: Width = %dpx, Height = %dpx, ' +
  443. 'Format = %s, Size = %.0nKiB, Bits @%p, Palette @%p.';
  444. SImageInfoInvalid = 'Access violation encountered when getting info on ' +
  445. 'image at address %p.';
  446. SFileNotValid = 'File "%s" is not valid image in "%s" format.';
  447. SStreamNotValid = 'Stream %p does not contain valid image in "%s" format.';
  448. SMemoryNotValid = 'Memory %p (%d Bytes) does not contain valid image ' +
  449. 'in "%s" format.';
  450. SErrorLoadingFile = 'Error while loading images from file "%s".';
  451. SErrorLoadingStream = 'Error while loading images from stream %p.';
  452. SErrorLoadingMemory = 'Error while loading images from memory %p (%d Bytes).';
  453. SErrorSavingFile = 'Error while saving images to file "%s".';
  454. SErrorSavingStream = 'Error while saving images to stream %p.';
  455. SErrorSavingMemory = 'Error while saving images to memory %p (%d Bytes).';
  456. SErrorFindColor = 'Error while finding color in palette @%p with %d entries.';
  457. SErrorGrayscalePalette = 'Error while filling grayscale palette @%p with %d entries.';
  458. SErrorCustomPalette = 'Error while filling custom palette @%p with %d entries.';
  459. SErrorSwapPalette = 'Error while swapping channels of palette @%p with %d entries.';
  460. SErrorReduceColors = 'Error while reducing number of colors of image to %d. %s';
  461. SErrorGenerateMipMaps = 'Error while generating %d mipmap levels for image %s';
  462. SImagesNotValid = 'One or more images are not valid.';
  463. SErrorCopyRect = 'Error while copying rect from image %s to image %s.';
  464. SErrorMapImage = 'Error while mapping image %s to palette.';
  465. SErrorFillRect = 'Error while filling rectangle X:%d Y:%d W:%d H:%d in image %s';
  466. SErrorSplitImage = 'Error while splitting image %s to %dx%d sized chunks.';
  467. SErrorMakePaletteForImages = 'Error while making %d color palette for %d images.';
  468. SErrorNewPalette = 'Error while creating new palette with %d entries';
  469. SErrorFreePalette = 'Error while freeing palette @%p';
  470. SErrorCopyPalette = 'Error while copying %d entries from palette @%p to @%p';
  471. SErrorReplaceColor = 'Error while replacing colors in rectangle X:%d Y:%d W:%d H:%d of image %s';
  472. SErrorRotateImage = 'Error while rotating image %s by %d degrees';
  473. SErrorStretchRect = 'Error while stretching rect from image %s to image %s.';
  474. const
  475. // initial size of array with options information
  476. InitialOptions = 256;
  477. // max depth of the option stack
  478. OptionStackDepth = 8;
  479. // do not change the default format now, its too late
  480. DefaultImageFormat: TImageFormat = ifA8R8G8B8;
  481. type
  482. TOptionArray = array of PLongInt;
  483. TOptionValueArray = array of LongInt;
  484. TOptionStack = class(TObject)
  485. private
  486. FStack: array[0..OptionStackDepth - 1] of TOptionValueArray;
  487. FPosition: LongInt;
  488. public
  489. constructor Create;
  490. destructor Destroy; override;
  491. function Push: Boolean;
  492. function Pop: Boolean;
  493. end;
  494. var
  495. // currently set IO functions
  496. IO: TIOFunctions;
  497. // list with all registered TImageFileFormat classes
  498. ImageFileFormats: TList = nil;
  499. // array with registered options (pointers to their values)
  500. Options: TOptionArray = nil;
  501. // array containing addional infomation about every image format
  502. ImageFormatInfos: TImageFormatInfoArray;
  503. // stack used by PushOptions/PopOtions functions
  504. OptionStack: TOptionStack = nil;
  505. var
  506. // variable for ImagingColorReduction option
  507. ColorReductionMask: LongInt = $FF;
  508. // variable for ImagingLoadOverrideFormat option
  509. LoadOverrideFormat: TImageFormat = ifUnknown;
  510. // variable for ImagingSaveOverrideFormat option
  511. SaveOverrideFormat: TImageFormat = ifUnknown;
  512. // variable for ImagingSaveOverrideFormat option
  513. MipMapFilter: TSamplingFilter = sfLinear;
  514. { Internal unit functions }
  515. { Modifies option value to be in the allowed range. Works only
  516. for options registered in this unit.}
  517. function CheckOptionValue(OptionId, Value: LongInt): LongInt; forward;
  518. { Sets IO functions to file IO.}
  519. procedure SetFileIO; forward;
  520. { Sets IO functions to stream IO.}
  521. procedure SetStreamIO; forward;
  522. { Sets IO functions to memory IO.}
  523. procedure SetMemoryIO; forward;
  524. { Inits image format infos array.}
  525. procedure InitImageFormats; forward;
  526. { Freew image format infos array.}
  527. procedure FreeImageFileFormats; forward;
  528. { Creates options array and stack.}
  529. procedure InitOptions; forward;
  530. { Frees options array and stack.}
  531. procedure FreeOptions; forward;
  532. {$IFDEF USE_INLINE}
  533. { Those inline functions are copied here from ImagingFormats
  534. because Delphi 9 cannot inline them if they are declared in
  535. circularly dependent units.}
  536. procedure CopyPixel(Src, Dest: Pointer; BytesPerPixel: LongInt); inline;
  537. begin
  538. case BytesPerPixel of
  539. 1: PByte(Dest)^ := PByte(Src)^;
  540. 2: PWord(Dest)^ := PWord(Src)^;
  541. 3: PColor24Rec(Dest)^ := PColor24Rec(Src)^;
  542. 4: PLongWord(Dest)^ := PLongWord(Src)^;
  543. 6: PColor48Rec(Dest)^ := PColor48Rec(Src)^;
  544. 8: PInt64(Dest)^ := PInt64(Src)^;
  545. 16: PColorFPRec(Dest)^ := PColorFPRec(Src)^;
  546. end;
  547. end;
  548. function ComparePixels(PixelA, PixelB: Pointer; BytesPerPixel: LongInt): Boolean; inline;
  549. begin
  550. case BytesPerPixel of
  551. 1: Result := PByte(PixelA)^ = PByte(PixelB)^;
  552. 2: Result := PWord(PixelA)^ = PWord(PixelB)^;
  553. 3: Result := (PWord(PixelA)^ = PWord(PixelB)^) and
  554. (PColor24Rec(PixelA).R = PColor24Rec(PixelB).R);
  555. 4: Result := PLongWord(PixelA)^ = PLongWord(PixelB)^;
  556. 6: Result := (PLongWord(PixelA)^ = PLongWord(PixelB)^) and
  557. (PColor48Rec(PixelA).R = PColor48Rec(PixelB).R);
  558. 8: Result := PInt64(PixelA)^ = PInt64(PixelB)^;
  559. 16: Result := (PFloatHelper(PixelA).Data2 = PFloatHelper(PixelB).Data2) and
  560. (PFloatHelper(PixelA).Data1 = PFloatHelper(PixelB).Data1);
  561. else
  562. Result := False;
  563. end;
  564. end;
  565. {$ENDIF}
  566. { ------------------------------------------------------------------------
  567. Low Level Interface Functions
  568. ------------------------------------------------------------------------}
  569. { General Functions }
  570. procedure InitImage(var Image: TImageData);
  571. begin
  572. FillChar(Image, SizeOf(Image), 0);
  573. end;
  574. function NewImage(Width, Height: LongInt; Format: TImageFormat; var Image:
  575. TImageData): Boolean;
  576. var
  577. FInfo: PImageFormatInfo;
  578. begin
  579. Result := False;
  580. if FreeImage(Image) and (Width >= 0) and (Height >= 0) then
  581. try
  582. Image.Width := Width;
  583. Image.Height := Height;
  584. // if desired format is not valid then default format is selected
  585. if (ImageFormatInfos[Format] = nil) or (Format = ifDefault) then
  586. Image.Format := DefaultImageFormat
  587. else
  588. Image.Format := Format;
  589. FInfo := ImageFormatInfos[Image.Format];
  590. Image.Size := FInfo.GetPixelsSize(FInfo.Format, Image.Width, Image.Height);
  591. if FInfo.IsSpecial then
  592. FInfo.CheckDimensions(FInfo.Format, Image.Width, Image.Height);
  593. // image bits are allocated and set to zeroes
  594. GetMem(Image.Bits, Image.Size);
  595. FillChar(Image.Bits^, Image.Size, 0);
  596. // palette is allocated and set to zeroes
  597. GetMem(Image.Palette, FInfo.PaletteEntries * SizeOf(TColor32Rec));
  598. FillChar(Image.Palette^, FInfo.PaletteEntries * SizeOf(TColor32Rec), 0);
  599. Result := TestImage(Image);
  600. except
  601. InitImage(Image);
  602. Result := False;
  603. RaiseImaging(SErrorNewImage, [Width, Height, GetFormatName(Format)]);
  604. end;
  605. end;
  606. function TestImage(const Image: TImageData): Boolean;
  607. begin
  608. Result := (LongInt(Image.Format) >= LongInt(Low(TImageFormat))) and
  609. (LongInt(Image.Format) <= LongInt(High(TImageFormat))) and
  610. (ImageFormatInfos[Image.Format] <> nil) and
  611. (Assigned(ImageFormatInfos[Image.Format].GetPixelsSize) and
  612. (ImageFormatInfos[Image.Format].GetPixelsSize(Image.Format,
  613. Image.Width, Image.Height) = Image.Size));
  614. end;
  615. function FreeImage(var Image: TImageData): Boolean;
  616. begin
  617. try
  618. if TestImage(Image) then
  619. begin
  620. FreeMemNil(Image.Bits);
  621. FreeMemNil(Image.Palette);
  622. end;
  623. InitImage(Image);
  624. Result := True;
  625. except
  626. Result := False;
  627. RaiseImaging(SErrorFreeImage, [ImageToStr(Image)]);
  628. end;
  629. end;
  630. function FreeImagesInArray(var Images: TDynImageDataArray): Boolean;
  631. var
  632. I: LongInt;
  633. begin
  634. Result := True;
  635. for I := 0 to Length(Images) - 1 do
  636. Result := Result and FreeImage(Images[I]);
  637. end;
  638. function TestImagesInArray(const Images: TDynImageDataArray): Boolean;
  639. var
  640. I: LongInt;
  641. begin
  642. Result := True;
  643. for I := 0 to Length(Images) - 1 do
  644. begin
  645. Result := Result and TestImage(Images[I]);
  646. if not Result then
  647. Break;
  648. end;
  649. end;
  650. function DetermineFileFormat(const FileName: string): string;
  651. var
  652. I: LongInt;
  653. Fmt: TImageFileFormat;
  654. Handle: TImagingHandle;
  655. begin
  656. Result := '';
  657. SetFileIO;
  658. try
  659. Handle := IO.OpenRead(PChar(FileName));
  660. try
  661. for I := 0 to ImageFileFormats.Count - 1 do
  662. begin
  663. Fmt := TImageFileFormat(ImageFileFormats[I]);
  664. if Fmt.TestFormat(Handle) then
  665. begin
  666. Result := Fmt.Extensions[0];
  667. Exit;
  668. end;
  669. end;
  670. finally
  671. IO.Close(Handle);
  672. end;
  673. except
  674. end;
  675. end;
  676. function DetermineStreamFormat(Stream: TStream): string;
  677. var
  678. I: LongInt;
  679. Fmt: TImageFileFormat;
  680. Handle: TImagingHandle;
  681. begin
  682. Result := '';
  683. SetStreamIO;
  684. try
  685. Handle := IO.OpenRead(Pointer(Stream));
  686. for I := 0 to ImageFileFormats.Count - 1 do
  687. begin
  688. Fmt := TImageFileFormat(ImageFileFormats[I]);
  689. if Fmt.TestFormat(Handle) then
  690. begin
  691. Result := Fmt.Extensions[0];
  692. Exit;
  693. end;
  694. end;
  695. IO.Close(Handle);
  696. except
  697. end;
  698. end;
  699. function DetermineMemoryFormat(Data: Pointer; Size: LongInt): string;
  700. var
  701. I: LongInt;
  702. Fmt: TImageFileFormat;
  703. Handle: TImagingHandle;
  704. IORec: TMemoryIORec;
  705. begin
  706. Result := '';
  707. SetMemoryIO;
  708. IORec.Data := Data;
  709. IORec.Position := 0;
  710. IORec.Size := Size;
  711. try
  712. Handle := IO.OpenRead(@IORec);
  713. for I := 0 to ImageFileFormats.Count - 1 do
  714. begin
  715. Fmt := TImageFileFormat(ImageFileFormats[I]);
  716. if Fmt.TestFormat(Handle) then
  717. begin
  718. Result := Fmt.Extensions[0];
  719. Exit;
  720. end;
  721. end;
  722. IO.Close(Handle);
  723. except
  724. end;
  725. end;
  726. function IsFileFormatSupported(const FileName: string): Boolean;
  727. begin
  728. Result := FindImageFileFormat(GetFileExt(FileName)) <> nil;
  729. end;
  730. { Loading Functions }
  731. function LoadImageFromFile(const FileName: string; var Image: TImageData):
  732. Boolean;
  733. var
  734. Format: TImageFileFormat;
  735. IArray: TDynImageDataArray;
  736. I: LongInt;
  737. begin
  738. Result := False;
  739. Format := FindImageFileFormat(DetermineFileFormat(FileName));
  740. if Format <> nil then
  741. begin
  742. FreeImage(Image);
  743. Result := Format.LoadFromFile(FileName, IArray, True);
  744. if Result and (Length(IArray) > 0) then
  745. begin
  746. Image := IArray[0];
  747. for I := 1 to Length(IArray) - 1 do
  748. FreeImage(IArray[I]);
  749. end
  750. else
  751. Result := False;
  752. end;
  753. end;
  754. function LoadImageFromStream(Stream: TStream; var Image: TImageData): Boolean;
  755. var
  756. Format: TImageFileFormat;
  757. IArray: TDynImageDataArray;
  758. I: LongInt;
  759. begin
  760. Result := False;
  761. Format := FindImageFileFormat(DetermineStreamFormat(Stream));
  762. if Format <> nil then
  763. begin
  764. FreeImage(Image);
  765. Result := Format.LoadFromStream(Stream, IArray, True);
  766. if Result and (Length(IArray) > 0) then
  767. begin
  768. Image := IArray[0];
  769. for I := 1 to Length(IArray) - 1 do
  770. FreeImage(IArray[I]);
  771. end
  772. else
  773. Result := False;
  774. end;
  775. end;
  776. function LoadImageFromMemory(Data: Pointer; Size: LongInt; var Image: TImageData): Boolean;
  777. var
  778. Format: TImageFileFormat;
  779. IArray: TDynImageDataArray;
  780. I: LongInt;
  781. begin
  782. Result := False;
  783. Format := FindImageFileFormat(DetermineMemoryFormat(Data, Size));
  784. if Format <> nil then
  785. begin
  786. FreeImage(Image);
  787. Result := Format.LoadFromMemory(Data, Size, IArray, True);
  788. if Result and (Length(IArray) > 0) then
  789. begin
  790. Image := IArray[0];
  791. for I := 1 to Length(IArray) - 1 do
  792. FreeImage(IArray[I]);
  793. end
  794. else
  795. Result := False;
  796. end;
  797. end;
  798. function LoadMultiImageFromFile(const FileName: string; var Images:
  799. TDynImageDataArray): Boolean;
  800. var
  801. Format: TImageFileFormat;
  802. begin
  803. Result := False;
  804. Format := FindImageFileFormat(DetermineFileFormat(FileName));
  805. if Format <> nil then
  806. begin
  807. FreeImagesInArray(Images);
  808. Result := Format.LoadFromFile(FileName, Images);
  809. end;
  810. end;
  811. function LoadMultiImageFromStream(Stream: TStream; var Images: TDynImageDataArray): Boolean;
  812. var
  813. Format: TImageFileFormat;
  814. begin
  815. Result := False;
  816. Format := FindImageFileFormat(DetermineStreamFormat(Stream));
  817. if Format <> nil then
  818. begin
  819. FreeImagesInArray(Images);
  820. Result := Format.LoadFromStream(Stream, Images);
  821. end;
  822. end;
  823. function LoadMultiImageFromMemory(Data: Pointer; Size: LongInt;
  824. var Images: TDynImageDataArray): Boolean;
  825. var
  826. Format: TImageFileFormat;
  827. begin
  828. Result := False;
  829. Format := FindImageFileFormat(DetermineMemoryFormat(Data, Size));
  830. if Format <> nil then
  831. begin
  832. FreeImagesInArray(Images);
  833. Result := Format.LoadFromMemory(Data, Size, Images);
  834. end;
  835. end;
  836. { Saving Functions }
  837. function SaveImageToFile(const FileName: string; const Image: TImageData): Boolean;
  838. var
  839. Ext: string;
  840. Format: TImageFileFormat;
  841. IArray: TDynImageDataArray;
  842. begin
  843. Result := False;
  844. Ext := GetFileExt(FileName);
  845. Format := FindImageFileFormat(Ext);
  846. if Format <> nil then
  847. begin
  848. SetLength(IArray, 1);
  849. IArray[0] := Image;
  850. Result := Format.SaveToFile(FileName, IArray, True);
  851. end;
  852. end;
  853. function SaveImageToStream(const Ext: string; Stream: TStream;
  854. const Image: TImageData): Boolean;
  855. var
  856. Format: TImageFileFormat;
  857. IArray: TDynImageDataArray;
  858. begin
  859. Result := False;
  860. Format := FindImageFileFormat(Ext);
  861. if Format <> nil then
  862. begin
  863. SetLength(IArray, 1);
  864. IArray[0] := Image;
  865. Result := Format.SaveToStream(Stream, IArray, True);
  866. end;
  867. end;
  868. function SaveImageToMemory(const Ext: string; Data: Pointer; var Size: LongInt;
  869. const Image: TImageData): Boolean;
  870. var
  871. Format: TImageFileFormat;
  872. IArray: TDynImageDataArray;
  873. begin
  874. Result := False;
  875. Format := FindImageFileFormat(Ext);
  876. if Format <> nil then
  877. begin
  878. SetLength(IArray, 1);
  879. IArray[0] := Image;
  880. Result := Format.SaveToMemory(Data, Size, IArray, True);
  881. end;
  882. end;
  883. function SaveMultiImageToFile(const FileName: string;
  884. const Images: TDynImageDataArray): Boolean;
  885. var
  886. Ext: string;
  887. Format: TImageFileFormat;
  888. begin
  889. Result := False;
  890. Ext := GetFileExt(FileName);
  891. Format := FindImageFileFormat(Ext);
  892. if Format <> nil then
  893. Result := Format.SaveToFile(FileName, Images);
  894. end;
  895. function SaveMultiImageToStream(const Ext: string; Stream: TStream;
  896. const Images: TDynImageDataArray): Boolean;
  897. var
  898. Format: TImageFileFormat;
  899. begin
  900. Result := False;
  901. Format := FindImageFileFormat(Ext);
  902. if Format <> nil then
  903. Result := Format.SaveToStream(Stream, Images);
  904. end;
  905. function SaveMultiImageToMemory(const Ext: string; Data: Pointer;
  906. var Size: LongInt; const Images: TDynImageDataArray): Boolean;
  907. var
  908. Format: TImageFileFormat;
  909. begin
  910. Result := False;
  911. Format := FindImageFileFormat(Ext);
  912. if Format <> nil then
  913. Result := Format.SaveToMemory(Data, Size, Images);
  914. end;
  915. { Manipulation Functions }
  916. function CloneImage(const Image: TImageData; var Clone: TImageData): Boolean;
  917. var
  918. Info: PImageFormatInfo;
  919. begin
  920. Result := False;
  921. if TestImage(Image) then
  922. with Image do
  923. try
  924. if TestImage(Clone) and (Image.Bits <> Clone.Bits) then
  925. FreeImage(Clone)
  926. else
  927. InitImage(Clone);
  928. Info := ImageFormatInfos[Format];
  929. Clone.Width := Width;
  930. Clone.Height := Height;
  931. Clone.Format := Format;
  932. Clone.Size := Size;
  933. if Info.PaletteEntries > 0 then
  934. begin
  935. GetMem(Clone.Palette, Info.PaletteEntries * SizeOf(TColor32Rec));
  936. Move(Palette^, Clone.Palette^, Info.PaletteEntries *
  937. SizeOf(TColor32Rec));
  938. end;
  939. GetMem(Clone.Bits, Clone.Size);
  940. Move(Bits^, Clone.Bits^, Clone.Size);
  941. Result := True;
  942. except
  943. RaiseImaging(SErrorCloneImage, [ImageToStr(Image)]);
  944. end;
  945. end;
  946. function ConvertImage(var Image: TImageData; DestFormat: TImageFormat): Boolean;
  947. var
  948. NewData: Pointer;
  949. NewPal: PPalette32;
  950. NewSize, NumPixels: LongInt;
  951. SrcInfo, DstInfo: PImageFormatInfo;
  952. begin
  953. Result := False;
  954. if TestImage(Image) then
  955. with Image do
  956. try
  957. // If default format is set as dest or dest is not defined
  958. // we use DefaultImageFormat
  959. if DestFormat in [ifDefault, ifUnknown] then
  960. DestFormat := DefaultImageFormat;
  961. SrcInfo := ImageFormatInfos[Format];
  962. DstInfo := ImageFormatInfos[DestFormat];
  963. if SrcInfo = DstInfo then
  964. begin
  965. // There is nothing to convert - src is alredy in dest format
  966. Result := True;
  967. Exit;
  968. end;
  969. // Exit Src or Dest format is invalid
  970. if (SrcInfo = nil) or (DstInfo = nil) then Exit;
  971. // If dest format is just src with swapped channels we call
  972. // SwapChannels instead
  973. if (SrcInfo.RBSwapFormat = DestFormat) and
  974. (DstInfo.RBSwapFormat = SrcInfo.Format) then
  975. begin
  976. Result := SwapChannels(Image, ChannelRed, ChannelBlue);
  977. Exit;
  978. end;
  979. if (not SrcInfo.IsSpecial) and (not DstInfo.IsSpecial) then
  980. begin
  981. NumPixels := Width * Height;
  982. NewSize := NumPixels * DstInfo.BytesPerPixel;
  983. GetMem(NewData, NewSize);
  984. GetMem(NewPal, DstInfo.PaletteEntries * SizeOf(TColor32Rec));
  985. // Source: indexed format
  986. if SrcInfo.IsIndexed then
  987. begin
  988. if DstInfo.IsIndexed then
  989. IndexToIndex(NumPixels, Bits, NewData, SrcInfo, DstInfo, Palette,
  990. NewPal)
  991. else
  992. if DstInfo.HasGrayChannel then
  993. IndexToGray(NumPixels, Bits, NewData, SrcInfo, DstInfo, Palette)
  994. else
  995. if DstInfo.IsFloatingPoint then
  996. IndexToFloat(NumPixels, Bits, NewData, SrcInfo, DstInfo, Palette)
  997. else
  998. IndexToChannel(NumPixels, Bits, NewData, SrcInfo, DstInfo,
  999. Palette)
  1000. end
  1001. else
  1002. // Source: grayscale format
  1003. if SrcInfo.HasGrayChannel then
  1004. begin
  1005. if DstInfo.IsIndexed then
  1006. GrayToIndex(NumPixels, Bits, NewData, SrcInfo, DstInfo, NewPal)
  1007. else
  1008. if DstInfo.HasGrayChannel then
  1009. GrayToGray(NumPixels, Bits, NewData, SrcInfo, DstInfo)
  1010. else
  1011. if DstInfo.IsFloatingPoint then
  1012. GrayToFloat(NumPixels, Bits, NewData, SrcInfo, DstInfo)
  1013. else
  1014. GrayToChannel(NumPixels, Bits, NewData, SrcInfo, DstInfo);
  1015. end
  1016. else
  1017. // Source: floating point format
  1018. if SrcInfo.IsFloatingPoint then
  1019. begin
  1020. if DstInfo.IsIndexed then
  1021. FloatToIndex(NumPixels, Bits, NewData, SrcInfo, DstInfo, NewPal)
  1022. else
  1023. if DstInfo.HasGrayChannel then
  1024. FloatToGray(NumPixels, Bits, NewData, SrcInfo, DstInfo)
  1025. else
  1026. if DstInfo.IsFloatingPoint then
  1027. FloatToFloat(NumPixels, Bits, NewData, SrcInfo, DstInfo)
  1028. else
  1029. FloatToChannel(NumPixels, Bits, NewData, SrcInfo, DstInfo);
  1030. end
  1031. else
  1032. // Source: standard multi channel image
  1033. begin
  1034. if DstInfo.IsIndexed then
  1035. ChannelToIndex(NumPixels, Bits, NewData, SrcInfo, DstInfo, NewPal)
  1036. else
  1037. if DstInfo.HasGrayChannel then
  1038. ChannelToGray(NumPixels, Bits, NewData, SrcInfo, DstInfo)
  1039. else
  1040. if DstInfo.IsFloatingPoint then
  1041. ChannelToFloat(NumPixels, Bits, NewData, SrcInfo, DstInfo)
  1042. else
  1043. ChannelToChannel(NumPixels, Bits, NewData, SrcInfo, DstInfo);
  1044. end;
  1045. FreeMemNil(Bits);
  1046. FreeMemNil(Palette);
  1047. Format := DestFormat;
  1048. Bits := NewData;
  1049. Size := NewSize;
  1050. Palette := NewPal;
  1051. end
  1052. else
  1053. ConvertSpecial(Image, SrcInfo, DstInfo);
  1054. Result := True;
  1055. except
  1056. RaiseImaging(SErrorConvertImage, [GetFormatName(DestFormat),
  1057. ImageToStr(Image)]);
  1058. end;
  1059. end;
  1060. function FlipImage(var Image: TImageData): Boolean;
  1061. var
  1062. P1, P2, Buff: Pointer;
  1063. WidthBytes, I: LongInt;
  1064. OldFmt: TImageFormat;
  1065. begin
  1066. Result := False;
  1067. OldFmt := Image.Format;
  1068. if TestImage(Image) then
  1069. with Image do
  1070. try
  1071. if ImageFormatInfos[OldFmt].IsSpecial then
  1072. ConvertImage(Image, ifDefault);
  1073. WidthBytes := Width * ImageFormatInfos[Format].BytesPerPixel;
  1074. GetMem(Buff, WidthBytes);
  1075. try
  1076. // swap all scanlines of image
  1077. for I := 0 to Height div 2 - 1 do
  1078. begin
  1079. P1 := @PByteArray(Bits)[I * WidthBytes];
  1080. P2 := @PByteArray(Bits)[(Height - I - 1) * WidthBytes];
  1081. Move(P1^, Buff^, WidthBytes);
  1082. Move(P2^, P1^, WidthBytes);
  1083. Move(Buff^, P2^, WidthBytes);
  1084. end;
  1085. Result := True;
  1086. finally
  1087. FreeMemNil(Buff);
  1088. end;
  1089. if OldFmt <> Format then
  1090. ConvertImage(Image, OldFmt);
  1091. except
  1092. RaiseImaging(SErrorFlipImage, [ImageToStr(Image)]);
  1093. end;
  1094. end;
  1095. function MirrorImage(var Image: TImageData): Boolean;
  1096. var
  1097. Scanline: PByte;
  1098. Buff: TColorFPRec;
  1099. Bpp, Y, X, WidthDiv2, WidthBytes, XLeft, XRight: LongInt;
  1100. OldFmt: TImageFormat;
  1101. begin
  1102. Result := False;
  1103. OldFmt := Image.Format;
  1104. if TestImage(Image) then
  1105. with Image do
  1106. try
  1107. if ImageFormatInfos[OldFmt].IsSpecial then
  1108. ConvertImage(Image, ifDefault);
  1109. Bpp := ImageFormatInfos[Format].BytesPerPixel;
  1110. WidthDiv2 := Width div 2;
  1111. WidthBytes := Width * Bpp;
  1112. // mirror all pixels on each scanline of image
  1113. for Y := 0 to Height - 1 do
  1114. begin
  1115. Scanline := @PByteArray(Bits)[Y * WidthBytes];
  1116. XLeft := 0;
  1117. XRight := (Width - 1) * Bpp;
  1118. for X := 0 to WidthDiv2 - 1 do
  1119. begin
  1120. CopyPixel(@PByteArray(Scanline)[XLeft], @Buff, Bpp);
  1121. CopyPixel(@PByteArray(Scanline)[XRight],
  1122. @PByteArray(Scanline)[XLeft], Bpp);
  1123. CopyPixel(@Buff, @PByteArray(Scanline)[XRight], Bpp);
  1124. Inc(XLeft, Bpp);
  1125. Dec(XRight, Bpp);
  1126. end;
  1127. end;
  1128. Result := True;
  1129. if OldFmt <> Format then
  1130. ConvertImage(Image, OldFmt);
  1131. except
  1132. RaiseImaging(SErrorMirrorImage, [ImageToStr(Image)]);
  1133. end;
  1134. end;
  1135. function ResizeImage(var Image: TImageData; NewWidth, NewHeight: LongInt;
  1136. Filter: TResizeFilter): Boolean;
  1137. var
  1138. WorkImage: TImageData;
  1139. begin
  1140. Result := False;
  1141. if TestImage(Image) and (NewWidth > 0) and (NewHeight > 0) and
  1142. ((Image.Width <> NewWidth) or (Image.Height <> NewHeight)) then
  1143. with Image do
  1144. try
  1145. InitImage(WorkImage);
  1146. // Create new image with desired dimensions
  1147. NewImage(NewWidth, NewHeight, Image.Format, WorkImage);
  1148. // Stretch pixels from old image to new one
  1149. StretchRect(Image, 0, 0, Image.Width, Image.Height,
  1150. WorkImage, 0, 0, WorkImage.Width, WorkImage.Height, Filter);
  1151. // Free old image and assign new image to it
  1152. FreeMemNil(Image.Bits);
  1153. if Image.Palette <> nil then
  1154. WorkImage.Palette := Image.Palette;
  1155. Image := WorkImage;
  1156. Result := True;
  1157. except
  1158. RaiseImaging(SErrorResizeImage, [ImageToStr(Image)]);
  1159. end;
  1160. end;
  1161. function SwapChannels(var Image: TImageData; SrcChannel, DstChannel: LongInt): Boolean;
  1162. var
  1163. I, NumPixels: LongInt;
  1164. Info: PImageFormatInfo;
  1165. Swap, Alpha: Word;
  1166. Data: PByte;
  1167. Pix64: TColor64Rec;
  1168. PixF: TColorFPRec;
  1169. SwapF: Single;
  1170. begin
  1171. Result := False;
  1172. if TestImage(Image) then
  1173. with Image do
  1174. try
  1175. NumPixels := Width * Height;
  1176. Info := ImageFormatInfos[Format];
  1177. Data := Bits;
  1178. // first swap channels of most common formats
  1179. if (Info.Format = ifA8R8G8B8) or ((Info.Format = ifR8G8B8) and
  1180. (SrcChannel <> ChannelAlpha) and (DstChannel <> ChannelAlpha)) then
  1181. for I := 0 to NumPixels - 1 do
  1182. with PColor24Rec(Data)^ do
  1183. begin
  1184. Swap := Channels[SrcChannel];
  1185. Channels[SrcChannel] := Channels[DstChannel];
  1186. Channels[DstChannel] := Swap;
  1187. Inc(Data, Info.BytesPerPixel);
  1188. end
  1189. else
  1190. // swap palette channels of indexed images
  1191. if Info.IsIndexed then
  1192. begin
  1193. SwapChannelsOfPalette(Palette, Info.PaletteEntries, SrcChannel,
  1194. DstChannel)
  1195. end
  1196. else
  1197. // swap channels of floating point images
  1198. if Info.IsFloatingPoint then
  1199. begin
  1200. for I := 0 to NumPixels - 1 do
  1201. begin
  1202. FloatGetSrcPixel(Data, Info, PixF);
  1203. with PixF do
  1204. begin
  1205. SwapF := Channels[SrcChannel];
  1206. Channels[SrcChannel] := Channels[DstChannel];
  1207. Channels[DstChannel] := SwapF;
  1208. end;
  1209. FloatSetDstPixel(Data, Info, PixF);
  1210. Inc(Data, Info.BytesPerPixel);
  1211. end;
  1212. end
  1213. else
  1214. // swap channels of special format images
  1215. if Info.IsSpecial then
  1216. begin
  1217. ConvertImage(Image, ifDefault);
  1218. SwapChannels(Image, SrcChannel, DstChannel);
  1219. ConvertImage(Image, Info.Format);
  1220. end
  1221. else
  1222. if Info.HasGrayChannel and Info.HasAlphaChannel and
  1223. ((SrcChannel = ChannelAlpha) or (DstChannel = ChannelAlpha)) then
  1224. begin
  1225. for I := 0 to NumPixels - 1 do
  1226. begin
  1227. // if we have grayscale image with alpha and alpha is channel
  1228. // to be swapped, we swap it
  1229. GrayGetSrcPixel(Data, Info, Pix64, Alpha);
  1230. Swap := Alpha;
  1231. Alpha := Pix64.A;
  1232. Pix64.A := Swap;
  1233. GraySetDstPixel(Data, Info, Pix64, Alpha);
  1234. Inc(Data, Info.BytesPerPixel);
  1235. end;
  1236. end
  1237. else
  1238. // then do general swap on other channel image formats
  1239. for I := 0 to NumPixels - 1 do
  1240. begin
  1241. ChannelGetSrcPixel(Data, Info, Pix64);
  1242. with Pix64 do
  1243. begin
  1244. Swap := Channels[SrcChannel];
  1245. Channels[SrcChannel] := Channels[DstChannel];
  1246. Channels[DstChannel] := Swap;
  1247. end;
  1248. ChannelSetDstPixel(Data, Info, Pix64);
  1249. Inc(Data, Info.BytesPerPixel);
  1250. end;
  1251. Result := True;
  1252. except
  1253. RaiseImaging(SErrorSwapImage, [ImageToStr(Image)]);
  1254. end;
  1255. end;
  1256. function ReduceColors(var Image: TImageData; MaxColors: LongInt): Boolean;
  1257. var
  1258. TmpInfo: TImageFormatInfo;
  1259. Data, Index: PWord;
  1260. I, NumPixels: LongInt;
  1261. Pal: PPalette32;
  1262. Col:PColor32Rec;
  1263. OldFmt: TImageFormat;
  1264. begin
  1265. Result := False;
  1266. if TestImage(Image) then
  1267. with Image do
  1268. try
  1269. // first create temp image info and allocate output bits and palette
  1270. MaxColors := Iff(MaxColors > $FFFF, $FFFF, MaxColors);
  1271. OldFmt := Format;
  1272. FillChar(TmpInfo, SizeOf(TmpInfo), 0);
  1273. TmpInfo.PaletteEntries := MaxColors;
  1274. TmpInfo.BytesPerPixel := 2;
  1275. NumPixels := Width * Height;
  1276. GetMem(Data, NumPixels * TmpInfo.BytesPerPixel);
  1277. GetMem(Pal, MaxColors * SizeOf(TColor32Rec));
  1278. ConvertImage(Image, ifA8R8G8B8);
  1279. // we use median cut algorithm to create reduced palette and to
  1280. // fill Data with indices to this palette
  1281. ReduceColorsMedianCut(NumPixels, Bits, PByte(Data),
  1282. ImageFormatInfos[Format], @TmpInfo, MaxColors, ColorReductionMask, Pal);
  1283. Col := Bits;
  1284. Index := Data;
  1285. // then we write reduced colors to the input image
  1286. for I := 0 to NumPixels - 1 do
  1287. begin
  1288. Col.Color := Pal[Index^].Color;
  1289. Inc(Col);
  1290. Inc(Index);
  1291. end;
  1292. FreeMemNil(Data);
  1293. FreeMemNil(Pal);
  1294. // and convert it to its original format
  1295. ConvertImage(Image, OldFmt);
  1296. Result := True;
  1297. except
  1298. RaiseImaging(SErrorReduceColors, [MaxColors, ImageToStr(Image)]);
  1299. end;
  1300. end;
  1301. function GenerateMipMaps(const Image: TImageData; Levels: LongInt;
  1302. var MipMaps: TDynImageDataArray): Boolean;
  1303. var
  1304. Width, Height, I, Count: LongInt;
  1305. begin
  1306. Result := False;
  1307. if TestImage(Image) then
  1308. try
  1309. Width := Image.Width;
  1310. Height := Image.Height;
  1311. // We compute number of possible mipmap levels and if
  1312. // the given levels are invalid or zero we use this value
  1313. Count := GetNumMipMapLevels(Width, Height);
  1314. if (Levels <= 0) or (Levels > Count) then
  1315. Levels := Count;
  1316. FreeImagesInArray(MipMaps);
  1317. SetLength(MipMaps, Levels);
  1318. CloneImage(Image, MipMaps[0]);
  1319. for I := 1 to Levels - 1 do
  1320. begin
  1321. Width := Width shr 1;
  1322. Height := Height shr 1;
  1323. if Width < 1 then Width := 1;
  1324. if Height < 1 then Height := 1;
  1325. FillMipMapLevel(MipMaps[I - 1], Width, Height, MipMaps[I]);
  1326. end;
  1327. Result := True;
  1328. except
  1329. RaiseImaging(SErrorGenerateMipMaps, [Levels, ImageToStr(Image)]);
  1330. end;
  1331. end;
  1332. function MapImageToPalette(var Image: TImageData; Pal: PPalette32;
  1333. Entries: LongInt): Boolean;
  1334. function FindNearestColor(Pal: PPalette32; Entries: LongInt; Col: TColor32Rec): LongInt;
  1335. var
  1336. I, MinDif, Dif: LongInt;
  1337. begin
  1338. Result := 0;
  1339. MinDif := 1020;
  1340. for I := 0 to Entries - 1 do
  1341. with Pal[I] do
  1342. begin
  1343. Dif := Abs(R - Col.R);
  1344. if Dif > MinDif then Continue;
  1345. Dif := Dif + Abs(G - Col.G);
  1346. if Dif > MinDif then Continue;
  1347. Dif := Dif + Abs(B - Col.B);
  1348. if Dif > MinDif then Continue;
  1349. Dif := Dif + Abs(A - Col.A);
  1350. if Dif < MinDif then
  1351. begin
  1352. MinDif := Dif;
  1353. Result := I;
  1354. end;
  1355. end;
  1356. end;
  1357. var
  1358. I, MaxEntries: LongInt;
  1359. PIndex: PByte;
  1360. PColor: PColor32Rec;
  1361. CloneARGB: TImageData;
  1362. Info: PImageFormatInfo;
  1363. begin
  1364. Result := False;
  1365. if TestImage(Image) and (Entries <= 256) then
  1366. try
  1367. // we create clone of source image in A8R8G8B8 and
  1368. // then recreate source image in ifIndex8 format
  1369. // with palette taken from Pal parameter
  1370. InitImage(CloneARGB);
  1371. CloneImage(Image, CloneARGB);
  1372. ConvertImage(CloneARGB, ifA8R8G8B8);
  1373. FreeImage(Image);
  1374. NewImage(CloneARGB.Width, CloneARGB.Height, ifIndex8, Image);
  1375. Info := ImageFormatInfos[Image.Format];
  1376. MaxEntries := Min(Info.PaletteEntries, Entries);
  1377. Move(Pal^, Image.Palette^, MaxEntries * SizeOf(TColor32Rec));
  1378. PIndex := Image.Bits;
  1379. PColor := CloneARGB.Bits;
  1380. // for every pixel of ARGB clone we find closest color in
  1381. // given palette and assign its index to resulting image's pixel
  1382. // procedure used here is very slow but simple and memory usage friendly
  1383. // (contrary to other methods)
  1384. for I := 0 to Image.Width * Image.Height - 1 do
  1385. begin
  1386. PIndex^ := Byte(FindNearestColor(Image.Palette, MaxEntries, PColor^));
  1387. Inc(PIndex);
  1388. Inc(PColor);
  1389. end;
  1390. FreeImage(CloneARGB);
  1391. Result := True;
  1392. except
  1393. RaiseImaging(SErrorMapImage, [ImageToStr(Image)]);
  1394. end;
  1395. end;
  1396. function SplitImage(var Image: TImageData; var Chunks: TDynImageDataArray;
  1397. ChunkWidth, ChunkHeight: LongInt; var XChunks, YChunks: LongInt;
  1398. PreserveSize: Boolean; Fill: Pointer): Boolean;
  1399. var
  1400. X, Y, XTrunc, YTrunc: LongInt;
  1401. NotOnEdge: Boolean;
  1402. Info: PImageFormatInfo;
  1403. OldFmt: TImageFormat;
  1404. begin
  1405. OldFmt := Image.Format;
  1406. Result := False;
  1407. if TestImage(Image) then
  1408. try
  1409. Info := ImageFormatInfos[Image.Format];
  1410. if Info.IsSpecial then
  1411. ConvertImage(Image, ifDefault);
  1412. // we compute make sure that chunks are not larger than source image or negative
  1413. ChunkWidth := ClampInt(ChunkWidth, 0, Image.Width);
  1414. ChunkHeight := ClampInt(ChunkHeight, 0, Image.Height);
  1415. // number of chunks along X and Y axes is computed
  1416. XChunks := Trunc(Ceil(Image.Width / ChunkWidth));
  1417. YChunks := Trunc(Ceil(Image.Height / ChunkHeight));
  1418. FreeImagesInArray(Chunks);
  1419. SetLength(Chunks, XChunks * YChunks);
  1420. // for every chunk we create new image and copy a portion of
  1421. // the source image to it. If chunk is on the edge of the source image
  1422. // we fill enpty space with Fill pixel data if PreserveSize is set or
  1423. // make the chunk smaller if it is not set
  1424. for Y := 0 to YChunks - 1 do
  1425. for X := 0 to XChunks - 1 do
  1426. begin
  1427. NotOnEdge := ((X < XChunks - 1) and (Y < YChunks - 1)) or
  1428. ((Image.Width mod ChunkWidth = 0) and (Image.Height mod ChunkHeight = 0));
  1429. if PreserveSize or NotOnEdge then
  1430. begin
  1431. NewImage(ChunkWidth, ChunkHeight, Image.Format, Chunks[Y * XChunks + X]);
  1432. if (not NotOnEdge) and (Fill <> nil) then
  1433. FillRect(Chunks[Y * XChunks + X], 0, 0, ChunkWidth, ChunkHeight, Fill);
  1434. CopyRect(Image, X * ChunkWidth, Y * ChunkHeight, ChunkWidth, ChunkHeight,
  1435. Chunks[Y * XChunks + X], 0, 0);
  1436. end
  1437. else
  1438. begin
  1439. XTrunc := Image.Width - (Image.Width div ChunkWidth) * ChunkWidth;
  1440. YTrunc := Image.Height - (Image.Height div ChunkHeight) * ChunkHeight;
  1441. NewImage(XTrunc, YTrunc, Image.Format, Chunks[Y * XChunks + X]);
  1442. CopyRect(Image, X * ChunkWidth, Y * ChunkHeight, XTrunc, YTrunc,
  1443. Chunks[Y * XChunks + X], 0, 0);
  1444. end;
  1445. // if source image is in indexed format we copy its palette to chunk
  1446. if Info.IsIndexed then
  1447. begin
  1448. Move(Image.Palette^, Chunks[Y * XChunks + X].Palette^,
  1449. Info.PaletteEntries * SizeOf(TColor32Rec));
  1450. end;
  1451. end;
  1452. if OldFmt <> Image.Format then
  1453. begin
  1454. ConvertImage(Image, OldFmt);
  1455. for X := 0 to Length(Chunks) - 1 do
  1456. ConvertImage(Chunks[X], OldFmt);
  1457. end;
  1458. Result := True;
  1459. except
  1460. RaiseImaging(SErrorSplitImage, [ImageToStr(Image), ChunkWidth, ChunkHeight]);
  1461. end;
  1462. end;
  1463. function MakePaletteForImages(var Images: TDynImageDataArray; Pal: PPalette32;
  1464. MaxColors: LongInt; ConvertImages: Boolean): Boolean;
  1465. var
  1466. I: LongInt;
  1467. SrcInfo, DstInfo: PImageFormatInfo;
  1468. Target: TImageData;
  1469. DstFormat: TImageFormat;
  1470. begin
  1471. Result := False;
  1472. if TestImagesInArray(Images) then
  1473. try
  1474. // null the color histogram
  1475. ReduceColorsMedianCut(0, nil, nil, nil, nil, 0, 0, nil, [raCreateHistogram]);
  1476. for I := 0 to Length(Images) - 1 do
  1477. begin
  1478. SrcInfo := ImageFormatInfos[Images[I].Format];
  1479. // update histogram with colors of each input image
  1480. ReduceColorsMedianCut(Images[I].Width * Images[I].Height, Images[I].Bits,
  1481. nil, SrcInfo, nil, MaxColors, ColorReductionMask, nil, [raUpdateHistogram]);
  1482. end;
  1483. // construct reduced color map from the histogram
  1484. ReduceColorsMedianCut(0, nil, nil, nil, nil, MaxColors, ColorReductionMask,
  1485. Pal, [raMakeColorMap]);
  1486. if ConvertImages then
  1487. begin
  1488. DstFormat := ifIndex8;
  1489. DstInfo := ImageFormatInfos[DstFormat];
  1490. MaxColors := Min(DstInfo.PaletteEntries, MaxColors);
  1491. for I := 0 to Length(Images) - 1 do
  1492. begin
  1493. SrcInfo := ImageFormatInfos[Images[I].Format];
  1494. InitImage(Target);
  1495. NewImage(Images[I].Width, Images[I].Height, DstFormat, Target);
  1496. // we map each input image to reduced palette and replace
  1497. // image in array with mapped image
  1498. ReduceColorsMedianCut(Images[I].Width * Images[I].Height, Images[I].Bits,
  1499. Target.Bits, SrcInfo, DstInfo, MaxColors, 0, nil, [raMapImage]);
  1500. Move(Pal^, Target.Palette^, MaxColors * SizeOf(TColor32Rec));
  1501. FreeImage(Images[I]);
  1502. Images[I] := Target;
  1503. end;
  1504. end;
  1505. Result := True;
  1506. except
  1507. RaiseImaging(SErrorMakePaletteForImages, [MaxColors, Length(Images)]);
  1508. end;
  1509. end;
  1510. function RotateImage(var Image: TImageData; Angle: LongInt): Boolean;
  1511. var
  1512. X, Y, BytesPerPixel: LongInt;
  1513. RotImage: TImageData;
  1514. Pix, RotPix: PByte;
  1515. OldFmt: TImageFormat;
  1516. begin
  1517. Result := False;
  1518. if TestImage(Image) then
  1519. try
  1520. if (Angle < -360) or (Angle > 360) then Angle := Angle mod 360;
  1521. if (Angle = 0) or (Abs(Angle) = 360) then
  1522. begin
  1523. Result := True;
  1524. Exit;
  1525. end;
  1526. Angle := Iff(Angle = -90, 270, Angle);
  1527. Angle := Iff(Angle = -270, 90, Angle);
  1528. Angle := Iff(Angle = -180, 180, Angle);
  1529. OldFmt := Image.Format;
  1530. if ImageFormatInfos[Image.Format].IsSpecial then
  1531. ConvertImage(Image, ifDefault);
  1532. InitImage(RotImage);
  1533. BytesPerPixel := ImageFormatInfos[Image.Format].BytesPerPixel;
  1534. if ((Angle = 90) or (Angle = 270)) and (Image.Width <> Image.Height) then
  1535. NewImage(Image.Height, Image.Width, Image.Format, RotImage)
  1536. else
  1537. NewImage(Image.Width, Image.Height, Image.Format, RotImage);
  1538. RotPix := RotImage.Bits;
  1539. case Angle of
  1540. 90:
  1541. begin
  1542. for Y := 0 to RotImage.Height - 1 do
  1543. begin
  1544. Pix := @PByteArray(Image.Bits)[(Image.Width - Y - 1) * BytesPerPixel];
  1545. for X := 0 to RotImage.Width - 1 do
  1546. begin
  1547. CopyPixel(Pix, RotPix, BytesPerPixel);
  1548. Inc(RotPix, BytesPerPixel);
  1549. Inc(Pix, Image.Width * BytesPerPixel);
  1550. end;
  1551. end;
  1552. end;
  1553. 180:
  1554. begin
  1555. Pix := @PByteArray(Image.Bits)[((Image.Height - 1) * Image.Width +
  1556. (Image.Width - 1)) * BytesPerPixel];
  1557. for Y := 0 to RotImage.Height - 1 do
  1558. for X := 0 to RotImage.Width - 1 do
  1559. begin
  1560. CopyPixel(Pix, RotPix, BytesPerPixel);
  1561. Inc(RotPix, BytesPerPixel);
  1562. Dec(Pix, BytesPerPixel);
  1563. end;
  1564. end;
  1565. 270:
  1566. begin
  1567. for Y := 0 to RotImage.Height - 1 do
  1568. begin
  1569. Pix := @PByteArray(Image.Bits)[((Image.Height - 1) * Image.Width +
  1570. Y) * BytesPerPixel];
  1571. for X := 0 to RotImage.Width - 1 do
  1572. begin
  1573. CopyPixel(Pix, RotPix, BytesPerPixel);
  1574. Inc(RotPix, BytesPerPixel);
  1575. Dec(Pix, Image.Width * BytesPerPixel);
  1576. end;
  1577. end;
  1578. end;
  1579. end;
  1580. FreeMemNil(Image.Bits);
  1581. RotImage.Palette := Image.Palette;
  1582. Image := RotImage;
  1583. if OldFmt <> Image.Format then
  1584. ConvertImage(Image, OldFmt);
  1585. Result := True;
  1586. except
  1587. RaiseImaging(SErrorRotateImage, [ImageToStr(Image), Angle]);
  1588. end;
  1589. end;
  1590. { Drawing/Pixel functions }
  1591. function CopyRect(const SrcImage: TImageData; SrcX, SrcY, Width, Height: LongInt;
  1592. var DstImage: TImageData; DstX, DstY: LongInt): Boolean;
  1593. var
  1594. Info: PImageFormatInfo;
  1595. I, SrcWidthBytes, DstWidthBytes, MoveBytes: LongInt;
  1596. SrcPointer, DstPointer: PByte;
  1597. WorkImage: TImageData;
  1598. OldFormat: TImageFormat;
  1599. begin
  1600. Result := False;
  1601. OldFormat := ifUnknown;
  1602. if TestImage(SrcImage) and TestImage(DstImage) then
  1603. try
  1604. Info := ImageFormatInfos[DstImage.Format];
  1605. if Info.IsSpecial then
  1606. begin
  1607. // if dest image is in special format we convert it to default
  1608. OldFormat := Info.Format;
  1609. ConvertImage(DstImage, ifDefault);
  1610. Info := ImageFormatInfos[DstImage.Format];
  1611. end;
  1612. if SrcImage.Format <> DstImage.Format then
  1613. begin
  1614. // If images are in different format source is converted to dest's format
  1615. InitImage(WorkImage);
  1616. CloneImage(SrcImage, WorkImage);
  1617. ConvertImage(WorkImage, DstImage.Format);
  1618. end
  1619. else
  1620. WorkImage := SrcImage;
  1621. // make sure we are still copying image to image, not invalid pointer to protected memory
  1622. ClipCopyBounds(SrcX, SrcY, Width, Height, DstX, DstY, SrcImage.Width, SrcImage.Height,
  1623. Rect(0, 0, DstImage.Width, DstImage.Height));
  1624. if (Width > 0) and (Height > 0) then
  1625. begin
  1626. MoveBytes := Width * Info.BytesPerPixel;
  1627. DstWidthBytes := DstImage.Width * Info.BytesPerPixel;
  1628. DstPointer := @PByteArray(DstImage.Bits)[DstY * DstWidthBytes +
  1629. DstX * Info.BytesPerPixel];
  1630. SrcWidthBytes := WorkImage.Width * Info.BytesPerPixel;
  1631. SrcPointer := @PByteArray(WorkImage.Bits)[SrcY * SrcWidthBytes +
  1632. SrcX * Info.BytesPerPixel];
  1633. for I := 0 to Height - 1 do
  1634. begin
  1635. Move(SrcPointer^, DstPointer^, MoveBytes);
  1636. Inc(SrcPointer, SrcWidthBytes);
  1637. Inc(DstPointer, DstWidthBytes);
  1638. end;
  1639. // If dest image was in special format we convert it back
  1640. if OldFormat <> ifUnknown then
  1641. ConvertImage(DstImage, OldFormat);
  1642. // Working image must be freed if it is not the same as source image
  1643. if WorkImage.Bits <> SrcImage.Bits then
  1644. FreeImage(WorkImage);
  1645. end;
  1646. Result := True;
  1647. except
  1648. RaiseImaging(SErrorCopyRect, [ImageToStr(SrcImage), ImageToStr(DstImage)]);
  1649. end;
  1650. end;
  1651. function FillRect(var Image: TImageData; X, Y, Width, Height: LongInt;
  1652. FillColor: Pointer): Boolean;
  1653. var
  1654. Info: PImageFormatInfo;
  1655. I, J, ImageWidthBytes, RectWidthBytes, Bpp: Longint;
  1656. LinePointer, PixPointer: PByte;
  1657. OldFmt: TImageFormat;
  1658. begin
  1659. Result := False;
  1660. if TestImage(Image) then
  1661. try
  1662. ClipRectBounds(X, Y, Width, Height, Rect(0, 0, Image.Width, Image.Height));
  1663. if (Width > 0) and (Height > 0) then
  1664. begin
  1665. OldFmt := Image.Format;
  1666. if ImageFormatInfos[OldFmt].IsSpecial then
  1667. ConvertImage(Image, ifDefault);
  1668. Info := ImageFormatInfos[Image.Format];
  1669. Bpp := Info.BytesPerPixel;
  1670. ImageWidthBytes := Image.Width * Bpp;
  1671. RectWidthBytes := Width * Bpp;
  1672. LinePointer := @PByteArray(Image.Bits)[Y * ImageWidthBytes + X * Bpp];
  1673. for I := 0 to Height - 1 do
  1674. begin
  1675. case Bpp of
  1676. 1: FillMemory(LinePointer, RectWidthBytes, PByte(FillColor)^);
  1677. 2: FillMemoryWord(LinePointer, RectWidthBytes, PWord(FillColor)^);
  1678. 4: FillMemoryLongWord(LinePointer, RectWidthBytes, PLongWord(FillColor)^);
  1679. else
  1680. PixPointer := LinePointer;
  1681. for J := 0 to Width - 1 do
  1682. begin
  1683. CopyPixel(FillColor, PixPointer, Bpp);
  1684. Inc(PixPointer, Bpp);
  1685. end;
  1686. end;
  1687. Inc(LinePointer, ImageWidthBytes);
  1688. end;
  1689. if OldFmt <> Image.Format then
  1690. ConvertImage(Image, OldFmt);
  1691. end;
  1692. Result := True;
  1693. except
  1694. RaiseImaging(SErrorFillRect, [X, Y, Width, Height, ImageToStr(Image)]);
  1695. end;
  1696. end;
  1697. function ReplaceColor(var Image: TImageData; X, Y, Width, Height: LongInt;
  1698. OldColor, NewColor: Pointer): Boolean;
  1699. var
  1700. Info: PImageFormatInfo;
  1701. I, J, WidthBytes, Bpp: Longint;
  1702. LinePointer, PixPointer: PByte;
  1703. OldFmt: TImageFormat;
  1704. begin
  1705. Result := False;
  1706. if TestImage(Image) then
  1707. try
  1708. ClipRectBounds(X, Y, Width, Height, Rect(0, 0, Image.Width, Image.Height));
  1709. if (Width > 0) and (Height > 0) then
  1710. begin
  1711. OldFmt := Image.Format;
  1712. if ImageFormatInfos[OldFmt].IsSpecial then
  1713. ConvertImage(Image, ifDefault);
  1714. Info := ImageFormatInfos[Image.Format];
  1715. Bpp := Info.BytesPerPixel;
  1716. WidthBytes := Image.Width * Bpp;
  1717. LinePointer := @PByteArray(Image.Bits)[Y * WidthBytes + X * Bpp];
  1718. for I := 0 to Height - 1 do
  1719. begin
  1720. PixPointer := LinePointer;
  1721. for J := 0 to Width - 1 do
  1722. begin
  1723. if ComparePixels(PixPointer, OldColor, Bpp) then
  1724. CopyPixel(NewColor, PixPointer, Bpp);
  1725. Inc(PixPointer, Bpp);
  1726. end;
  1727. Inc(LinePointer, WidthBytes);
  1728. end;
  1729. if OldFmt <> Image.Format then
  1730. ConvertImage(Image, OldFmt);
  1731. end;
  1732. Result := True;
  1733. except
  1734. RaiseImaging(SErrorReplaceColor, [X, Y, Width, Height, ImageToStr(Image)]);
  1735. end;
  1736. end;
  1737. function StretchRect(const SrcImage: TImageData; SrcX, SrcY, SrcWidth,
  1738. SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth,
  1739. DstHeight: LongInt; Filter: TResizeFilter): Boolean;
  1740. var
  1741. Info: PImageFormatInfo;
  1742. WorkImage: TImageData;
  1743. OldFormat: TImageFormat;
  1744. begin
  1745. Result := False;
  1746. OldFormat := ifUnknown;
  1747. if (SrcWidth <> DstWidth) or (SrcHeight <> DstHeight) then
  1748. begin
  1749. // If source and dest rectangles don't have the same size we do stretch
  1750. if TestImage(SrcImage) and TestImage(DstImage) then
  1751. try
  1752. Info := ImageFormatInfos[DstImage.Format];
  1753. if Info.IsSpecial then
  1754. begin
  1755. // If dest image is in special format we convert it to default
  1756. OldFormat := Info.Format;
  1757. ConvertImage(DstImage, ifDefault);
  1758. Info := ImageFormatInfos[DstImage.Format];
  1759. end;
  1760. if SrcImage.Format <> DstImage.Format then
  1761. begin
  1762. // If images are in different format source is converted to dest's format
  1763. InitImage(WorkImage);
  1764. CloneImage(SrcImage, WorkImage);
  1765. ConvertImage(WorkImage, DstImage.Format);
  1766. end
  1767. else
  1768. WorkImage := SrcImage;
  1769. // Make sure we are still copying image to image, not invalid pointer to protected memory
  1770. ClipStretchBounds(SrcX, SrcY, SrcWidth, SrcHeight, DstX, DstY, DstWidth, DstHeight,
  1771. SrcImage.Width, SrcImage.Height, Rect(0, 0, DstImage.Width, DstImage.Height));
  1772. // Only pixel resize is supported for indexed images
  1773. if Info.IsIndexed then
  1774. Filter := rfNearest;
  1775. case Filter of
  1776. rfNearest: StretchNearest(WorkImage, SrcX, SrcY, SrcWidth, SrcHeight,
  1777. DstImage, DstX, DstY, DstWidth, DstHeight);
  1778. rfBilinear: StretchResample(WorkImage, SrcX, SrcY, SrcWidth, SrcHeight,
  1779. DstImage, DstX, DstY, DstWidth, DstHeight, sfLinear);
  1780. rfBicubic: StretchResample(WorkImage, SrcX, SrcY, SrcWidth, SrcHeight,
  1781. DstImage, DstX, DstY, DstWidth, DstHeight, sfCatmullRom);
  1782. end;
  1783. // If dest image was in special format we convert it back
  1784. if OldFormat <> ifUnknown then
  1785. ConvertImage(DstImage, OldFormat);
  1786. // Working image must be freed if it is not the same as source image
  1787. if WorkImage.Bits <> SrcImage.Bits then
  1788. FreeImage(WorkImage);
  1789. Result := True;
  1790. except
  1791. RaiseImaging(SErrorStretchRect, [ImageToStr(SrcImage), ImageToStr(DstImage)]);
  1792. end;
  1793. end
  1794. else
  1795. begin
  1796. // If source and dest rectangles have the same size call CopyRect
  1797. Result := CopyRect(SrcImage, SrcX, SrcY, SrcWidth, SrcHeight, DstImage, DstX, DstY)
  1798. end;
  1799. end;
  1800. procedure GetPixelDirect(const Image: TImageData; X, Y: LongInt; Pixel: Pointer);
  1801. var
  1802. BytesPerPixel: LongInt;
  1803. begin
  1804. BytesPerPixel := ImageFormatInfos[Image.Format].BytesPerPixel;
  1805. CopyPixel(@PByteArray(Image.Bits)[(Y * Image.Width + X) * BytesPerPixel],
  1806. Pixel, BytesPerPixel);
  1807. end;
  1808. procedure SetPixelDirect(const Image: TImageData; X, Y: LongInt; Pixel: Pointer);
  1809. var
  1810. BytesPerPixel: LongInt;
  1811. begin
  1812. BytesPerPixel := ImageFormatInfos[Image.Format].BytesPerPixel;
  1813. CopyPixel(Pixel, @PByteArray(Image.Bits)[(Y * Image.Width + X) * BytesPerPixel],
  1814. BytesPerPixel);
  1815. end;
  1816. function GetPixel32(const Image: TImageData; X, Y: LongInt): TColor32Rec;
  1817. var
  1818. Info: PImageFormatInfo;
  1819. Data: PByte;
  1820. begin
  1821. Info := ImageFormatInfos[Image.Format];
  1822. Data := @PByteArray(Image.Bits)[(Y * Image.Width + X) * Info.BytesPerPixel];
  1823. Result := GetPixel32Generic(Data, Info, Image.Palette);
  1824. end;
  1825. procedure SetPixel32(const Image: TImageData; X, Y: LongInt; const Color: TColor32Rec);
  1826. var
  1827. Info: PImageFormatInfo;
  1828. Data: PByte;
  1829. begin
  1830. Info := ImageFormatInfos[Image.Format];
  1831. Data := @PByteArray(Image.Bits)[(Y * Image.Width + X) * Info.BytesPerPixel];
  1832. SetPixel32Generic(Data, Info, Image.Palette, Color);
  1833. end;
  1834. function GetPixelFP(const Image: TImageData; X, Y: LongInt): TColorFPRec;
  1835. var
  1836. Info: PImageFormatInfo;
  1837. Data: PByte;
  1838. begin
  1839. Info := ImageFormatInfos[Image.Format];
  1840. Data := @PByteArray(Image.Bits)[(Y * Image.Width + X) * Info.BytesPerPixel];
  1841. Result := GetPixelFPGeneric(Data, Info, Image.Palette);
  1842. end;
  1843. procedure SetPixelFP(const Image: TImageData; X, Y: LongInt; const Color: TColorFPRec);
  1844. var
  1845. Info: PImageFormatInfo;
  1846. Data: PByte;
  1847. begin
  1848. Info := ImageFormatInfos[Image.Format];
  1849. Data := @PByteArray(Image.Bits)[(Y * Image.Width + X) * Info.BytesPerPixel];
  1850. SetPixelFPGeneric(Data, Info, Image.Palette, Color);
  1851. end;
  1852. { Palette Functions }
  1853. function NewPalette(Entries: LongInt; var Pal: PPalette32): Boolean;
  1854. begin
  1855. Result := False;
  1856. try
  1857. GetMem(Pal, Entries * SizeOf(TColor32Rec));
  1858. FillChar(Pal^, Entries * SizeOf(TColor32Rec), $FF);
  1859. Result := True;
  1860. except
  1861. RaiseImaging(SErrorNewPalette, [Entries]);
  1862. end;
  1863. end;
  1864. function FreePalette(var Pal: PPalette32): Boolean;
  1865. begin
  1866. Result := False;
  1867. try
  1868. FreeMemNil(Pal);
  1869. Result := True;
  1870. except
  1871. RaiseImaging(SErrorFreePalette, [Pal]);
  1872. end;
  1873. end;
  1874. function CopyPalette(SrcPal, DstPal: PPalette32; SrcIdx, DstIdx, Count: LongInt): Boolean;
  1875. begin
  1876. Result := False;
  1877. try
  1878. Move(SrcPal[SrcIdx], DstPal[DstIdx], Count * SizeOf(TColor32Rec));
  1879. Result := True;
  1880. except
  1881. RaiseImaging(SErrorCopyPalette, [Count, SrcPal, DstPal]);
  1882. end;
  1883. end;
  1884. function FindColor(Pal: PPalette32; Entries: LongInt; Color: TColor32):
  1885. LongInt;
  1886. var
  1887. Col: TColor32Rec;
  1888. I, MinDif, Dif: LongInt;
  1889. begin
  1890. Result := 0;
  1891. Col.Color := Color;
  1892. if Pal <> nil then
  1893. try
  1894. // first try to find exact match
  1895. for I := 0 to Entries - 1 do
  1896. with Pal[I] do
  1897. begin
  1898. if (A = Col.A) and (R = Col.R) and
  1899. (G = Col.G) and (B = Col.B) then
  1900. begin
  1901. Result := I;
  1902. Exit;
  1903. end;
  1904. end;
  1905. // if exact match was not found, find nearest color
  1906. MinDif := 1020;
  1907. for I := 0 to Entries - 1 do
  1908. with Pal[I] do
  1909. begin
  1910. Dif := Abs(R - Col.R);
  1911. if Dif > MinDif then Continue;
  1912. Dif := Dif + Abs(G - Col.G);
  1913. if Dif > MinDif then Continue;
  1914. Dif := Dif + Abs(B - Col.B);
  1915. if Dif > MinDif then Continue;
  1916. Dif := Dif + Abs(A - Col.A);
  1917. if Dif < MinDif then
  1918. begin
  1919. MinDif := Dif;
  1920. Result := I;
  1921. end;
  1922. end;
  1923. except
  1924. RaiseImaging(SErrorFindColor, [Pal, Entries]);
  1925. end;
  1926. end;
  1927. function FillGrayscalePalette(Pal: PPalette32; Entries: LongInt): Boolean;
  1928. var
  1929. I: LongInt;
  1930. begin
  1931. Result := False;
  1932. if Pal <> nil then
  1933. try
  1934. for I := 0 to Entries - 1 do
  1935. with Pal[I] do
  1936. begin
  1937. A := $FF;
  1938. R := Byte(I);
  1939. G := Byte(I);
  1940. B := Byte(I);
  1941. end;
  1942. Result := True;
  1943. except
  1944. RaiseImaging(SErrorGrayscalePalette, [Pal, Entries]);
  1945. end;
  1946. end;
  1947. function FillCustomPalette(Pal: PPalette32; Entries: LongInt; RBits, GBits,
  1948. BBits: Byte; Alpha: Byte = $FF): Boolean;
  1949. var
  1950. I, TotalBits, MaxEntries: LongInt;
  1951. begin
  1952. Result := False;
  1953. TotalBits := RBits + GBits + BBits;
  1954. MaxEntries := Min(Pow2Int(TotalBits), Entries);
  1955. FillChar(Pal^, Entries * SizeOf(TColor32Rec), 0);
  1956. if Pal <> nil then
  1957. try
  1958. for I := 0 to MaxEntries - 1 do
  1959. with Pal[I] do
  1960. begin
  1961. A := Alpha;
  1962. if RBits > 0 then
  1963. R := ((I shr Max(0, GBits + BBits - 1)) and (1 shl RBits - 1)) * 255 div (1 shl RBits - 1);
  1964. if GBits > 0 then
  1965. G := ((I shr Max(0, BBits - 1)) and (1 shl GBits - 1)) * 255 div (1 shl GBits - 1);
  1966. if BBits > 0 then
  1967. B := ((I shr 0) and (1 shl BBits - 1)) * 255 div (1 shl BBits - 1);
  1968. end;
  1969. Result := True;
  1970. except
  1971. RaiseImaging(SErrorCustomPalette, [Pal, Entries]);
  1972. end;
  1973. end;
  1974. function SwapChannelsOfPalette(Pal: PPalette32; Entries, SrcChannel,
  1975. DstChannel: LongInt): Boolean;
  1976. var
  1977. I: LongInt;
  1978. Swap: Byte;
  1979. begin
  1980. Result := False;
  1981. if Pal <> nil then
  1982. try
  1983. for I := 0 to Entries - 1 do
  1984. with Pal[I] do
  1985. begin
  1986. Swap := Channels[SrcChannel];
  1987. Channels[SrcChannel] := Channels[DstChannel];
  1988. Channels[DstChannel] := Swap;
  1989. end;
  1990. Result := True;
  1991. except
  1992. RaiseImaging(SErrorSwapPalette, [Pal, Entries]);
  1993. end;
  1994. end;
  1995. { Options Functions }
  1996. function SetOption(OptionId, Value: LongInt): Boolean;
  1997. begin
  1998. Result := False;
  1999. if (OptionId >= 0) and (OptionId < Length(Options)) and
  2000. (Options[OptionID] <> nil) then
  2001. begin
  2002. Options[OptionID]^ := CheckOptionValue(OptionId, Value);
  2003. Result := True;
  2004. end;
  2005. end;
  2006. function GetOption(OptionId: LongInt): LongInt;
  2007. begin
  2008. Result := InvalidOption;
  2009. if (OptionId >= 0) and (OptionId < Length(Options)) and
  2010. (Options[OptionID] <> nil) then
  2011. Result := Options[OptionID]^;
  2012. end;
  2013. function PushOptions: Boolean;
  2014. begin
  2015. Result := OptionStack.Push;
  2016. end;
  2017. function PopOptions: Boolean;
  2018. begin
  2019. Result := OptionStack.Pop;
  2020. end;
  2021. { Image Format Functions }
  2022. function GetImageFormatInfo(Format: TImageFormat; var Info: TImageFormatInfo): Boolean;
  2023. begin
  2024. FillChar(Info, SizeOf(Info), 0);
  2025. if ImageFormatInfos[Format] <> nil then
  2026. begin
  2027. Info := ImageFormatInfos[Format]^;
  2028. Result := True;
  2029. end
  2030. else
  2031. Result := False;
  2032. end;
  2033. function GetPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt;
  2034. begin
  2035. if ImageFormatInfos[Format] <> nil then
  2036. Result := ImageFormatInfos[Format].GetPixelsSize(Format, Width, Height)
  2037. else
  2038. Result := 0;
  2039. end;
  2040. { IO Functions }
  2041. procedure SetUserFileIO(OpenReadProc: TOpenReadProc; OpenWriteProc:
  2042. TOpenWriteProc;
  2043. CloseProc: TCloseProc; EofProc: TEofProc; SeekProc: TSeekProc; TellProc:
  2044. TTellProc; ReadProc: TReadProc; WriteProc: TWriteProc);
  2045. begin
  2046. FileIO.OpenRead := OpenReadProc;
  2047. FileIO.OpenWrite := OpenWriteProc;
  2048. FileIO.Close := CloseProc;
  2049. FileIO.Eof := EofProc;
  2050. FileIO.Seek := SeekProc;
  2051. FileIO.Tell := TellProc;
  2052. FileIO.Read := ReadProc;
  2053. FileIO.Write := WriteProc;
  2054. end;
  2055. procedure ResetFileIO;
  2056. begin
  2057. FileIO := OriginalFileIO;
  2058. end;
  2059. { ------------------------------------------------------------------------
  2060. Other Imaging Stuff
  2061. ------------------------------------------------------------------------}
  2062. function GetFormatName(Format: TImageFormat): string;
  2063. begin
  2064. if ImageFormatInfos[Format] <> nil then
  2065. Result := ImageFormatInfos[Format].Name
  2066. else
  2067. Result := SUnknownFormat;
  2068. end;
  2069. function ImageToStr(const Image: TImageData): string;
  2070. begin
  2071. if TestImage(Image) then
  2072. begin
  2073. with Image do
  2074. Result := SysUtils.Format(SImageInfo, [@Image, Width, Height,
  2075. GetFormatName(Format), (Size div 1024) + 0.0, Bits, Palette]);
  2076. end
  2077. else
  2078. Result := SysUtils.Format(SImageInfoInvalid, [@Image]);
  2079. end;
  2080. function GetVersionStr: string;
  2081. begin
  2082. Result := Format('%.1d.%.2d.%.1d', [ImagingVersionMajor, ImagingVersionMinor, ImagingVersionPatch]);
  2083. end;
  2084. function IffFormat(Condition: Boolean; const TruePart, FalsePart: TImageFormat): TImageFormat;
  2085. begin
  2086. if Condition then
  2087. Result := TruePart
  2088. else
  2089. Result := FalsePart;
  2090. end;
  2091. procedure RegisterImageFileFormat(AClass: TImageFileFormatClass);
  2092. begin
  2093. Assert(AClass <> nil);
  2094. if ImageFileFormats = nil then
  2095. ImageFileFormats := TList.Create;
  2096. if ImageFileFormats <> nil then
  2097. ImageFileFormats.Add(AClass.Create);
  2098. end;
  2099. procedure RegisterOption(OptionId: LongInt; Variable: PLongInt);
  2100. begin
  2101. if Options = nil then
  2102. InitOptions;
  2103. if Options <> nil then
  2104. begin
  2105. if OptionId >= Length(Options) then
  2106. SetLength(Options, OptionId + InitialOptions);
  2107. if (OptionId >= 0) and (OptionId < Length(Options)) and (Options[OptionId] = nil) then
  2108. Options[OptionId] := Variable;
  2109. end;
  2110. end;
  2111. function FindImageFileFormat(const Ext: string): TImageFileFormat;
  2112. var
  2113. I: LongInt;
  2114. begin
  2115. Result := nil;
  2116. for I := 0 to ImageFileFormats.Count - 1 do
  2117. if TImageFileFormat(ImageFileFormats[I]).Extensions.IndexOf(Ext) >= 0 then
  2118. begin
  2119. Result := TImageFileFormat(ImageFileFormats[I]);
  2120. Break;
  2121. end;
  2122. end;
  2123. function FindImageFileFormat(AClass: TImageFileFormatClass): TImageFileFormat;
  2124. var
  2125. I: LongInt;
  2126. begin
  2127. Result := nil;
  2128. for I := 0 to ImageFileFormats.Count - 1 do
  2129. if TImageFileFormat(ImageFileFormats[I]) is AClass then
  2130. begin
  2131. Result := TObject(ImageFileFormats[I]) as TImageFileFormat;
  2132. Break;
  2133. end;
  2134. end;
  2135. function GetImageFileFormatsFilter(AllFilter: Boolean): string;
  2136. var
  2137. I, J: LongInt;
  2138. Descriptions: string;
  2139. Filters, CurFilter: string;
  2140. FileFormat: TImageFileFormat;
  2141. begin
  2142. Descriptions := '';
  2143. Filters := '';
  2144. for I := 0 to ImageFileFormats.Count - 1 do
  2145. begin
  2146. FileFormat := TObject(ImageFileFormats[I]) as TImageFileFormat;
  2147. CurFilter := '';
  2148. for J := 0 to FileFormat.Extensions.Count - 1 do
  2149. begin
  2150. CurFilter := CurFilter + Format('*.%s', [FileFormat.Extensions[J]]);
  2151. if J < FileFormat.Extensions.Count - 1 then
  2152. CurFilter := CurFilter + ';';
  2153. end;
  2154. FmtStr(Descriptions, '%s%s (%s)|%2:s', [Descriptions, FileFormat.Name, CurFilter]);
  2155. FmtStr(Filters, '%s;%s', [Filters, CurFilter]);
  2156. if I < ImageFileFormats.Count - 1 then
  2157. Descriptions := Descriptions + '|';
  2158. end;
  2159. if (ImageFileFormats.Count > 1) and AllFilter then
  2160. FmtStr(Descriptions, '%s (%s)|%1:s|%s', [SAllFilter, Filters, Descriptions]);
  2161. Result := Descriptions;
  2162. end;
  2163. function GetFilterIndexExtension(Index: LongInt; AllFilter: Boolean): string;
  2164. var
  2165. FileFormat: TImageFileFormat;
  2166. begin
  2167. Index := Index - 1;
  2168. if AllFilter then
  2169. begin
  2170. if Index > 0 then
  2171. Index := Index - 1;
  2172. end;
  2173. if (Index >= 0) and (Index < ImageFileFormats.Count) then
  2174. begin
  2175. FileFormat := TObject(ImageFileFormats[Index]) as TImageFileFormat;
  2176. if FileFormat.Extensions.Count > 0 then
  2177. Result := FileFormat.Extensions[0]
  2178. else
  2179. Result := '';
  2180. end
  2181. else
  2182. Result := '';
  2183. end;
  2184. function GetExtensionFilterIndex(const Ext: string; AllFilter: Boolean): LongInt;
  2185. var
  2186. FileFormat: TImageFileFormat;
  2187. begin
  2188. FileFormat := FindImageFileFormat(Ext);
  2189. if FileFormat <> nil then
  2190. begin
  2191. Result := ImageFileFormats.IndexOf(FileFormat) + 1;
  2192. if AllFilter then
  2193. Inc(Result);
  2194. end
  2195. else
  2196. Result := -1;
  2197. end;
  2198. function GetIO: TIOFunctions;
  2199. begin
  2200. Result := IO;
  2201. end;
  2202. procedure RaiseImaging(const Msg: string; const Args: array of const);
  2203. {$IFDEF RAISE_EXCEPTIONS}
  2204. var
  2205. WholeMsg: string;
  2206. begin
  2207. WholeMsg := Msg;
  2208. if GetExceptObject <> nil then
  2209. WholeMsg := WholeMsg + ' ' + SExceptMsg + ': ' +
  2210. GetExceptObject.Message;
  2211. raise EImagingError.CreateFmt(WholeMsg, Args);
  2212. end;
  2213. {$ELSE}
  2214. begin
  2215. end;
  2216. {$ENDIF}
  2217. { Internal unit functions }
  2218. function CheckOptionValue(OptionId, Value: LongInt): LongInt;
  2219. begin
  2220. case OptionId of
  2221. ImagingColorReductionMask:
  2222. Result := ClampInt(Value, 0, $FF);
  2223. ImagingLoadOverrideFormat, ImagingSaveOverrideFormat:
  2224. Result := Iff(ImagingFormats.IsImageFormatValid(TImageFormat(Value)),
  2225. Value, LongInt(ifUnknown));
  2226. ImagingMipMapFilter: Result := ClampInt(Value, Ord(Low(TSamplingFilter)),
  2227. Ord(High(TSamplingFilter)));
  2228. else
  2229. Result := Value;
  2230. end;
  2231. end;
  2232. procedure SetFileIO;
  2233. begin
  2234. IO := FileIO;
  2235. end;
  2236. procedure SetStreamIO;
  2237. begin
  2238. IO := StreamIO;
  2239. end;
  2240. procedure SetMemoryIO;
  2241. begin
  2242. IO := MemoryIO;
  2243. end;
  2244. procedure InitImageFormats;
  2245. begin
  2246. ImagingFormats.InitImageFormats(ImageFormatInfos);
  2247. end;
  2248. procedure FreeImageFileFormats;
  2249. var
  2250. I: LongInt;
  2251. begin
  2252. if ImageFileFormats <> nil then
  2253. for I := 0 to ImageFileFormats.Count - 1 do
  2254. TImageFileFormat(ImageFileFormats[I]).Free;
  2255. FreeAndNil(ImageFileFormats);
  2256. end;
  2257. procedure InitOptions;
  2258. begin
  2259. SetLength(Options, InitialOptions);
  2260. OptionStack := TOptionStack.Create;
  2261. end;
  2262. procedure FreeOptions;
  2263. begin
  2264. SetLength(Options, 0);
  2265. FreeAndNil(OptionStack);
  2266. end;
  2267. { TImageFileFormat class implementation }
  2268. constructor TImageFileFormat.Create;
  2269. begin
  2270. inherited Create;
  2271. FName := SUnknownFormat;
  2272. FExtensions := TStringList.Create;
  2273. end;
  2274. destructor TImageFileFormat.Destroy;
  2275. begin
  2276. FExtensions.Free;
  2277. inherited Destroy;
  2278. end;
  2279. procedure TImageFileFormat.AddExtensions(const AExtensions: string);
  2280. begin
  2281. FExtensions.CommaText := AExtensions;
  2282. end;
  2283. function TImageFileFormat.GetFormatInfo(Format: TImageFormat): PImageFormatInfo;
  2284. begin
  2285. Result := ImageFormatInfos[Format];
  2286. end;
  2287. function TImageFileFormat.GetSupportedFormats: TImageFormats;
  2288. begin
  2289. Result := [];
  2290. end;
  2291. procedure TImageFileFormat.LoadData(Handle: TImagingHandle;
  2292. var Images: TDynImageDataArray; OnlyFirstFrame: Boolean);
  2293. begin
  2294. RaiseImaging(SFileFormatCanNotLoad, [FName]);
  2295. end;
  2296. function TImageFileFormat.LoadFromFile(const FileName: string;
  2297. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  2298. var
  2299. I: LongInt;
  2300. Handle: TImagingHandle;
  2301. begin
  2302. Result := False;
  2303. if FCanLoad then
  2304. try
  2305. // set IO ops to file ops and open given file
  2306. SetFileIO;
  2307. Handle := IO.OpenRead(PChar(FileName));
  2308. try
  2309. // test if file contains valid image and if so then load it
  2310. if TestFormat(Handle) then
  2311. begin
  2312. LoadData(Handle, Images, OnlyFirstlevel);
  2313. Result := True;
  2314. end
  2315. else
  2316. RaiseImaging(SFileNotValid, [FileName, Name]);
  2317. finally
  2318. IO.Close(Handle);
  2319. end;
  2320. // convert to overriden format if set
  2321. if LoadOverrideFormat <> ifUnknown then
  2322. for I := 0 to Length(Images) - 1 do
  2323. ConvertImage(Images[I], LoadOverrideFormat);
  2324. except
  2325. RaiseImaging(SErrorLoadingFile, [FileName]);
  2326. end;
  2327. end;
  2328. function TImageFileFormat.LoadFromStream(Stream: TStream;
  2329. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  2330. var
  2331. I: LongInt;
  2332. Handle: TImagingHandle;
  2333. OldPosition: Int64;
  2334. begin
  2335. Result := False;
  2336. OldPosition := Stream.Position;
  2337. if FCanLoad then
  2338. try
  2339. // set IO ops to stream ops and "open" given memory
  2340. SetStreamIO;
  2341. Handle := IO.OpenRead(Pointer(Stream));
  2342. if TestFormat(Handle) then
  2343. begin
  2344. // test if stream contains valid image and if so then load it
  2345. LoadData(Handle, Images, OnlyFirstlevel);
  2346. Result := True;
  2347. end
  2348. else
  2349. RaiseImaging(SStreamNotValid, [@Stream, Name]);
  2350. IO.Close(Handle);
  2351. // convert to overriden format if set
  2352. if LoadOverrideFormat <> ifUnknown then
  2353. for I := 0 to Length(Images) - 1 do
  2354. ConvertImage(Images[I], LoadOverrideFormat);
  2355. except
  2356. Stream.Position := OldPosition;
  2357. RaiseImaging(SErrorLoadingStream, [@Stream]);
  2358. end;
  2359. end;
  2360. function TImageFileFormat.LoadFromMemory(Data: Pointer; Size: LongInt; var
  2361. Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  2362. var
  2363. I: LongInt;
  2364. Handle: TImagingHandle;
  2365. IORec: TMemoryIORec;
  2366. begin
  2367. Result := False;
  2368. if FCanLoad then
  2369. try
  2370. // set IO ops to memory ops and "open" given memory
  2371. SetMemoryIO;
  2372. IORec.Data := Data;
  2373. IORec.Position := 0;
  2374. IORec.Size := Size;
  2375. Handle := IO.OpenRead(@IORec);
  2376. if TestFormat(Handle) then
  2377. begin
  2378. // test if memory contains valid image and if so then load it
  2379. LoadData(Handle, Images, OnlyFirstlevel);
  2380. Result := True;
  2381. end
  2382. else
  2383. RaiseImaging(SMemoryNotValid, [Data, Size, Name]);
  2384. IO.Close(Handle);
  2385. // convert to overriden format if set
  2386. if LoadOverrideFormat <> ifUnknown then
  2387. for I := 0 to Length(Images) - 1 do
  2388. ConvertImage(Images[I], LoadOverrideFormat);
  2389. except
  2390. RaiseImaging(SErrorLoadingMemory, [Data, Size]);
  2391. end;
  2392. end;
  2393. function TImageFileFormat.MakeCompatible(const Image: TImageData;
  2394. var Comp: TImageData): Boolean;
  2395. begin
  2396. InitImage(Comp);
  2397. if (SaveOverrideFormat in GetSupportedFormats) and
  2398. (SaveOverrideFormat <> Image.Format) then
  2399. begin
  2400. CloneImage(Image, Comp);
  2401. ConvertImage(Comp, SaveOverrideFormat);
  2402. Result := True;
  2403. end
  2404. else
  2405. if Image.Format in GetSupportedFormats then
  2406. begin
  2407. Comp := Image;
  2408. Result := True;
  2409. end
  2410. else
  2411. begin
  2412. CloneImage(Image, Comp);
  2413. Result := False;
  2414. end;
  2415. end;
  2416. function TImageFileFormat.TestFormat(Handle: TImagingHandle): Boolean;
  2417. begin
  2418. Result := False;
  2419. end;
  2420. procedure TImageFileFormat.SaveData(Handle: TImagingHandle;
  2421. const Images: TDynImageDataArray; Index: LongInt);
  2422. begin
  2423. RaiseImaging(SFileFormatCanNotSave, [FName]);
  2424. end;
  2425. function TImageFileFormat.SaveToFile(const FileName: string;
  2426. const Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  2427. var
  2428. Handle: TImagingHandle;
  2429. Len, I: LongInt;
  2430. Ext, FName: string;
  2431. begin
  2432. Result := False;
  2433. if FCanSave and TestImagesInArray(Images) then
  2434. try
  2435. SetFileIO;
  2436. Len := Length(Images);
  2437. if ((not FIsMultiImageFormat) and (OnlyFirstLevel or (Len = 1))) or
  2438. FIsMultiImageFormat then
  2439. begin
  2440. // write multi image to one file
  2441. Handle := IO.OpenWrite(PChar(FileName));
  2442. try
  2443. if OnlyFirstLevel then
  2444. SaveData(Handle, Images, 0)
  2445. else
  2446. SaveData(Handle, Images);
  2447. finally
  2448. IO.Close(Handle);
  2449. end;
  2450. end
  2451. else
  2452. begin
  2453. // write multi image to file sequence
  2454. Ext := ExtractFileExt(FileName);
  2455. FName := ChangeFileExt(FileName, '');
  2456. for I := 0 to Len - 1 do
  2457. begin
  2458. Handle := IO.OpenWrite(PChar(Format(FName + '%.3d' + Ext, [I])));
  2459. try
  2460. SaveData(Handle, Images, I);
  2461. finally
  2462. IO.Close(Handle);
  2463. end;
  2464. end;
  2465. end;
  2466. Result := True;
  2467. except
  2468. RaiseImaging(SErrorSavingFile, [FileName]);
  2469. end;
  2470. end;
  2471. function TImageFileFormat.SaveToStream(Stream: TStream;
  2472. const Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  2473. var
  2474. Handle: TImagingHandle;
  2475. Len, I: LongInt;
  2476. OldPosition: Int64;
  2477. begin
  2478. Result := False;
  2479. OldPosition := Stream.Position;
  2480. if FCanSave and TestImagesInArray(Images) then
  2481. try
  2482. SetStreamIO;
  2483. Len := Length(Images);
  2484. Handle := IO.OpenWrite(PChar(Stream));
  2485. if ((not FIsMultiImageFormat) and (OnlyFirstLevel or (Len = 1))) or
  2486. FIsMultiImageFormat then
  2487. begin
  2488. if OnlyFirstLevel then
  2489. SaveData(Handle, Images, 0)
  2490. else
  2491. SaveData(Handle, Images);
  2492. end
  2493. else
  2494. for I := 0 to Len - 1 do
  2495. SaveData(Handle, Images, I);
  2496. IO.Close(Handle);
  2497. Result := True;
  2498. except
  2499. Stream.Position := OldPosition;
  2500. RaiseImaging(SErrorSavingStream, [@Stream]);
  2501. end;
  2502. end;
  2503. function TImageFileFormat.SaveToMemory(Data: Pointer; var Size: LongInt;
  2504. const Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  2505. var
  2506. Handle: TImagingHandle;
  2507. Len, I: LongInt;
  2508. IORec: TMemoryIORec;
  2509. begin
  2510. Result := False;
  2511. if FCanSave and TestImagesInArray(Images) then
  2512. try
  2513. SetMemoryIO;
  2514. IORec.Data := Data;
  2515. IORec.Position := 0;
  2516. IORec.Size := Size;
  2517. IORec.Written := 0;
  2518. Len := Length(Images);
  2519. Handle := IO.OpenWrite(PChar(@IORec));
  2520. if ((not FIsMultiImageFormat) and (OnlyFirstLevel or (Len = 1))) or
  2521. FIsMultiImageFormat then
  2522. begin
  2523. if OnlyFirstLevel then
  2524. SaveData(Handle, Images, 0)
  2525. else
  2526. SaveData(Handle, Images);
  2527. end
  2528. else
  2529. for I := 0 to Len - 1 do
  2530. SaveData(Handle, Images, I);
  2531. IO.Close(Handle);
  2532. Size := IORec.Written;
  2533. Result := True;
  2534. except
  2535. RaiseImaging(SErrorSavingMemory, [Data, Size]);
  2536. end;
  2537. end;
  2538. { TOptionStack class implementation }
  2539. constructor TOptionStack.Create;
  2540. begin
  2541. inherited Create;
  2542. FPosition := -1;
  2543. end;
  2544. destructor TOptionStack.Destroy;
  2545. var
  2546. I: LongInt;
  2547. begin
  2548. for I := 0 to OptionStackDepth - 1 do
  2549. SetLength(FStack[I], 0);
  2550. inherited Destroy;
  2551. end;
  2552. function TOptionStack.Pop: Boolean;
  2553. var
  2554. I: LongInt;
  2555. begin
  2556. Result := False;
  2557. if FPosition >= 0 then
  2558. begin
  2559. SetLength(Options, Length(FStack[FPosition]));
  2560. for I := 0 to Length(FStack[FPosition]) - 1 do
  2561. if Options[I] <> nil then
  2562. Options[I]^ := FStack[FPosition, I];
  2563. Dec(FPosition);
  2564. Result := True;
  2565. end;
  2566. end;
  2567. function TOptionStack.Push: Boolean;
  2568. var
  2569. I: LongInt;
  2570. begin
  2571. Result := False;
  2572. if FPosition < OptionStackDepth - 1 then
  2573. begin
  2574. Inc(FPosition);
  2575. SetLength(FStack[FPosition], Length(Options));
  2576. for I := 0 to Length(Options) - 1 do
  2577. if Options[I] <> nil then
  2578. FStack[FPosition, I] := Options[I]^;
  2579. Result := True;
  2580. end;
  2581. end;
  2582. initialization
  2583. {$IFDEF MEMCHECK}
  2584. {$IF CompilerVersion >= 18}
  2585. System.ReportMemoryLeaksOnShutdown := True;
  2586. {$IFEND}
  2587. {$ENDIF}
  2588. InitImageFormats;
  2589. RegisterOption(ImagingColorReductionMask, @ColorReductionMask);
  2590. RegisterOption(ImagingLoadOverrideFormat, @LoadOverrideFormat);
  2591. RegisterOption(ImagingSaveOverrideFormat, @SaveOverrideFormat);
  2592. RegisterOption(ImagingMipMapFilter, @MipMapFilter);
  2593. finalization
  2594. FreeOptions;
  2595. FreeImageFileFormats;
  2596. {
  2597. File Notes:
  2598. -- TODOS ----------------------------------------------------
  2599. - make searching for the closest color in palette much faster - MapImageToPal
  2600. - investigate CopyPixel and ComparePixels inline problems - line 550
  2601. - add to low level interface function
  2602. CreateImageFromRawData(W, H, Bpp, Data, Align, Flipped, Endian, ...)
  2603. and CreateRawDataFromImage() - use these in BMP loading (align)
  2604. and PNG loading (endian)
  2605. - remove cloning of SrcImage in CopyRect for
  2606. incompatible formats - use CopyPixel rather? test speeds
  2607. - return additional info about loaded image like this
  2608. TicksPerSecond := PMNGDetails(GetOption(ImagingMNGFileDetails)).TicksPerSecond;
  2609. - add some file format enumeration functions (to low level)
  2610. - add loading of multi images from file sequence
  2611. - add some color functions - create, convert, add, merge, ...
  2612. - do not load all frames when only one is required, possible?
  2613. (LoadImageFromFile on MNG/DDS)
  2614. -- 0.19 Changes/Bug Fixes -----------------------------------
  2615. - fixed bug in NewImage: if given format was ifDefault it wasn't
  2616. replaced with DefaultImageFormat constant which caused problems later
  2617. in other units
  2618. - fixed bug in RotateImage which caused that rotated special format
  2619. images were whole black
  2620. - LoadImageFromXXX and LoadMultiImageFromXXX now use DetermineXXXFormat
  2621. when choosing proper loader, this eliminated need for Ext parameter
  2622. in stream and memory loading functions
  2623. - added GetVersionStr function
  2624. - fixed bug in ResizeImage which caued indexed images to lose their
  2625. palette during process resulting in whole black image
  2626. - Clipping in ...Rect functions now uses clipping procs from ImagingUtility,
  2627. it also works better
  2628. - FillRect optimization for 8, 16, and 32 bit formats
  2629. - added pixel set/get functions to low level interface:
  2630. GetPixelDirect, SetPixelDirect, GetPixel32, SetPixel32,
  2631. GetPixelFP, SetPixelFP
  2632. - removed GetPixelBytes low level intf function - redundant
  2633. (same data can be obtained by GetImageFormatInfo)
  2634. - made small changes in many parts of library to compile
  2635. on AMD64 CPU (Linux with FPC)
  2636. - changed InitImage to procedure (function was pointless)
  2637. - Method TestFormat of TImageFileFormat class made public
  2638. (was protected)
  2639. - added function IsFileFormatSupported to low level interface
  2640. (contributed by Paul Michell)
  2641. - fixed some missing format arguments from error strings
  2642. which caused Format function to raise exception
  2643. - removed forgotten debug code that disabled filtered resizing of images with
  2644. channel bitcounts > 8
  2645. -- 0.17 Changes/Bug Fixes -----------------------------------
  2646. - changed order of parameters of CopyRect function
  2647. - GenerateMipMaps now filters mipmap levels
  2648. - ResizeImage functions was extended to allow bilinear and bicubic filtering
  2649. - added StretchRect function to low level interface
  2650. - added functions GetImageFileFormatsFilter, GetFilterIndexExtension,
  2651. and GetExtensionFilterIndex
  2652. -- 0.15 Changes/Bug Fixes -----------------------------------
  2653. - added function RotateImage to low level interface
  2654. - moved TImageFormatInfo record and types required by it to
  2655. ImagingTypes unit, changed GetImageFormatInfo low level
  2656. interface function to return TImageFormatInfo instead of short info
  2657. - added checking of options values validity before they are used
  2658. - fixed possible memory leak in CloneImage
  2659. - added ReplaceColor function to low level interface
  2660. - new function FindImageFileFormat by class added
  2661. -- 0.13 Changes/Bug Fixes -----------------------------------
  2662. - added DetermineFileFormat, DetermineStreamFormat, DetermineMemoryFormat,
  2663. GetPixelsSize functions to low level interface
  2664. - added NewPalette, CopyPalette, FreePalette functions
  2665. to low level interface
  2666. - added MapImageToPalette, FillRect, SplitImage, MakePaletteForImages
  2667. functions to low level interface
  2668. - fixed buggy FillCustomPalette function (possible div by zero and others)
  2669. - added CopyRect function to low level interface
  2670. - Member functions of TImageFormatInfo record implemented for all formats
  2671. - before saving images TestImagesInArray is called now
  2672. - added TestImagesInArray function to low level interface
  2673. - added GenerateMipMaps function to low level interface
  2674. - stream position in load/save from/to stream is now set to position before
  2675. function was called if error occurs
  2676. - when error occured during load/save from/to file file handle
  2677. was not released
  2678. - CloneImage returned always False
  2679. }
  2680. end.