Imaging.pas 113 KB

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