Imaging.pas 117 KB

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