Imaging.pas 119 KB

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