Imaging.pas 116 KB

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