Imaging.pas 116 KB

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