Imaging.pas 146 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420
  1. {
  2. Vampyre Imaging Library
  3. by Marek Mauder
  4. https://github.com/galfar/imaginglib
  5. https://imaginglib.sourceforge.io
  6. - - - - -
  7. This Source Code Form is subject to the terms of the Mozilla Public
  8. License, v. 2.0. If a copy of the MPL was not distributed with this
  9. file, You can obtain one at https://mozilla.org/MPL/2.0.
  10. }
  11. { This unit is heart of Imaging library. It contains basic functions for
  12. manipulating image data as well as various image file format support.}
  13. unit Imaging;
  14. {$I ImagingOptions.inc}
  15. interface
  16. uses
  17. SysUtils, Classes, Types, ImagingTypes;
  18. type
  19. { Default Imaging exception class }
  20. EImagingError = class(Exception);
  21. { Raised when function receives bad image (not passed TestImage).}
  22. EImagingBadImage = class(Exception)
  23. public
  24. constructor Create;
  25. end;
  26. { Dynamic array of TImageData records }
  27. TDynImageDataArray = array of TImageData;
  28. { ------------------------------------------------------------------------
  29. Low Level Interface Functions
  30. ------------------------------------------------------------------------}
  31. { General Functions }
  32. { Initializes image (all is set to zeroes). Call this for each image
  33. before using it (before calling every other function) to be sure there
  34. are no random-filled bytes (which would cause errors later).}
  35. procedure InitImage(out Image: TImageData);
  36. { Creates empty image of given dimensions and format. Image is filled with
  37. transparent black color (A=0, R=0, G=0, B=0).}
  38. function NewImage(Width, Height: LongInt; Format: TImageFormat;
  39. var Image: TImageData): Boolean;
  40. { Returns True if given TImageData record is valid.}
  41. function TestImage(const Image: TImageData): Boolean;
  42. { Frees given image data. After this call image is in the same state
  43. as after calling InitImage. If image is not valid (dost not pass TestImage
  44. test) it is only zeroed by calling InitImage.}
  45. procedure FreeImage(var Image: TImageData);
  46. { Call FreeImage() on all images in given dynamic array and sets its
  47. length to zero.}
  48. procedure FreeImagesInArray(var Images: TDynImageDataArray);
  49. { Returns True if all TImageData records in given array are valid. Returns False
  50. if at least one is invalid or if array is empty.}
  51. function TestImagesInArray(const Images: TDynImageDataArray): Boolean;
  52. { Checks given file for every supported image file format and if
  53. the file is in one of them returns its string identifier
  54. (which can be used in LoadFromStream/LoadFromMem type functions).
  55. If file is not in any of the supported formats empty string is returned.}
  56. function DetermineFileFormat(const FileName: string): string;
  57. { Checks given stream for every supported image file format and if
  58. the stream is in one of them returns its string identifier
  59. (which can be used in LoadFromStream/LoadFromMem type functions).
  60. If stream is not in any of the supported formats empty string is returned.}
  61. function DetermineStreamFormat(Stream: TStream): string;
  62. { Checks given memory for every supported image file format and if
  63. the memory is in one of them returns its string identifier
  64. (which can be used in LoadFromStream/LoadFromMem type functions).
  65. If memory is not in any of the supported formats empty string is returned.}
  66. function DetermineMemoryFormat(Data: Pointer; Size: LongInt): string;
  67. { Checks that an appropriate file format is supported purely from inspecting
  68. the given file name's extension (not contents of the file itself).
  69. The file need not exist.}
  70. function IsFileFormatSupported(const FileName: string): Boolean;
  71. { Enumerates all registered image file formats. Descriptive name,
  72. default extension, masks (like '*.jpg,*.jfif') and some capabilities
  73. of each format are returned. To enumerate all formats start with Index at 0 and
  74. call EnumFileFormats with given Index in loop until it returns False (Index is
  75. automatically increased by 1 in function's body on successful call).}
  76. function EnumFileFormats(var Index: LongInt; var Name, DefaultExt, Masks: string;
  77. var CanSaveImages, IsMultiImageFormat: Boolean): Boolean;
  78. { Loading Functions }
  79. { Loads single image from given file.}
  80. function LoadImageFromFile(const FileName: string; var Image: TImageData): Boolean;
  81. { Loads single image from given stream. If function fails stream position
  82. is not changed.}
  83. function LoadImageFromStream(Stream: TStream; var Image: TImageData): Boolean;
  84. { Loads single image from given memory location.}
  85. function LoadImageFromMemory(Data: Pointer; Size: LongInt; var Image: TImageData): Boolean;
  86. { Loads multiple images from given file.}
  87. function LoadMultiImageFromFile(const FileName: string;
  88. var Images: TDynImageDataArray): Boolean;
  89. { Loads multiple images from given stream. If function fails stream position
  90. is not changed.}
  91. function LoadMultiImageFromStream(Stream: TStream;
  92. var Images: TDynImageDataArray): Boolean;
  93. { Loads multiple images from given memory location.}
  94. function LoadMultiImageFromMemory(Data: Pointer; Size: LongInt;
  95. var Images: TDynImageDataArray): Boolean;
  96. { Saving Functions }
  97. { Saves single image to given file.}
  98. function SaveImageToFile(const FileName: string; const Image: TImageData): Boolean;
  99. { Saves single image to given stream. If function fails stream position
  100. is not changed. Ext identifies desired image file format (jpg, png, dds, ...).}
  101. function SaveImageToStream(const Ext: string; Stream: TStream;
  102. const Image: TImageData): Boolean;
  103. { Saves single image to given memory location. Memory must be allocated and its
  104. size is passed in Size parameter in which number of written bytes is returned.
  105. Ext identifies desired image file format (jpg, png, dds, ...).}
  106. function SaveImageToMemory(const Ext: string; Data: Pointer; var Size: LongInt;
  107. const Image: TImageData): Boolean;
  108. { Saves multiple images to given file. If format supports
  109. only single level images and there are multiple images to be saved,
  110. they are saved as sequence of files img000.jpg, img001.jpg ....).}
  111. function SaveMultiImageToFile(const FileName: string;
  112. const Images: TDynImageDataArray): Boolean;
  113. { Saves multiple images to given stream. If format supports
  114. only single level images and there are multiple images to be saved,
  115. they are saved one after another to the stream. If function fails stream
  116. position is not changed. Ext identifies desired image file format (jpg, png, dds, ...).}
  117. function SaveMultiImageToStream(const Ext: string; Stream: TStream;
  118. const Images: TDynImageDataArray): Boolean;
  119. { Saves multiple images to given memory location. If format supports
  120. only single level images and there are multiple images to be saved,
  121. they are saved one after another to the memory. Memory must be allocated and
  122. its size is passed in Size parameter in which number of written bytes is returned.
  123. Ext identifies desired image file format (jpg, png, dds, ...).}
  124. function SaveMultiImageToMemory(const Ext: string; Data: Pointer;
  125. var Size: LongInt; const Images: TDynImageDataArray): Boolean;
  126. { Manipulation Functions }
  127. { Creates identical copy of image data. Clone should be initialized
  128. by InitImage or it should be valid image which will be freed by CloneImage.}
  129. function CloneImage(const Image: TImageData; var Clone: TImageData): Boolean;
  130. { Converts image to the given format.}
  131. function ConvertImage(var Image: TImageData; DestFormat: TImageFormat): Boolean;
  132. { Flips given image. Reverses the image along its horizontal axis - the top
  133. becomes the bottom and vice versa.}
  134. function FlipImage(var Image: TImageData): Boolean;
  135. { Mirrors given image. Reverses the image along its vertical axis � the left
  136. side becomes the right and vice versa.}
  137. function MirrorImage(var Image: TImageData): Boolean;
  138. { Resizes given image to new dimensions. Nearest, bilinear, or bicubic filtering
  139. can be used. Input Image must already be created - use NewImage to create new images.}
  140. function ResizeImage(var Image: TImageData; NewWidth, NewHeight: LongInt;
  141. Filter: TResizeFilter): Boolean;
  142. { Swaps SrcChannel and DstChannel color or alpha channels of image.
  143. Use ChannelRed, ChannelBlue, ChannelGreen, ChannelAlpha constants to
  144. identify channels.}
  145. function SwapChannels(var Image: TImageData; SrcChannel, DstChannel: LongInt): Boolean;
  146. { Reduces the number of colors of the Image. Currently MaxColors must be in
  147. range <2, 4096>. Color reduction works also for alpha channel. Note that for
  148. large images and big number of colors it can be very slow.
  149. Output format of the image is the same as input format.}
  150. function ReduceColors(var Image: TImageData; MaxColors: LongInt): Boolean;
  151. { Generates mipmaps for image. Levels is the number of desired mipmaps levels
  152. with zero (or some invalid number) meaning all possible levels.}
  153. function GenerateMipMaps(const Image: TImageData; Levels: LongInt;
  154. var MipMaps: TDynImageDataArray): Boolean;
  155. { Maps image to existing palette producing image in ifIndex8 format.
  156. Pal must be allocated to at least Entries * SizeOf(TColor32Rec) bytes.
  157. As resulting image is in 8bit indexed format Entries must be lower or
  158. equal to 256.}
  159. function MapImageToPalette(var Image: TImageData; Pal: PPalette32;
  160. Entries: LongInt): Boolean;
  161. { Splits image into XChunks x YChunks subimages. Default size of each chunk is
  162. ChunkWidth x ChunkHeight. If PreserveSize si True chunks at the edges of
  163. the image are also ChunkWidth x ChunkHeight sized and empty space is filled
  164. with optional Fill pixels. After calling this function XChunks contains number of
  165. chunks along x axis and YChunks along y axis. To access chunk [X, Y] use this
  166. index: Chunks[Y * XChunks + X].}
  167. function SplitImage(var Image: TImageData; var Chunks: TDynImageDataArray;
  168. ChunkWidth, ChunkHeight: LongInt; var XChunks, YChunks: LongInt;
  169. PreserveSize: Boolean; Fill: Pointer = nil): Boolean;
  170. { Creates palette with MaxColors based on the colors of images in Images array.
  171. Use it when you want to convert several images to indexed format using
  172. single palette for all of them. If ConvertImages is True images in array
  173. are converted to indexed format using resulting palette. if it is False
  174. images are left intact and only resulting palette is returned in Pal.
  175. Pal must be allocated to have at least MaxColors entries.}
  176. function MakePaletteForImages(var Images: TDynImageDataArray; Pal: PPalette32;
  177. MaxColors: LongInt; ConvertImages: Boolean): Boolean;
  178. { Rotates image by Angle degrees counterclockwise. All angles are allowed. }
  179. procedure RotateImage(var Image: TImageData; Angle: Single);
  180. { Rotates image by Angle that is multiple of 90 degrees counterclockwise. }
  181. procedure RotateImageMul90(var Image: TImageData; AngleDeg: Integer);
  182. { Drawing/Pixel functions }
  183. { Copies rectangular part of SrcImage to DstImage. No blending is performed -
  184. alpha is simply copied to destination image. Operates also with
  185. negative X and Y coordinates.
  186. Note that copying is fastest for images in the same data format
  187. (and slowest for images in special formats).}
  188. function CopyRect(const SrcImage: TImageData; SrcX, SrcY, Width, Height: LongInt;
  189. var DstImage: TImageData; DstX, DstY: LongInt): Boolean;
  190. { Fills given rectangle of image with given pixel fill data. Fill should point
  191. to the pixel in the same format as the given image is in.}
  192. function FillRect(var Image: TImageData; X, Y, Width, Height: LongInt; FillColor: Pointer): Boolean;
  193. { Replaces pixels with OldPixel in the given rectangle by NewPixel.
  194. OldPixel and NewPixel should point to the pixels in the same format
  195. as the given image is in.}
  196. function ReplaceColor(var Image: TImageData; X, Y, Width, Height: LongInt;
  197. OldColor, NewColor: Pointer): Boolean;
  198. { Stretches the contents of the source rectangle to the destination rectangle
  199. with optional resampling. No blending is performed - alpha is
  200. simply copied/resampled to destination image. Note that stretching is
  201. fastest for images in the same data format (and slowest for
  202. images in special formats).}
  203. function StretchRect(const SrcImage: TImageData; SrcX, SrcY, SrcWidth,
  204. SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth,
  205. DstHeight: LongInt; Filter: TResizeFilter): Boolean;
  206. { Copies pixel of Image at [X, Y] to memory pointed at by Pixel. Doesn't
  207. work with special formats.}
  208. procedure GetPixelDirect(const Image: TImageData; X, Y: LongInt; Pixel: Pointer);
  209. { Copies pixel from memory pointed at by Pixel to Image at position [X, Y].
  210. Doesn't work with special formats.}
  211. procedure SetPixelDirect(const Image: TImageData; X, Y: LongInt; Pixel: Pointer);
  212. { Function for getting pixel colors. Native pixel is read from Image and
  213. then translated to 32 bit ARGB. Works for all image formats (except special)
  214. so it is not very fast.}
  215. function GetPixel32(const Image: TImageData; X, Y: LongInt): TColor32Rec;
  216. { Procedure for setting pixel colors. Input 32 bit ARGB color is translated to
  217. native format and then written to Image. Works for all image formats (except special)
  218. so it is not very fast.}
  219. procedure SetPixel32(const Image: TImageData; X, Y: LongInt; const Color: TColor32Rec);
  220. { Function for getting pixel colors. Native pixel is read from Image and
  221. then translated to FP ARGB. Works for all image formats (except special)
  222. so it is not very fast.}
  223. function GetPixelFP(const Image: TImageData; X, Y: LongInt): TColorFPRec;
  224. { Procedure for setting pixel colors. Input FP ARGB color is translated to
  225. native format and then written to Image. Works for all image formats (except special)
  226. so it is not very fast.}
  227. procedure SetPixelFP(const Image: TImageData; X, Y: LongInt; const Color: TColorFPRec);
  228. { Palette Functions }
  229. { Allocates new palette with Entries ARGB color entries.}
  230. procedure NewPalette(Entries: LongInt; var Pal: PPalette32);
  231. { Frees given palette.}
  232. procedure FreePalette(var Pal: PPalette32);
  233. { Copies Count palette entries from SrcPal starting at index SrcIdx to
  234. DstPal at index DstPal.}
  235. procedure CopyPalette(SrcPal, DstPal: PPalette32; SrcIdx, DstIdx, Count: LongInt);
  236. { Returns index of color in palette or index of nearest color if exact match
  237. is not found. Pal must have at least Entries color entries.}
  238. function FindColor(Pal: PPalette32; Entries: LongInt; Color: TColor32): LongInt;
  239. { Creates grayscale palette where each color channel has the same value.
  240. Pal must have at least Entries color entries.}
  241. procedure FillGrayscalePalette(Pal: PPalette32; Entries: LongInt);
  242. { Creates palette with given bitcount for each channel.
  243. 2^(RBits + GBits + BBits) should be equal to Entries. Examples:
  244. (3, 3, 2) will create palette with all possible colors of R3G3B2 format
  245. and (8, 0, 0) will create palette with 256 shades of red.
  246. Pal must be allocated to at least Entries * SizeOf(TColor32Rec) bytes.}
  247. procedure FillCustomPalette(Pal: PPalette32; Entries: LongInt; RBits, GBits,
  248. BBits: Byte; Alpha: Byte = $FF);
  249. { Swaps SrcChannel and DstChannel color or alpha channels of palette.
  250. Use ChannelRed, ChannelBlue, ChannelGreen, ChannelAlpha constants to
  251. identify channels. Pal must be allocated to at least
  252. Entries * SizeOf(TColor32Rec) bytes.}
  253. procedure SwapChannelsOfPalette(Pal: PPalette32; Entries, SrcChannel,
  254. DstChannel: LongInt);
  255. { Options Functions }
  256. { Sets value of integer option specified by OptionId parameter.
  257. Option Ids are constants starting ImagingXXX.}
  258. function SetOption(OptionId, Value: LongInt): Boolean;
  259. { Returns value of integer option specified by OptionId parameter. If OptionId is
  260. invalid, InvalidOption is returned. Option Ids are constants
  261. starting ImagingXXX.}
  262. function GetOption(OptionId: LongInt): LongInt;
  263. { Pushes current values of all options on the stack. Returns True
  264. if successful (max stack depth is 8 now). }
  265. function PushOptions: Boolean;
  266. { Pops back values of all options from the top of the stack. Returns True
  267. if successful (max stack depth is 8 now). }
  268. function PopOptions: Boolean;
  269. { Image Data Format Functions }
  270. { Returns short information about given image format.}
  271. function GetImageFormatInfo(Format: TImageFormat; out Info: TImageFormatInfo): Boolean;
  272. { Returns size in bytes of Width x Height area of pixels. Works for all formats.}
  273. function GetPixelsSize(Format: TImageFormat; Width, Height: LongInt): Int64;
  274. { IO Functions }
  275. { User can set his own file IO functions used when loading from/saving to
  276. files by this function.}
  277. procedure SetUserFileIO(OpenProc: TOpenProc; CloseProc: TCloseProc; EofProc: TEofProc; SeekProc:
  278. TSeekProc; TellProc: TTellProc; ReadProc: TReadProc; WriteProc: TWriteProc);
  279. { Sets file IO functions to Imaging default.}
  280. procedure ResetFileIO;
  281. { Raw Image IO Functions }
  282. procedure ReadRawImageFromFile(const FileName: string; Width, Height: Integer;
  283. Format: TImageFormat; var Image: TImageData; const Offset: Int64 = 0; RowLength: Integer = 0);
  284. procedure ReadRawImageFromStream(Stream: TStream; Width, Height: Integer;
  285. Format: TImageFormat; var Image: TImageData; const Offset: Int64 = 0; RowLength: Integer = 0);
  286. procedure ReadRawImageFromMemory(Data: Pointer; const DataSize: Int64; Width, Height: Integer;
  287. Format: TImageFormat; var Image: TImageData; const Offset: Int64 = 0; RowLength: Integer = 0);
  288. procedure ReadRawImageRect(Data: Pointer; Left, Top, Width, Height: Integer;
  289. var Image: TImageData; const Offset: Int64 = 0; RowLength: Integer = 0);
  290. procedure WriteRawImageToFile(const FileName: string; const Image: TImageData;
  291. const Offset: Int64 = 0; RowLength: Integer = 0);
  292. procedure WriteRawImageToStream(Stream: TStream; const Image: TImageData;
  293. const Offset: Int64 = 0; RowLength: Integer = 0);
  294. procedure WriteRawImageToMemory(Data: Pointer; const DataSize: Int64; const Image: TImageData;
  295. const Offset: Int64 = 0; RowLength: Integer = 0);
  296. procedure WriteRawImageRect(Data: Pointer; Left, Top, Width, Height: Integer;
  297. const Image: TImageData; const Offset: Int64 = 0; RowLength: Integer = 0);
  298. { Convenience/helper Functions }
  299. { Resizes image proportionally to fit the given width and height. }
  300. procedure ResizeImageToFit(const SrcImage: TImageData; FitWidth, FitHeight: Integer;
  301. Filter: TResizeFilter; var DestImage: TImageData);
  302. { Color functions }
  303. { Constructs TColor24Rec color.}
  304. function Color24(R, G, B: Byte): TColor24Rec; {$IFDEF USE_INLINE}inline;{$ENDIF}
  305. { Constructs TColor32Rec color.}
  306. function Color32(A, R, G, B: Byte): TColor32Rec; {$IFDEF USE_INLINE}inline;{$ENDIF}
  307. { Constructs TColor48Rec color.}
  308. function Color48(R, G, B: Word): TColor48Rec; {$IFDEF USE_INLINE}inline;{$ENDIF}
  309. { Constructs TColor64Rec color.}
  310. function Color64(A, R, G, B: Word): TColor64Rec; {$IFDEF USE_INLINE}inline;{$ENDIF}
  311. { Constructs TColorFPRec color.}
  312. function ColorFP(A, R, G, B: Single): TColorFPRec; {$IFDEF USE_INLINE}inline;{$ENDIF}
  313. { Constructs TColorHFRec color.}
  314. function ColorHF(A, R, G, B: THalfFloat): TColorHFRec; {$IFDEF USE_INLINE}inline;{$ENDIF}
  315. { Convenience function for getting alpha component of TColor32.}
  316. function GetAlphaValue(Color32: TColor32): Byte; {$IFDEF USE_INLINE}inline;{$ENDIF}
  317. { Convenience function for getting red component of TColor32.}
  318. function GetRedValue(Color32: TColor32): Byte; {$IFDEF USE_INLINE}inline;{$ENDIF}
  319. { Convenience function for getting green component of TColor32.}
  320. function GetGreenValue(Color32: TColor32): Byte; {$IFDEF USE_INLINE}inline;{$ENDIF}
  321. { Convenience function for getting blue component of TColor32.}
  322. function GetBlueValue(Color32: TColor32): Byte; {$IFDEF USE_INLINE}inline;{$ENDIF}
  323. { ------------------------------------------------------------------------
  324. Other Imaging Stuff
  325. ------------------------------------------------------------------------}
  326. type
  327. { Set of TImageFormat enum.}
  328. TImageFormats = set of TImageFormat;
  329. { Record containing set of IO functions internally used by image loaders/savers.}
  330. TIOFunctions = record
  331. Open: TOpenProc;
  332. Close: TCloseProc;
  333. Eof: TEofProc;
  334. Seek: TSeekProc;
  335. Tell: TTellProc;
  336. Read: TReadProc;
  337. Write: TWriteProc;
  338. end;
  339. PIOFunctions = ^TIOFunctions;
  340. type
  341. TFileFormatFeature = (
  342. ffLoad,
  343. ffSave,
  344. ffMultiImage,
  345. ffProgress,
  346. ffReadScanlines);
  347. TFileFormatFeatures = set of TFileFormatFeature;
  348. TMetadata = class;
  349. { Base class for various image file format loaders/savers which
  350. descend from this class. If you want to add support for new image file
  351. format the best way is probably to look at TImageFileFormat descendants'
  352. implementations that are already part of Imaging.}
  353. {$TYPEINFO ON}
  354. TImageFileFormat = class
  355. private
  356. FExtensions: TStringList;
  357. FMasks: TStringList;
  358. function GetCanLoad: Boolean;
  359. function GetCanSave: Boolean;
  360. function GetIsMultiImageFormat: Boolean;
  361. { Does various checks and actions before LoadData method is called.}
  362. function PrepareLoad(Handle: TImagingHandle; var Images: TDynImageDataArray;
  363. OnlyFirstFrame: Boolean): Boolean;
  364. { Processes some actions according to result of LoadData.}
  365. function PostLoadCheck(var Images: TDynImageDataArray; LoadResult: Boolean): Boolean;
  366. { Helper function to be called in SaveData methods of descendants (ensures proper
  367. index and sets FFirstIdx and FLastIdx for multi-images).}
  368. function PrepareSave(Handle: TImagingHandle; const Images: TDynImageDataArray;
  369. var Index: LongInt): Boolean;
  370. protected
  371. FName: string;
  372. FFeatures: TFileFormatFeatures;
  373. FSupportedFormats: TImageFormats;
  374. FFirstIdx, FLastIdx: LongInt;
  375. FMetadata: TMetadata;
  376. { Descendants must override this method and define file format name and
  377. capabilities.}
  378. procedure Define; virtual;
  379. { Defines filename masks for this image file format. AMasks should be
  380. in format '*.ext1,*.ext2,umajo.*'.}
  381. procedure AddMasks(const AMasks: string);
  382. function GetFormatInfo(Format: TImageFormat): TImageFormatInfo;
  383. { Returns set of TImageData formats that can be saved in this file format
  384. without need for conversion.}
  385. function GetSupportedFormats: TImageFormats; virtual;
  386. { Method which must be overridden in descendants if they' are be capable
  387. of loading images. Images are already freed and length is set to zero
  388. whenever this method gets called. Also Handle is assured to be valid
  389. and contains data that passed TestFormat method's check.}
  390. function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray;
  391. OnlyFirstFrame: Boolean): Boolean; virtual;
  392. { Method which must be overridden in descendants if they are be capable
  393. of saving images. Images are checked to have length >0 and
  394. that they contain valid images. For single-image file formats
  395. Index contain valid index to Images array (to image which should be saved).
  396. Multi-image formats should use FFirstIdx and FLastIdx fields to
  397. to get all images that are to be saved.}
  398. function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray;
  399. Index: LongInt): Boolean; virtual;
  400. { This method is called internally by MakeCompatible when input image
  401. is in format not supported by this file format. Image is clone of
  402. MakeCompatible's input and Info is its extended format info.}
  403. procedure ConvertToSupported(var Image: TImageData;
  404. const Info: TImageFormatInfo); virtual;
  405. { Returns True if given image is supported for saving by this file format.
  406. Most file formats don't need to override this method. It checks
  407. (in this base class) if Image's format is in SupportedFormats set.
  408. But you may override it if you want further checks
  409. (proper width and height for example).}
  410. function IsSupported(const Image: TImageData): Boolean; virtual;
  411. public
  412. constructor Create(AMetadata: TMetadata = nil); virtual;
  413. destructor Destroy; override;
  414. { Loads images from file source.}
  415. function LoadFromFile(const FileName: string; var Images: TDynImageDataArray;
  416. OnlyFirstLevel: Boolean = False): Boolean;
  417. { Loads images from stream source.}
  418. function LoadFromStream(Stream: TStream; var Images: TDynImageDataArray;
  419. OnlyFirstLevel: Boolean = False): Boolean;
  420. { Loads images from memory source.}
  421. function LoadFromMemory(Data: Pointer; Size: LongInt;
  422. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean = False): Boolean;
  423. { Saves images to file. If format supports only single level images and
  424. there are multiple images to be saved, they are saved as sequence of
  425. independent images (for example SaveToFile saves sequence of
  426. files img000.jpg, img001.jpg ....).}
  427. function SaveToFile(const FileName: string; const Images: TDynImageDataArray;
  428. OnlyFirstLevel: Boolean = False): Boolean;
  429. { Saves images to stream. If format supports only single level images and
  430. there are multiple images to be saved, they are saved as sequence of
  431. independent images.}
  432. function SaveToStream(Stream: TStream; const Images: TDynImageDataArray;
  433. OnlyFirstLevel: Boolean = False): Boolean;
  434. { Saves images to memory. If format supports only single level images and
  435. there are multiple images to be saved, they are saved as sequence of
  436. independent images. Data must be already allocated and their size passed
  437. as Size parameter, number of written bytes is then returned in the same
  438. parameter.}
  439. function SaveToMemory(Data: Pointer; var Size: LongInt;
  440. const Images: TDynImageDataArray; OnlyFirstLevel: Boolean = False): Boolean;
  441. { Makes Image compatible with this file format (that means it is in one
  442. of data formats in Supported formats set). If input is already
  443. in supported format then Compatible just use value from input
  444. (Compatible := Image) so must not free it after you are done with it
  445. (image bits pointer points to input image's bits).
  446. If input is not in supported format then it is cloned to Compatible
  447. and converted to one of supported formats (what exact format depends on
  448. this file format). If image is cloned MustBeFreed is set to True
  449. to indicated that you must free Compatible after you are done with it.}
  450. function MakeCompatible(const Image: TImageData; var Compatible: TImageData;
  451. out MustBeFreed: Boolean): Boolean;
  452. { Returns True if data located in source identified by Handle
  453. represent valid image in current format.}
  454. function TestFormat(Handle: TImagingHandle): Boolean; virtual;
  455. { Returns True if the given FileName matches filter for this file format.
  456. For most formats it just checks filename extensions.
  457. It uses filename masks in from Masks property so it can recognize
  458. filenames like this 'umajoXXXumajo.j0j' if one of the masks is
  459. 'umajo*umajo.j?j'.}
  460. function TestFileName(const FileName: string): Boolean;
  461. { Descendants use this method to check if their options (registered with
  462. constant Ids for SetOption/GetOption interface or accessible as properties
  463. of descendants) have valid values and make necessary changes.}
  464. procedure CheckOptionsValidity; virtual;
  465. { Description of this format.}
  466. property Name: string read FName;
  467. { Indicates whether images in this format can be loaded.}
  468. property CanLoad: Boolean read GetCanLoad;
  469. { Indicates whether images in this format can be saved.}
  470. property CanSave: Boolean read GetCanSave;
  471. { Indicates whether images in this format can contain multiple image levels.}
  472. property IsMultiImageFormat: Boolean read GetIsMultiImageFormat;
  473. { List of filename extensions for this format.}
  474. property Extensions: TStringList read FExtensions;
  475. { List of filename masks that are used to associate filenames
  476. with TImageFileFormat descendants. Typical mask looks like
  477. '*.bmp' or 'texture.*' (supports file formats which use filename instead
  478. of extension to identify image files).}
  479. property Masks: TStringList read FMasks;
  480. { Set of TImageFormats supported by saving functions of this format. Images
  481. can be saved only in one those formats.}
  482. property SupportedFormats: TImageFormats read GetSupportedFormats;
  483. end;
  484. {$TYPEINFO OFF}
  485. { Class reference for TImageFileFormat class}
  486. TImageFileFormatClass = class of TImageFileFormat;
  487. { Physical resolution unit.}
  488. TResolutionUnit = (
  489. ruSizeInMicroMeters, // value is pixel size in micrometers
  490. ruDpi, // value is pixels/dots per inch
  491. ruDpm, // value is pixels/dots per meter
  492. ruDpcm // value is pixels/dots per centimeter
  493. );
  494. { Class for storage of single metadata item.}
  495. TMetadataItem = class
  496. public
  497. Id: string;
  498. ImageIndex: Integer;
  499. Value: Variant;
  500. end;
  501. { Metadata manager class.}
  502. TMetadata = class
  503. private
  504. FLoadMetaItems: TStringList;
  505. FSaveMetaItems: TStringList;
  506. procedure AddMetaToList(List: TStringList; const Id: string; const Value: Variant; ImageIndex: Integer);
  507. procedure ClearMetaList(List: TStringList);
  508. function GetMetaById(const Id: string): Variant;
  509. function GetMetaByIdMulti(const Id: string; ImageIndex: Integer): Variant;
  510. function GetMetaCount: Integer;
  511. function GetMetaByIdx(Index: Integer): TMetadataItem;
  512. function GetSaveMetaById(const Id: string): Variant;
  513. function GetSaveMetaByIdMulti(const Id: string; ImageIndex: Integer): Variant;
  514. procedure TranslateUnits(ResolutionUnit: TResolutionUnit; var XRes, YRes: Double);
  515. public
  516. constructor Create;
  517. destructor Destroy; override;
  518. procedure SetMetaItem(const Id: string; const Value: Variant; ImageIndex: Integer = 0);
  519. procedure SetMetaItemForSaving(const Id: string; const Value: Variant; ImageIndex: Integer = 0);
  520. function HasMetaItem(const Id: string; ImageIndex: Integer = 0): Boolean;
  521. function HasMetaItemForSaving(const Id: string; ImageIndex: Integer = 0): Boolean;
  522. procedure ClearMetaItems;
  523. procedure ClearMetaItemsForSaving;
  524. function GetMetaItemName(const Id: string; ImageIndex: Integer): string;
  525. { Copies loaded meta items to items-for-save stack. Use this when you want to
  526. save metadata that have been just loaded (e.g. resaving image in
  527. different file format but keeping the metadata).}
  528. procedure CopyLoadedMetaItemsForSaving;
  529. function GetPhysicalPixelSize(ResUnit: TResolutionUnit; out XSize,
  530. YSize: Double; MetaForSave: Boolean = False; ImageIndex: Integer = 0): Boolean;
  531. procedure SetPhysicalPixelSize(ResUnit: TResolutionUnit; XSize, YSize: Double;
  532. MetaForSave: Boolean = False; ImageIndex: Integer = 0);
  533. property MetaItems[const Id: string]: Variant read GetMetaById;
  534. property MetaItemsMulti[const Id: string; ImageIndex: Integer]: Variant read GetMetaByIdMulti;
  535. { Number of loaded metadata items.}
  536. property MetaItemCount: Integer read GetMetaCount;
  537. property MetaItemsByIdx[Index: Integer]: TMetadataItem read GetMetaByIdx;
  538. property MetaItemsForSaving[const Id: string]: Variant read GetSaveMetaById;
  539. property MetaItemsForSavingMulti[const Id: string; ImageIndex: Integer]: Variant read GetSaveMetaByIdMulti;
  540. end;
  541. const
  542. { Metadata item id constants }
  543. { Physical size of one pixel in micrometers. Type of value is Double.}
  544. SMetaPhysicalPixelSizeX = 'PhysicalPixelSizeX';
  545. SMetaPhysicalPixelSizeY = 'PhysicalPixelSizeY';
  546. { Delay for frame of animation (how long it should stay visible) in milliseconds.
  547. Type of value is Integer.}
  548. SMetaFrameDelay = 'FrameDelay';
  549. { Number of times animation should be looped (0 = infinite looping). Type is Int. }
  550. SMetaAnimationLoops = 'AnimationLoops';
  551. { Gamma correction value. Type is Float.}
  552. SMetaGamma = 'Gamma';
  553. { Exposure value for HDR etc. Type is Float.}
  554. SMetaExposure = 'Exposure';
  555. { EXIF image metadata raw blob.}
  556. SMetaExifBlob = 'ExifBlob';
  557. { XMP image metadata raw blob.}
  558. SMetaXmpBlob = 'XmpBlob';
  559. { IPTC image metadata raw blob.}
  560. SMetaIptcBlob = 'IptcBlob';
  561. var
  562. GlobalMetadata: TMetadata;
  563. { Returns symbolic name of given format.}
  564. function GetFormatName(Format: TImageFormat): string;
  565. { Returns string with information about given Image.}
  566. function ImageToStr(const Image: TImageData): string;
  567. { Returns Imaging version string in format 'Major.Minor'.}
  568. function GetVersionStr: string;
  569. { If Condition is True then TruePart is returned, otherwise FalsePart is returned.}
  570. function IffFormat(Condition: Boolean; const TruePart, FalsePart: TImageFormat): TImageFormat;
  571. { Registers new option so it can be used by SetOption and GetOption functions.
  572. Returns True if registration was successful - that is Id is valid and is
  573. not already taken by another option.}
  574. function RegisterOption(OptionId: LongInt; Variable: PLongInt): Boolean;
  575. { Registers new image loader/saver so it can be used by LoadFrom/SaveTo
  576. functions.}
  577. procedure RegisterImageFileFormat(AClass: TImageFileFormatClass);
  578. { Returns image format loader/saver according to a given extension
  579. (case insensitive) or nil if not found. Extension may or may not
  580. contain the initial dot.}
  581. function FindImageFileFormatByExt(const Ext: string): TImageFileFormat;
  582. { Returns image format loader/saver according to a given filename
  583. (case insensitive) or nil if not found. }
  584. function FindImageFileFormatByName(const FileName: string): TImageFileFormat;
  585. { Returns image format loader/saver based on its class
  586. or nil if not found or not registered.}
  587. function FindImageFileFormatByClass(AClass: TImageFileFormatClass): TImageFileFormat;
  588. { Returns number of registered image file format loaders/saver.}
  589. function GetFileFormatCount: LongInt;
  590. { Returns image file format loader/saver at given index. Index must be
  591. in range [0..GetFileFormatCount - 1] otherwise nil is returned.}
  592. function GetFileFormatAtIndex(Index: LongInt): TImageFileFormat;
  593. { Returns filter string for usage with open and save picture dialogs
  594. which contains all registered image file formats.
  595. Set OpenFileFilter to True if you want filter for open dialog
  596. and to False if you want save dialog filter (formats that cannot save to files
  597. are not added then).
  598. For open dialog filter for all known graphic files
  599. (like All(*.jpg;*.png;....) is added too at the first index.}
  600. function GetImageFileFormatsFilter(OpenFileFilter: Boolean): string;
  601. { Returns file extension (without dot) of image format selected
  602. by given filter index. Used filter string is defined by GetImageFileFormatsFilter
  603. function. This function can be used with save dialogs (with filters created
  604. by GetImageFileFormatsFilter) to get the extension of file format selected
  605. in dialog quickly. Index is in range 1..N (as FilterIndex property
  606. of TOpenDialog/TSaveDialog)}
  607. function GetFilterIndexExtension(Index: LongInt; OpenFileFilter: Boolean): string;
  608. { Returns filter index of image file format of file specified by FileName. Used filter
  609. string is defined by GetImageFileFormatsFilter function.
  610. Returned index is in range 1..N (as FilterIndex property of TOpenDialog/TSaveDialog)}
  611. function GetFileNameFilterIndex(const FileName: string; OpenFileFilter: Boolean): LongInt;
  612. { Returns current IO functions.}
  613. function GetIO: TIOFunctions;
  614. { Raises EImagingError with given message.}
  615. procedure RaiseImaging(const Msg: string; const Args: array of const); overload;
  616. procedure RaiseImaging(const Msg: string); overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  617. const
  618. SImagingLibTitle = 'Vampyre Imaging Library';
  619. implementation
  620. uses
  621. {$IFNDEF DONT_LINK_FILE_FORMATS}
  622. {$IFNDEF DONT_LINK_BITMAP}
  623. ImagingBitmap,
  624. {$ENDIF}
  625. {$IFNDEF DONT_LINK_JPEG}
  626. ImagingJpeg,
  627. {$ENDIF}
  628. {$IF not Defined(DONT_LINK_PNG) or not Defined(DONT_LINK_MNG) or not Defined(DONT_LINK_JNG)}
  629. ImagingNetworkGraphics,
  630. {$IFEND}
  631. {$IFNDEF DONT_LINK_GIF}
  632. ImagingGif,
  633. {$ENDIF}
  634. {$IFNDEF DONT_LINK_DDS}
  635. ImagingDds,
  636. {$ENDIF}
  637. {$IFNDEF DONT_LINK_QOI}
  638. ImagingQoi,
  639. {$ENDIF}
  640. {$IFNDEF DONT_LINK_TARGA}
  641. ImagingTarga,
  642. {$ENDIF}
  643. {$IFNDEF DONT_LINK_PNM}
  644. ImagingPortableMaps,
  645. {$ENDIF}
  646. {$IFNDEF DONT_LINK_RADHDR}
  647. ImagingRadiance,
  648. {$ENDIF}
  649. {$IFNDEF DONT_LINK_EXTRAS}
  650. ImagingExtFileFormats,
  651. {$ENDIF}
  652. {$ENDIF}
  653. //ImagingDebug,
  654. ImagingFormats, ImagingUtility, ImagingIO, Variants;
  655. resourcestring
  656. SExceptMsg = 'Exception Message';
  657. SAllFilter = 'All Images';
  658. SUnknownFormat = 'Unknown and unsupported format';
  659. SErrorFreeImage = 'Error while freeing image. %s';
  660. SErrorCloneImage = 'Error while cloning image. %s';
  661. SErrorFlipImage = 'Error while flipping image. %s';
  662. SErrorMirrorImage = 'Error while mirroring image. %s';
  663. SErrorResizeImage = 'Error while resizing image. %s';
  664. SErrorSwapImage = 'Error while swapping channels of image. %s';
  665. SFileFormatCanNotLoad = 'Image Format "%s" does not support loading images.';
  666. SFileFormatCanNotSave = 'Image Format "%s" does not support saving images.';
  667. SErrorNewImage = 'Error while creating image data with params: Width=%d ' +
  668. 'Height=%d Format=%s.';
  669. SErrorConvertImage = 'Error while converting image to format "%s". %s';
  670. SImageInfo = 'Image @%p info: Width = %dpx, Height = %dpx, ' +
  671. 'Format = %s, Size = %.0n %s, Bits @%p, Palette @%p.';
  672. SImageInfoInvalid = 'Access violation encountered when getting info on ' +
  673. 'image at address %p.';
  674. SFileNotValid = 'File "%s" is not valid image in "%s" format.';
  675. SStreamNotValid = 'Stream %p does not contain valid image in "%s" format.';
  676. SMemoryNotValid = 'Memory %p (%d Bytes) does not contain valid image ' +
  677. 'in "%s" format.';
  678. SErrorLoadingFile = 'Error while loading images from file "%s" (file format: %s).';
  679. SErrorLoadingStream = 'Error while loading images from stream %p (file format: %s).';
  680. SErrorLoadingMemory = 'Error while loading images from memory %p (%d Bytes) (file format: %s).';
  681. SErrorSavingFile = 'Error while saving images to file "%s" (file format: %s).';
  682. SErrorSavingStream = 'Error while saving images to stream %p (file format: %s).';
  683. SErrorSavingMemory = 'Error while saving images to memory %p (%d Bytes) (file format: %s).';
  684. SErrorFindColor = 'Error while finding color in palette @%p with %d entries.';
  685. SErrorGrayscalePalette = 'Error while filling grayscale palette @%p with %d entries.';
  686. SErrorCustomPalette = 'Error while filling custom palette @%p with %d entries.';
  687. SErrorSwapPalette = 'Error while swapping channels of palette @%p with %d entries.';
  688. SErrorReduceColors = 'Error while reducing number of colors of image to %d. %s';
  689. SErrorGenerateMipMaps = 'Error while generating %d mipmap levels for image %s';
  690. SImagesNotValid = 'One or more images are not valid.';
  691. SErrorCopyRect = 'Error while copying rect from image %s to image %s.';
  692. SErrorMapImage = 'Error while mapping image %s to palette.';
  693. SErrorFillRect = 'Error while filling rectangle X:%d Y:%d W:%d H:%d in image %s';
  694. SErrorSplitImage = 'Error while splitting image %s to %dx%d sized chunks.';
  695. SErrorMakePaletteForImages = 'Error while making %d color palette for %d images.';
  696. SErrorNewPalette = 'Error while creating new palette with %d entries';
  697. SErrorFreePalette = 'Error while freeing palette @%p';
  698. SErrorCopyPalette = 'Error while copying %d entries from palette @%p to @%p';
  699. SErrorReplaceColor = 'Error while replacing colors in rectangle X:%d Y:%d W:%d H:%d of image %s';
  700. SErrorRotateImage = 'Error while rotating image %s by %.2n degrees';
  701. SErrorStretchRect = 'Error while stretching rect from image %s to image %s.';
  702. SErrorEmptyStream = 'Input stream has no data. Check Position property.';
  703. SErrorInvalidInputImage = 'Invalid input image.';
  704. SErrorBadImage = 'Bad image detected.';
  705. const
  706. // Initial size of array with options information
  707. InitialOptions = 256;
  708. // Max depth of the option stack
  709. OptionStackDepth = 8;
  710. // Do not change the default format now, its too late
  711. DefaultImageFormat: TImageFormat = ifA8R8G8B8;
  712. // Format used to create metadata IDs for frames loaded from multi-images.
  713. SMetaIdForSubImage = '%s/%d';
  714. type
  715. TOptionArray = array of PLongInt;
  716. TOptionValueArray = array of LongInt;
  717. TOptionStack = class(TObject)
  718. private
  719. FStack: array[0..OptionStackDepth - 1] of TOptionValueArray;
  720. FPosition: LongInt;
  721. public
  722. constructor Create;
  723. destructor Destroy; override;
  724. function Push: Boolean;
  725. function Pop: Boolean;
  726. end;
  727. var
  728. // Currently set IO functions
  729. IO: TIOFunctions;
  730. // List with all registered TImageFileFormat classes
  731. ImageFileFormats: TList = nil;
  732. // Array with registered options (pointers to their values)
  733. Options: TOptionArray = nil;
  734. // Array containing additional information about every image format
  735. ImageFormatInfos: TImageFormatInfoArray;
  736. // Stack used by PushOptions/PopOptions functions
  737. OptionStack: TOptionStack = nil;
  738. var
  739. // Variable for ImagingColorReduction option
  740. ColorReductionMask: LongInt = $FF;
  741. // Variable for ImagingLoadOverrideFormat option
  742. LoadOverrideFormat: TImageFormat = ifUnknown;
  743. // Variable for ImagingSaveOverrideFormat option
  744. SaveOverrideFormat: TImageFormat = ifUnknown;
  745. // Variable for ImagingSaveOverrideFormat option
  746. MipMapFilter: TSamplingFilter = sfLinear;
  747. // Variable for ImagingBinaryThreshold option
  748. BinaryThreshold: Integer = 128;
  749. { Exceptions }
  750. constructor EImagingBadImage.Create;
  751. begin
  752. inherited Create(SErrorBadImage);
  753. end;
  754. { Internal unit functions }
  755. { Modifies option value to be in the allowed range. Works only
  756. for options registered in this unit.}
  757. function CheckOptionValue(OptionId, Value: LongInt): LongInt; forward;
  758. { Sets IO functions to file IO.}
  759. procedure SetFileIO; forward;
  760. { Sets IO functions to stream IO.}
  761. procedure SetStreamIO; forward;
  762. { Sets IO functions to memory IO.}
  763. procedure SetMemoryIO; forward;
  764. { Inits image format infos array.}
  765. procedure InitImageFormats; forward;
  766. { Free image format infos array.}
  767. procedure FreeImageFileFormats; forward;
  768. { Creates options array and stack.}
  769. procedure InitOptions; forward;
  770. { Frees options array and stack.}
  771. procedure FreeOptions; forward;
  772. function UpdateExceptMessage(E: Exception; const MsgToPrepend: string; const Args: array of const): Exception;
  773. begin
  774. Result := E;
  775. E.Message := Format(MsgToPrepend, Args) + ' ' + SExceptMsg + ': ' + E.Message
  776. end;
  777. { ------------------------------------------------------------------------
  778. Low Level Interface Functions
  779. ------------------------------------------------------------------------}
  780. { General Functions }
  781. procedure InitImage(out Image: TImageData);
  782. begin
  783. FillChar(Image, SizeOf(Image), 0);
  784. end;
  785. function NewImage(Width, Height: LongInt; Format: TImageFormat; var Image:
  786. TImageData): Boolean;
  787. var
  788. FInfo: PImageFormatInfo;
  789. begin
  790. Assert((Width > 0) and (Height >0));
  791. Assert(IsImageFormatValid(Format));
  792. Result := False;
  793. FreeImage(Image);
  794. try
  795. Image.Width := Width;
  796. Image.Height := Height;
  797. // Select default data format if selected
  798. if (Format = ifDefault) then
  799. Image.Format := DefaultImageFormat
  800. else
  801. Image.Format := Format;
  802. // Get extended format info
  803. FInfo := ImageFormatInfos[Image.Format];
  804. if FInfo = nil then
  805. begin
  806. InitImage(Image);
  807. Exit;
  808. end;
  809. // Check image dimensions and calculate its size in bytes
  810. FInfo.CheckDimensions(FInfo.Format, Image.Width, Image.Height);
  811. Image.Size := FInfo.GetPixelsSize(FInfo.Format, Image.Width, Image.Height);
  812. if Image.Size <= 0 then
  813. begin
  814. InitImage(Image);
  815. Exit;
  816. end;
  817. // Image bits are allocated and set to zeroes
  818. Image.Bits := AllocMem(Image.Size);
  819. // Palette is allocated and set to zeroes
  820. if FInfo.PaletteEntries > 0 then
  821. Image.Palette := AllocMem(FInfo.PaletteEntries * SizeOf(TColor32Rec));
  822. Result := TestImage(Image);
  823. except
  824. on E: Exception do
  825. begin
  826. FreeMem(Image.Bits);
  827. FreeMem(Image.Palette);
  828. InitImage(Image);
  829. raise UpdateExceptMessage(E, SErrorNewImage, [Width, Height, GetFormatName(Format)]);
  830. end;
  831. end;
  832. end;
  833. function TestImage(const Image: TImageData): Boolean;
  834. begin
  835. try
  836. Result := (LongInt(Image.Format) >= LongInt(Low(TImageFormat))) and
  837. (LongInt(Image.Format) <= LongInt(High(TImageFormat))) and
  838. (ImageFormatInfos[Image.Format] <> nil) and
  839. (Assigned(ImageFormatInfos[Image.Format].GetPixelsSize) and
  840. (ImageFormatInfos[Image.Format].GetPixelsSize(Image.Format,
  841. Image.Width, Image.Height) = Image.Size));
  842. except
  843. // Possible int overflows or other errors
  844. Result := False;
  845. end;
  846. end;
  847. procedure FreeImage(var Image: TImageData);
  848. begin
  849. try
  850. if TestImage(Image) then
  851. begin
  852. FreeMemNil(Image.Bits);
  853. FreeMemNil(Image.Palette);
  854. end;
  855. InitImage(Image);
  856. except
  857. raise UpdateExceptMessage(GetExceptObject, SErrorFreeImage, [ImageToStr(Image)]);
  858. end;
  859. end;
  860. procedure FreeImagesInArray(var Images: TDynImageDataArray);
  861. var
  862. I: LongInt;
  863. begin
  864. if Length(Images) > 0 then
  865. begin
  866. for I := 0 to Length(Images) - 1 do
  867. FreeImage(Images[I]);
  868. SetLength(Images, 0);
  869. end;
  870. end;
  871. function TestImagesInArray(const Images: TDynImageDataArray): Boolean;
  872. var
  873. I: LongInt;
  874. begin
  875. if Length(Images) > 0 then
  876. begin
  877. Result := True;
  878. for I := 0 to Length(Images) - 1 do
  879. begin
  880. Result := Result and TestImage(Images[I]);
  881. if not Result then
  882. Break;
  883. end;
  884. end
  885. else
  886. Result := False;
  887. end;
  888. function DetermineFileFormat(const FileName: string): string;
  889. var
  890. I: LongInt;
  891. Fmt: TImageFileFormat;
  892. Handle: TImagingHandle;
  893. begin
  894. Assert(FileName <> '');
  895. Result := '';
  896. SetFileIO;
  897. Handle := IO.Open(PChar(FileName), omReadOnly);
  898. try
  899. // First file format according to FileName and test if the data in
  900. // file is really in that format
  901. for I := 0 to ImageFileFormats.Count - 1 do
  902. begin
  903. Fmt := TImageFileFormat(ImageFileFormats[I]);
  904. if Fmt.TestFileName(FileName) and Fmt.TestFormat(Handle) then
  905. begin
  906. Result := Fmt.Extensions[0];
  907. Exit;
  908. end;
  909. end;
  910. // No file format was found with filename search so try data-based search
  911. for I := 0 to ImageFileFormats.Count - 1 do
  912. begin
  913. Fmt := TImageFileFormat(ImageFileFormats[I]);
  914. if Fmt.TestFormat(Handle) then
  915. begin
  916. Result := Fmt.Extensions[0];
  917. Exit;
  918. end;
  919. end;
  920. finally
  921. IO.Close(Handle);
  922. end;
  923. end;
  924. function DetermineStreamFormat(Stream: TStream): string;
  925. var
  926. I: LongInt;
  927. Fmt: TImageFileFormat;
  928. Handle: TImagingHandle;
  929. begin
  930. Assert(Stream <> nil);
  931. Result := '';
  932. SetStreamIO;
  933. Handle := IO.Open(Pointer(Stream), omReadOnly);
  934. try
  935. for I := 0 to ImageFileFormats.Count - 1 do
  936. begin
  937. Fmt := TImageFileFormat(ImageFileFormats[I]);
  938. if Fmt.TestFormat(Handle) then
  939. begin
  940. Result := Fmt.Extensions[0];
  941. Exit;
  942. end;
  943. end;
  944. finally
  945. IO.Close(Handle);
  946. end;
  947. end;
  948. function DetermineMemoryFormat(Data: Pointer; Size: LongInt): string;
  949. var
  950. I: LongInt;
  951. Fmt: TImageFileFormat;
  952. Handle: TImagingHandle;
  953. IORec: TMemoryIORec;
  954. begin
  955. Assert((Data <> nil) and (Size > 0));
  956. Result := '';
  957. SetMemoryIO;
  958. IORec.Data := Data;
  959. IORec.Position := 0;
  960. IORec.Size := Size;
  961. Handle := IO.Open(@IORec, omReadOnly);
  962. try
  963. for I := 0 to ImageFileFormats.Count - 1 do
  964. begin
  965. Fmt := TImageFileFormat(ImageFileFormats[I]);
  966. if Fmt.TestFormat(Handle) then
  967. begin
  968. Result := Fmt.Extensions[0];
  969. Exit;
  970. end;
  971. end;
  972. finally
  973. IO.Close(Handle);
  974. end;
  975. end;
  976. function IsFileFormatSupported(const FileName: string): Boolean;
  977. begin
  978. Result := FindImageFileFormatByName(FileName) <> nil;
  979. end;
  980. function EnumFileFormats(var Index: LongInt; var Name, DefaultExt, Masks: string;
  981. var CanSaveImages, IsMultiImageFormat: Boolean): Boolean;
  982. var
  983. FileFmt: TImageFileFormat;
  984. begin
  985. FileFmt := GetFileFormatAtIndex(Index);
  986. Result := FileFmt <> nil;
  987. if Result then
  988. begin
  989. Name := FileFmt.Name;
  990. DefaultExt := FileFmt.Extensions[0];
  991. Masks := FileFmt.Masks.DelimitedText;
  992. CanSaveImages := FileFmt.CanSave;
  993. IsMultiImageFormat := FileFmt.IsMultiImageFormat;
  994. Inc(Index);
  995. end
  996. else
  997. begin
  998. Name := '';
  999. DefaultExt := '';
  1000. Masks := '';
  1001. CanSaveImages := False;
  1002. IsMultiImageFormat := False;
  1003. end;
  1004. end;
  1005. { Loading Functions }
  1006. function LoadImageFromFile(const FileName: string; var Image: TImageData):
  1007. Boolean;
  1008. var
  1009. Format: TImageFileFormat;
  1010. IArray: TDynImageDataArray;
  1011. I: LongInt;
  1012. begin
  1013. Assert(FileName <> '');
  1014. Result := False;
  1015. Format := FindImageFileFormatByExt(DetermineFileFormat(FileName));
  1016. if Format <> nil then
  1017. begin
  1018. FreeImage(Image);
  1019. Result := Format.LoadFromFile(FileName, IArray, True);
  1020. if Result and (Length(IArray) > 0) then
  1021. begin
  1022. Image := IArray[0];
  1023. for I := 1 to Length(IArray) - 1 do
  1024. FreeImage(IArray[I]);
  1025. end
  1026. else
  1027. Result := False;
  1028. end;
  1029. end;
  1030. function LoadImageFromStream(Stream: TStream; var Image: TImageData): Boolean;
  1031. var
  1032. Format: TImageFileFormat;
  1033. IArray: TDynImageDataArray;
  1034. I: LongInt;
  1035. begin
  1036. Assert(Stream <> nil);
  1037. if Stream.Size - Stream.Position = 0 then
  1038. RaiseImaging(SErrorEmptyStream, []);
  1039. Result := False;
  1040. Format := FindImageFileFormatByExt(DetermineStreamFormat(Stream));
  1041. if Format <> nil then
  1042. begin
  1043. FreeImage(Image);
  1044. Result := Format.LoadFromStream(Stream, IArray, True);
  1045. if Result and (Length(IArray) > 0) then
  1046. begin
  1047. Image := IArray[0];
  1048. for I := 1 to Length(IArray) - 1 do
  1049. FreeImage(IArray[I]);
  1050. end
  1051. else
  1052. Result := False;
  1053. end;
  1054. end;
  1055. function LoadImageFromMemory(Data: Pointer; Size: LongInt; var Image: TImageData): Boolean;
  1056. var
  1057. Format: TImageFileFormat;
  1058. IArray: TDynImageDataArray;
  1059. I: LongInt;
  1060. begin
  1061. Assert((Data <> nil) and (Size > 0));
  1062. Result := False;
  1063. Format := FindImageFileFormatByExt(DetermineMemoryFormat(Data, Size));
  1064. if Format <> nil then
  1065. begin
  1066. FreeImage(Image);
  1067. Result := Format.LoadFromMemory(Data, Size, IArray, True);
  1068. if Result and (Length(IArray) > 0) then
  1069. begin
  1070. Image := IArray[0];
  1071. for I := 1 to Length(IArray) - 1 do
  1072. FreeImage(IArray[I]);
  1073. end
  1074. else
  1075. Result := False;
  1076. end;
  1077. end;
  1078. function LoadMultiImageFromFile(const FileName: string; var Images:
  1079. TDynImageDataArray): Boolean;
  1080. var
  1081. Format: TImageFileFormat;
  1082. begin
  1083. Assert(FileName <> '');
  1084. Result := False;
  1085. Format := FindImageFileFormatByExt(DetermineFileFormat(FileName));
  1086. if Format <> nil then
  1087. begin
  1088. FreeImagesInArray(Images);
  1089. Result := Format.LoadFromFile(FileName, Images);
  1090. end;
  1091. end;
  1092. function LoadMultiImageFromStream(Stream: TStream; var Images: TDynImageDataArray): Boolean;
  1093. var
  1094. Format: TImageFileFormat;
  1095. begin
  1096. Assert(Stream <> nil);
  1097. if Stream.Size - Stream.Position = 0 then
  1098. RaiseImaging(SErrorEmptyStream, []);
  1099. Result := False;
  1100. Format := FindImageFileFormatByExt(DetermineStreamFormat(Stream));
  1101. if Format <> nil then
  1102. begin
  1103. FreeImagesInArray(Images);
  1104. Result := Format.LoadFromStream(Stream, Images);
  1105. end;
  1106. end;
  1107. function LoadMultiImageFromMemory(Data: Pointer; Size: LongInt;
  1108. var Images: TDynImageDataArray): Boolean;
  1109. var
  1110. Format: TImageFileFormat;
  1111. begin
  1112. Assert((Data <> nil) and (Size > 0));
  1113. Result := False;
  1114. Format := FindImageFileFormatByExt(DetermineMemoryFormat(Data, Size));
  1115. if Format <> nil then
  1116. begin
  1117. FreeImagesInArray(Images);
  1118. Result := Format.LoadFromMemory(Data, Size, Images);
  1119. end;
  1120. end;
  1121. { Saving Functions }
  1122. function SaveImageToFile(const FileName: string; const Image: TImageData): Boolean;
  1123. var
  1124. Format: TImageFileFormat;
  1125. IArray: TDynImageDataArray;
  1126. begin
  1127. Assert(FileName <> '');
  1128. Result := False;
  1129. Format := FindImageFileFormatByName(FileName);
  1130. if Format <> nil then
  1131. begin
  1132. SetLength(IArray, 1);
  1133. IArray[0] := Image;
  1134. Result := Format.SaveToFile(FileName, IArray, True);
  1135. end;
  1136. end;
  1137. function SaveImageToStream(const Ext: string; Stream: TStream;
  1138. const Image: TImageData): Boolean;
  1139. var
  1140. Format: TImageFileFormat;
  1141. IArray: TDynImageDataArray;
  1142. begin
  1143. Assert((Ext <> '') and (Stream <> nil));
  1144. Result := False;
  1145. Format := FindImageFileFormatByExt(Ext);
  1146. if Format <> nil then
  1147. begin
  1148. SetLength(IArray, 1);
  1149. IArray[0] := Image;
  1150. Result := Format.SaveToStream(Stream, IArray, True);
  1151. end;
  1152. end;
  1153. function SaveImageToMemory(const Ext: string; Data: Pointer; var Size: LongInt;
  1154. const Image: TImageData): Boolean;
  1155. var
  1156. Format: TImageFileFormat;
  1157. IArray: TDynImageDataArray;
  1158. begin
  1159. Assert((Ext <> '') and (Data <> nil) and (Size > 0));
  1160. Result := False;
  1161. Format := FindImageFileFormatByExt(Ext);
  1162. if Format <> nil then
  1163. begin
  1164. SetLength(IArray, 1);
  1165. IArray[0] := Image;
  1166. Result := Format.SaveToMemory(Data, Size, IArray, True);
  1167. end;
  1168. end;
  1169. function SaveMultiImageToFile(const FileName: string;
  1170. const Images: TDynImageDataArray): Boolean;
  1171. var
  1172. Format: TImageFileFormat;
  1173. begin
  1174. Assert(FileName <> '');
  1175. Result := False;
  1176. Format := FindImageFileFormatByName(FileName);
  1177. if Format <> nil then
  1178. Result := Format.SaveToFile(FileName, Images);
  1179. end;
  1180. function SaveMultiImageToStream(const Ext: string; Stream: TStream;
  1181. const Images: TDynImageDataArray): Boolean;
  1182. var
  1183. Format: TImageFileFormat;
  1184. begin
  1185. Assert((Ext <> '') and (Stream <> nil));
  1186. Result := False;
  1187. Format := FindImageFileFormatByExt(Ext);
  1188. if Format <> nil then
  1189. Result := Format.SaveToStream(Stream, Images);
  1190. end;
  1191. function SaveMultiImageToMemory(const Ext: string; Data: Pointer;
  1192. var Size: LongInt; const Images: TDynImageDataArray): Boolean;
  1193. var
  1194. Format: TImageFileFormat;
  1195. begin
  1196. Assert((Ext <> '') and (Data <> nil) and (Size > 0));
  1197. Result := False;
  1198. Format := FindImageFileFormatByExt(Ext);
  1199. if Format <> nil then
  1200. Result := Format.SaveToMemory(Data, Size, Images);
  1201. end;
  1202. { Manipulation Functions }
  1203. function CloneImage(const Image: TImageData; var Clone: TImageData): Boolean;
  1204. var
  1205. Info: PImageFormatInfo;
  1206. begin
  1207. Result := False;
  1208. if TestImage(Image) then
  1209. try
  1210. if TestImage(Clone) and (Image.Bits <> Clone.Bits) then
  1211. FreeImage(Clone)
  1212. else
  1213. InitImage(Clone);
  1214. Info := ImageFormatInfos[Image.Format];
  1215. Clone.Width := Image.Width;
  1216. Clone.Height := Image.Height;
  1217. Clone.Format := Image.Format;
  1218. Clone.Size := Image.Size;
  1219. if Info.PaletteEntries > 0 then
  1220. begin
  1221. GetMem(Clone.Palette, Info.PaletteEntries * SizeOf(TColor32Rec));
  1222. Move(Image.Palette^, Clone.Palette^, Info.PaletteEntries *
  1223. SizeOf(TColor32Rec));
  1224. end;
  1225. GetMem(Clone.Bits, Clone.Size);
  1226. Move(Image.Bits^, Clone.Bits^, Clone.Size);
  1227. Result := True;
  1228. except
  1229. raise UpdateExceptMessage(GetExceptObject, SErrorCloneImage, [ImageToStr(Image)]);
  1230. end;
  1231. end;
  1232. function ConvertImage(var Image: TImageData; DestFormat: TImageFormat): Boolean;
  1233. var
  1234. NewData: Pointer;
  1235. NewPal: PPalette32;
  1236. NewSize, NumPixels: Int64;
  1237. SrcInfo, DstInfo: PImageFormatInfo;
  1238. begin
  1239. Assert(IsImageFormatValid(DestFormat));
  1240. Result := False;
  1241. if TestImage(Image) then
  1242. with Image do
  1243. try
  1244. // If default format is set we use DefaultImageFormat
  1245. if DestFormat = ifDefault then
  1246. DestFormat := DefaultImageFormat;
  1247. SrcInfo := ImageFormatInfos[Format];
  1248. DstInfo := ImageFormatInfos[DestFormat];
  1249. if SrcInfo = DstInfo then
  1250. begin
  1251. // There is nothing to convert - src is already in dest format
  1252. Result := True;
  1253. Exit;
  1254. end;
  1255. // Exit Src or Dest format is invalid
  1256. if (SrcInfo = nil) or (DstInfo = nil) then Exit;
  1257. // If dest format is just src with swapped channels we call
  1258. // SwapChannels instead
  1259. if (SrcInfo.RBSwapFormat = DestFormat) and
  1260. (DstInfo.RBSwapFormat = SrcInfo.Format) then
  1261. begin
  1262. Result := SwapChannels(Image, ChannelRed, ChannelBlue);
  1263. Image.Format := SrcInfo.RBSwapFormat;
  1264. Exit;
  1265. end;
  1266. if (not SrcInfo.IsSpecial) and (not DstInfo.IsSpecial) then
  1267. begin
  1268. NumPixels := Int64(Width) * Height;
  1269. NewSize := NumPixels * DstInfo.BytesPerPixel;
  1270. NewData := AllocMem(NewSize);
  1271. NewPal := AllocMem(DstInfo.PaletteEntries * SizeOf(TColor32Rec));
  1272. if SrcInfo.IsIndexed then
  1273. begin
  1274. // Source: indexed format
  1275. if DstInfo.IsIndexed then
  1276. IndexToIndex(NumPixels, Bits, NewData, SrcInfo, DstInfo, Palette, NewPal)
  1277. else if DstInfo.HasGrayChannel then
  1278. IndexToGray(NumPixels, Bits, NewData, SrcInfo, DstInfo, Palette)
  1279. else if DstInfo.IsFloatingPoint then
  1280. IndexToFloat(NumPixels, Bits, NewData, SrcInfo, DstInfo, Palette)
  1281. else
  1282. IndexToChannel(NumPixels, Bits, NewData, SrcInfo, DstInfo, Palette);
  1283. end
  1284. else if SrcInfo.HasGrayChannel then
  1285. begin
  1286. // Source: grayscale format
  1287. if DstInfo.IsIndexed then
  1288. GrayToIndex(NumPixels, Bits, NewData, SrcInfo, DstInfo, NewPal)
  1289. else if DstInfo.HasGrayChannel then
  1290. GrayToGray(NumPixels, Bits, NewData, SrcInfo, DstInfo)
  1291. else if DstInfo.IsFloatingPoint then
  1292. GrayToFloat(NumPixels, Bits, NewData, SrcInfo, DstInfo)
  1293. else
  1294. GrayToChannel(NumPixels, Bits, NewData, SrcInfo, DstInfo);
  1295. end
  1296. else if SrcInfo.IsFloatingPoint then
  1297. begin
  1298. // Source: floating point format
  1299. if DstInfo.IsIndexed then
  1300. FloatToIndex(NumPixels, Bits, NewData, SrcInfo, DstInfo, NewPal)
  1301. else if DstInfo.HasGrayChannel then
  1302. FloatToGray(NumPixels, Bits, NewData, SrcInfo, DstInfo)
  1303. else if DstInfo.IsFloatingPoint then
  1304. FloatToFloat(NumPixels, Bits, NewData, SrcInfo, DstInfo)
  1305. else
  1306. FloatToChannel(NumPixels, Bits, NewData, SrcInfo, DstInfo);
  1307. end
  1308. else
  1309. begin
  1310. // Source: standard multi channel image
  1311. if DstInfo.IsIndexed then
  1312. ChannelToIndex(NumPixels, Bits, NewData, SrcInfo, DstInfo, NewPal)
  1313. else if DstInfo.HasGrayChannel then
  1314. ChannelToGray(NumPixels, Bits, NewData, SrcInfo, DstInfo)
  1315. else if DstInfo.IsFloatingPoint then
  1316. ChannelToFloat(NumPixels, Bits, NewData, SrcInfo, DstInfo)
  1317. else
  1318. ChannelToChannel(NumPixels, Bits, NewData, SrcInfo, DstInfo);
  1319. end;
  1320. FreeMemNil(Bits);
  1321. FreeMemNil(Palette);
  1322. Format := DestFormat;
  1323. Bits := NewData;
  1324. Size := NewSize;
  1325. Palette := NewPal;
  1326. end
  1327. else
  1328. ConvertSpecial(Image, SrcInfo, DstInfo);
  1329. Assert(SrcInfo.Format <> Image.Format);
  1330. Result := True;
  1331. except
  1332. raise UpdateExceptMessage(GetExceptObject, SErrorConvertImage, [GetFormatName(DestFormat), ImageToStr(Image)]);
  1333. end;
  1334. end;
  1335. function FlipImage(var Image: TImageData): Boolean;
  1336. var
  1337. P1, P2, Buff: Pointer;
  1338. WidthBytes, I: LongInt;
  1339. OldFmt: TImageFormat;
  1340. begin
  1341. Result := False;
  1342. OldFmt := Image.Format;
  1343. if TestImage(Image) then
  1344. with Image do
  1345. try
  1346. if ImageFormatInfos[OldFmt].IsSpecial then
  1347. ConvertImage(Image, ifDefault);
  1348. WidthBytes := Width * ImageFormatInfos[Format].BytesPerPixel;
  1349. GetMem(Buff, WidthBytes);
  1350. try
  1351. // Swap all scanlines of image
  1352. for I := 0 to Height div 2 - 1 do
  1353. begin
  1354. P1 := @PByteArray(Bits)[I * WidthBytes];
  1355. P2 := @PByteArray(Bits)[(Height - I - 1) * WidthBytes];
  1356. Move(P1^, Buff^, WidthBytes);
  1357. Move(P2^, P1^, WidthBytes);
  1358. Move(Buff^, P2^, WidthBytes);
  1359. end;
  1360. finally
  1361. FreeMemNil(Buff);
  1362. end;
  1363. if OldFmt <> Format then
  1364. ConvertImage(Image, OldFmt);
  1365. Result := True;
  1366. except
  1367. RaiseImaging(SErrorFlipImage, [ImageToStr(Image)]);
  1368. end;
  1369. end;
  1370. function MirrorImage(var Image: TImageData): Boolean;
  1371. var
  1372. Scanline: PByte;
  1373. Buff: TColorFPRec;
  1374. Bpp, Y, X, WidthDiv2, WidthBytes, XLeft, XRight: LongInt;
  1375. OldFmt: TImageFormat;
  1376. begin
  1377. Result := False;
  1378. OldFmt := Image.Format;
  1379. if TestImage(Image) then
  1380. with Image do
  1381. try
  1382. if ImageFormatInfos[OldFmt].IsSpecial then
  1383. ConvertImage(Image, ifDefault);
  1384. Bpp := ImageFormatInfos[Format].BytesPerPixel;
  1385. WidthDiv2 := Width div 2;
  1386. WidthBytes := Width * Bpp;
  1387. // Mirror all pixels on each scanline of image
  1388. for Y := 0 to Height - 1 do
  1389. begin
  1390. Scanline := @PByteArray(Bits)[Y * WidthBytes];
  1391. XLeft := 0;
  1392. XRight := (Width - 1) * Bpp;
  1393. for X := 0 to WidthDiv2 - 1 do
  1394. begin
  1395. CopyPixel(@PByteArray(Scanline)[XLeft], @Buff, Bpp);
  1396. CopyPixel(@PByteArray(Scanline)[XRight],
  1397. @PByteArray(Scanline)[XLeft], Bpp);
  1398. CopyPixel(@Buff, @PByteArray(Scanline)[XRight], Bpp);
  1399. Inc(XLeft, Bpp);
  1400. Dec(XRight, Bpp);
  1401. end;
  1402. end;
  1403. if OldFmt <> Format then
  1404. ConvertImage(Image, OldFmt);
  1405. Result := True;
  1406. except
  1407. RaiseImaging(SErrorMirrorImage, [ImageToStr(Image)]);
  1408. end;
  1409. end;
  1410. function ResizeImage(var Image: TImageData; NewWidth, NewHeight: LongInt;
  1411. Filter: TResizeFilter): Boolean;
  1412. var
  1413. WorkImage: TImageData;
  1414. begin
  1415. Assert((NewWidth > 0) and (NewHeight > 0), 'New width or height is zero.');
  1416. Result := False;
  1417. if TestImage(Image) and ((Image.Width <> NewWidth) or (Image.Height <> NewHeight)) then
  1418. try
  1419. InitImage(WorkImage);
  1420. // Create new image with desired dimensions
  1421. NewImage(NewWidth, NewHeight, Image.Format, WorkImage);
  1422. // Stretch pixels from old image to new one
  1423. StretchRect(Image, 0, 0, Image.Width, Image.Height,
  1424. WorkImage, 0, 0, WorkImage.Width, WorkImage.Height, Filter);
  1425. // Free old image and assign new image to it
  1426. FreeMemNil(Image.Bits);
  1427. if Image.Palette <> nil then
  1428. begin
  1429. FreeMem(WorkImage.Palette);
  1430. WorkImage.Palette := Image.Palette;
  1431. end;
  1432. Image := WorkImage;
  1433. Result := True;
  1434. except
  1435. raise UpdateExceptMessage(GetExceptObject, SErrorResizeImage, [ImageToStr(Image)]);
  1436. end;
  1437. end;
  1438. function SwapChannels(var Image: TImageData; SrcChannel, DstChannel: LongInt): Boolean;
  1439. var
  1440. I, NumPixels: NativeInt;
  1441. Info: PImageFormatInfo;
  1442. Swap, Alpha: Word;
  1443. Data: PByte;
  1444. Pix64: TColor64Rec;
  1445. PixF: TColorFPRec;
  1446. SwapF: Single;
  1447. begin
  1448. Assert((SrcChannel in [0..3]) and (DstChannel in [0..3]));
  1449. Result := False;
  1450. if TestImage(Image) and (SrcChannel <> DstChannel) then
  1451. with Image do
  1452. try
  1453. NumPixels := NativeInt(Width) * Height;
  1454. Info := ImageFormatInfos[Format];
  1455. Data := Bits;
  1456. if (Info.Format = ifR8G8B8) or ((Info.Format = ifA8R8G8B8) and
  1457. (SrcChannel <> ChannelAlpha) and (DstChannel <> ChannelAlpha)) then
  1458. begin
  1459. // Swap channels of most common formats R8G8B8 and A8R8G8B8 (no alpha)
  1460. for I := 0 to NumPixels - 1 do
  1461. with PColor24Rec(Data)^ do
  1462. begin
  1463. Swap := Channels[SrcChannel];
  1464. Channels[SrcChannel] := Channels[DstChannel];
  1465. Channels[DstChannel] := Swap;
  1466. Inc(Data, Info.BytesPerPixel);
  1467. end;
  1468. end
  1469. else if Info.IsIndexed then
  1470. begin
  1471. // Swap palette channels of indexed images
  1472. SwapChannelsOfPalette(Palette, Info.PaletteEntries, SrcChannel, DstChannel)
  1473. end
  1474. else if Info.IsFloatingPoint then
  1475. begin
  1476. // Swap channels of floating point images
  1477. for I := 0 to NumPixels - 1 do
  1478. begin
  1479. FloatGetSrcPixel(Data, Info, PixF);
  1480. with PixF do
  1481. begin
  1482. SwapF := Channels[SrcChannel];
  1483. Channels[SrcChannel] := Channels[DstChannel];
  1484. Channels[DstChannel] := SwapF;
  1485. end;
  1486. FloatSetDstPixel(Data, Info, PixF);
  1487. Inc(Data, Info.BytesPerPixel);
  1488. end;
  1489. end
  1490. else if Info.IsSpecial then
  1491. begin
  1492. // Swap channels of special format images
  1493. ConvertImage(Image, ifDefault);
  1494. SwapChannels(Image, SrcChannel, DstChannel);
  1495. ConvertImage(Image, Info.Format);
  1496. end
  1497. else if Info.HasGrayChannel and Info.HasAlphaChannel and
  1498. ((SrcChannel = ChannelAlpha) or (DstChannel = ChannelAlpha)) then
  1499. begin
  1500. for I := 0 to NumPixels - 1 do
  1501. begin
  1502. // If we have grayscale image with alpha and alpha is channel
  1503. // to be swapped, we swap it. No other alternative for gray images,
  1504. // just alpha and something
  1505. GrayGetSrcPixel(Data, Info, Pix64, Alpha);
  1506. Swap := Alpha;
  1507. Alpha := Pix64.A;
  1508. Pix64.A := Swap;
  1509. GraySetDstPixel(Data, Info, Pix64, Alpha);
  1510. Inc(Data, Info.BytesPerPixel);
  1511. end;
  1512. end
  1513. else
  1514. begin
  1515. // Then do general swap on other channel image formats
  1516. for I := 0 to NumPixels - 1 do
  1517. begin
  1518. ChannelGetSrcPixel(Data, Info, Pix64);
  1519. with Pix64 do
  1520. begin
  1521. Swap := Channels[SrcChannel];
  1522. Channels[SrcChannel] := Channels[DstChannel];
  1523. Channels[DstChannel] := Swap;
  1524. end;
  1525. ChannelSetDstPixel(Data, Info, Pix64);
  1526. Inc(Data, Info.BytesPerPixel);
  1527. end;
  1528. end;
  1529. Result := True;
  1530. except
  1531. RaiseImaging(SErrorSwapImage, [ImageToStr(Image)]);
  1532. end;
  1533. end;
  1534. function ReduceColors(var Image: TImageData; MaxColors: LongInt): Boolean;
  1535. var
  1536. TmpInfo: TImageFormatInfo;
  1537. Data, Index: PWord;
  1538. I, NumPixels: LongInt;
  1539. Pal: PPalette32;
  1540. Col:PColor32Rec;
  1541. OldFmt: TImageFormat;
  1542. begin
  1543. Result := False;
  1544. if TestImage(Image) then
  1545. with Image do
  1546. try
  1547. // First create temp image info and allocate output bits and palette
  1548. MaxColors := ClampInt(MaxColors, 2, High(Word));
  1549. OldFmt := Format;
  1550. FillChar(TmpInfo, SizeOf(TmpInfo), 0);
  1551. TmpInfo.PaletteEntries := MaxColors;
  1552. TmpInfo.BytesPerPixel := 2;
  1553. NumPixels := Width * Height;
  1554. GetMem(Data, NumPixels * TmpInfo.BytesPerPixel);
  1555. GetMem(Pal, MaxColors * SizeOf(TColor32Rec));
  1556. ConvertImage(Image, ifA8R8G8B8);
  1557. // We use median cut algorithm to create reduced palette and to
  1558. // fill Data with indices to this palette
  1559. ReduceColorsMedianCut(NumPixels, Bits, PByte(Data),
  1560. ImageFormatInfos[Format], @TmpInfo, MaxColors, ColorReductionMask, Pal);
  1561. Col := Bits;
  1562. Index := Data;
  1563. // Then we write reduced colors to the input image
  1564. for I := 0 to NumPixels - 1 do
  1565. begin
  1566. Col.Color := Pal[Index^].Color;
  1567. Inc(Col);
  1568. Inc(Index);
  1569. end;
  1570. FreeMemNil(Data);
  1571. FreeMemNil(Pal);
  1572. // And convert it to its original format
  1573. ConvertImage(Image, OldFmt);
  1574. Result := True;
  1575. except
  1576. RaiseImaging(SErrorReduceColors, [MaxColors, ImageToStr(Image)]);
  1577. end;
  1578. end;
  1579. function GenerateMipMaps(const Image: TImageData; Levels: LongInt;
  1580. var MipMaps: TDynImageDataArray): Boolean;
  1581. var
  1582. Width, Height, I, Count: LongInt;
  1583. Info: TImageFormatInfo;
  1584. CompatibleCopy: TImageData;
  1585. begin
  1586. Result := False;
  1587. if TestImage(Image) then
  1588. try
  1589. Width := Image.Width;
  1590. Height := Image.Height;
  1591. // We compute number of possible mipmap levels and if
  1592. // the given levels are invalid or zero we use this value
  1593. Count := GetNumMipMapLevels(Width, Height);
  1594. if (Levels <= 0) or (Levels > Count) then
  1595. Levels := Count;
  1596. // If we have special format image we create copy to allow pixel access.
  1597. // This is also done in FillMipMapLevel which is called for each level
  1598. // but then the main big image would be converted to compatible
  1599. // for every level.
  1600. GetImageFormatInfo(Image.Format, Info);
  1601. if Info.IsSpecial then
  1602. begin
  1603. InitImage(CompatibleCopy);
  1604. CloneImage(Image, CompatibleCopy);
  1605. ConvertImage(CompatibleCopy, ifDefault);
  1606. end
  1607. else
  1608. CompatibleCopy := Image;
  1609. FreeImagesInArray(MipMaps);
  1610. SetLength(MipMaps, Levels);
  1611. CloneImage(Image, MipMaps[0]);
  1612. for I := 1 to Levels - 1 do
  1613. begin
  1614. Width := Width shr 1;
  1615. Height := Height shr 1;
  1616. if Width < 1 then Width := 1;
  1617. if Height < 1 then Height := 1;
  1618. FillMipMapLevel(CompatibleCopy, Width, Height, MipMaps[I]);
  1619. end;
  1620. if CompatibleCopy.Format <> MipMaps[0].Format then
  1621. begin
  1622. // Must convert smaller levels to proper format
  1623. for I := 1 to High(MipMaps) do
  1624. ConvertImage(MipMaps[I], MipMaps[0].Format);
  1625. FreeImage(CompatibleCopy);
  1626. end;
  1627. Result := True;
  1628. except
  1629. RaiseImaging(SErrorGenerateMipMaps, [Levels, ImageToStr(Image)]);
  1630. end;
  1631. end;
  1632. function MapImageToPalette(var Image: TImageData; Pal: PPalette32;
  1633. Entries: LongInt): Boolean;
  1634. function FindNearestColor(Pal: PPalette32; Entries: LongInt; Col: TColor32Rec): LongInt;
  1635. var
  1636. I, MinDif, Dif: LongInt;
  1637. begin
  1638. Result := 0;
  1639. MinDif := 1020;
  1640. for I := 0 to Entries - 1 do
  1641. with Pal[I] do
  1642. begin
  1643. Dif := Abs(R - Col.R);
  1644. if Dif > MinDif then Continue;
  1645. Dif := Dif + Abs(G - Col.G);
  1646. if Dif > MinDif then Continue;
  1647. Dif := Dif + Abs(B - Col.B);
  1648. if Dif > MinDif then Continue;
  1649. Dif := Dif + Abs(A - Col.A);
  1650. if Dif < MinDif then
  1651. begin
  1652. MinDif := Dif;
  1653. Result := I;
  1654. end;
  1655. end;
  1656. end;
  1657. var
  1658. I, MaxEntries: LongInt;
  1659. PIndex: PByte;
  1660. PColor: PColor32Rec;
  1661. CloneARGB: TImageData;
  1662. Info: PImageFormatInfo;
  1663. begin
  1664. Assert((Entries >= 2) and (Entries <= 256));
  1665. Result := False;
  1666. if TestImage(Image) then
  1667. try
  1668. // We create clone of source image in A8R8G8B8 and
  1669. // then recreate source image in ifIndex8 format
  1670. // with palette taken from Pal parameter
  1671. InitImage(CloneARGB);
  1672. CloneImage(Image, CloneARGB);
  1673. ConvertImage(CloneARGB, ifA8R8G8B8);
  1674. FreeImage(Image);
  1675. NewImage(CloneARGB.Width, CloneARGB.Height, ifIndex8, Image);
  1676. Info := ImageFormatInfos[Image.Format];
  1677. MaxEntries := Min(Info.PaletteEntries, Entries);
  1678. Move(Pal^, Image.Palette^, MaxEntries * SizeOf(TColor32Rec));
  1679. PIndex := Image.Bits;
  1680. PColor := CloneARGB.Bits;
  1681. // For every pixel of ARGB clone we find closest color in
  1682. // given palette and assign its index to resulting image's pixel
  1683. // procedure used here is very slow but simple and memory usage friendly
  1684. // (contrary to other methods)
  1685. for I := 0 to Image.Width * Image.Height - 1 do
  1686. begin
  1687. PIndex^ := Byte(FindNearestColor(Image.Palette, MaxEntries, PColor^));
  1688. Inc(PIndex);
  1689. Inc(PColor);
  1690. end;
  1691. FreeImage(CloneARGB);
  1692. Result := True;
  1693. except
  1694. raise UpdateExceptMessage(GetExceptObject, SErrorMapImage, [ImageToStr(Image)]);
  1695. end;
  1696. end;
  1697. function SplitImage(var Image: TImageData; var Chunks: TDynImageDataArray;
  1698. ChunkWidth, ChunkHeight: LongInt; var XChunks, YChunks: LongInt;
  1699. PreserveSize: Boolean; Fill: Pointer): Boolean;
  1700. var
  1701. X, Y, XTrunc, YTrunc: LongInt;
  1702. NotOnEdge: Boolean;
  1703. Info: PImageFormatInfo;
  1704. OldFmt: TImageFormat;
  1705. begin
  1706. Assert((ChunkWidth > 0) and (ChunkHeight > 0));
  1707. Result := False;
  1708. OldFmt := Image.Format;
  1709. FreeImagesInArray(Chunks);
  1710. if TestImage(Image) then
  1711. try
  1712. Info := ImageFormatInfos[Image.Format];
  1713. if Info.IsSpecial then
  1714. ConvertImage(Image, ifDefault);
  1715. // We compute make sure that chunks are not larger than source image or negative
  1716. ChunkWidth := ClampInt(ChunkWidth, 0, Image.Width);
  1717. ChunkHeight := ClampInt(ChunkHeight, 0, Image.Height);
  1718. // Number of chunks along X and Y axes is computed
  1719. XChunks := Ceil(Image.Width / ChunkWidth);
  1720. YChunks := Ceil(Image.Height / ChunkHeight);
  1721. SetLength(Chunks, XChunks * YChunks);
  1722. // For every chunk we create new image and copy a portion of
  1723. // the source image to it. If chunk is on the edge of the source image
  1724. // we fill empty space with Fill pixel data if PreserveSize is set or
  1725. // make the chunk smaller if it is not set
  1726. for Y := 0 to YChunks - 1 do
  1727. for X := 0 to XChunks - 1 do
  1728. begin
  1729. // Determine if current chunk is on the edge of original image
  1730. NotOnEdge := ((X < XChunks - 1) and (Y < YChunks - 1)) or
  1731. ((Image.Width mod ChunkWidth = 0) and (Image.Height mod ChunkHeight = 0));
  1732. if PreserveSize or NotOnEdge then
  1733. begin
  1734. // We should preserve chunk sizes or we are somewhere inside original image
  1735. NewImage(ChunkWidth, ChunkHeight, Image.Format, Chunks[Y * XChunks + X]);
  1736. if (not NotOnEdge) and (Fill <> nil) then
  1737. FillRect(Chunks[Y * XChunks + X], 0, 0, ChunkWidth, ChunkHeight, Fill);
  1738. CopyRect(Image, X * ChunkWidth, Y * ChunkHeight, ChunkWidth, ChunkHeight,
  1739. Chunks[Y * XChunks + X], 0, 0);
  1740. end
  1741. else
  1742. begin
  1743. // Create smaller edge chunk
  1744. XTrunc := Image.Width - X * ChunkWidth;
  1745. YTrunc := Image.Height - Y * ChunkHeight;
  1746. NewImage(XTrunc, YTrunc, Image.Format, Chunks[Y * XChunks + X]);
  1747. CopyRect(Image, X * ChunkWidth, Y * ChunkHeight, XTrunc, YTrunc,
  1748. Chunks[Y * XChunks + X], 0, 0);
  1749. end;
  1750. // If source image is in indexed format we copy its palette to chunk
  1751. if Info.IsIndexed then
  1752. begin
  1753. Move(Image.Palette^, Chunks[Y * XChunks + X].Palette^,
  1754. Info.PaletteEntries * SizeOf(TColor32Rec));
  1755. end;
  1756. end;
  1757. if OldFmt <> Image.Format then
  1758. begin
  1759. ConvertImage(Image, OldFmt);
  1760. for X := 0 to Length(Chunks) - 1 do
  1761. ConvertImage(Chunks[X], OldFmt);
  1762. end;
  1763. Result := True;
  1764. except
  1765. raise UpdateExceptMessage(GetExceptObject, SErrorSplitImage,
  1766. [ImageToStr(Image), ChunkWidth, ChunkHeight]);
  1767. end;
  1768. end;
  1769. function MakePaletteForImages(var Images: TDynImageDataArray; Pal: PPalette32;
  1770. MaxColors: LongInt; ConvertImages: Boolean): Boolean;
  1771. var
  1772. I: Integer;
  1773. SrcInfo, DstInfo: PImageFormatInfo;
  1774. Target, TempImage: TImageData;
  1775. DstFormat: TImageFormat;
  1776. begin
  1777. Assert((Pal <> nil) and (MaxColors > 0));
  1778. Result := False;
  1779. InitImage(TempImage);
  1780. if TestImagesInArray(Images) then
  1781. try
  1782. // Null the color histogram
  1783. ReduceColorsMedianCut(0, nil, nil, nil, nil, 0, 0, nil, [raCreateHistogram]);
  1784. for I := 0 to Length(Images) - 1 do
  1785. begin
  1786. SrcInfo := ImageFormatInfos[Images[I].Format];
  1787. if SrcInfo.IsIndexed or SrcInfo.IsSpecial then
  1788. begin
  1789. // create temp image in supported format for updating histogram
  1790. CloneImage(Images[I], TempImage);
  1791. ConvertImage(TempImage, ifA8R8G8B8);
  1792. SrcInfo := ImageFormatInfos[TempImage.Format];
  1793. end
  1794. else
  1795. TempImage := Images[I];
  1796. // Update histogram with colors of each input image
  1797. ReduceColorsMedianCut(TempImage.Width * TempImage.Height, TempImage.Bits,
  1798. nil, SrcInfo, nil, MaxColors, ColorReductionMask, nil, [raUpdateHistogram]);
  1799. if Images[I].Bits <> TempImage.Bits then
  1800. FreeImage(TempImage);
  1801. end;
  1802. // Construct reduced color map from the histogram
  1803. ReduceColorsMedianCut(0, nil, nil, nil, nil, MaxColors, ColorReductionMask,
  1804. Pal, [raMakeColorMap]);
  1805. if ConvertImages then
  1806. begin
  1807. DstFormat := ifIndex8;
  1808. DstInfo := ImageFormatInfos[DstFormat];
  1809. MaxColors := Min(DstInfo.PaletteEntries, MaxColors);
  1810. for I := 0 to Length(Images) - 1 do
  1811. begin
  1812. SrcInfo := ImageFormatInfos[Images[I].Format];
  1813. if SrcInfo.IsIndexed or SrcInfo.IsSpecial then
  1814. begin
  1815. // If source image is in format not supported by ReduceColorsMedianCut
  1816. // we convert it
  1817. ConvertImage(Images[I], ifA8R8G8B8);
  1818. SrcInfo := ImageFormatInfos[Images[I].Format];
  1819. end;
  1820. InitImage(Target);
  1821. NewImage(Images[I].Width, Images[I].Height, DstFormat, Target);
  1822. // We map each input image to reduced palette and replace
  1823. // image in array with mapped image
  1824. ReduceColorsMedianCut(Images[I].Width * Images[I].Height, Images[I].Bits,
  1825. Target.Bits, SrcInfo, DstInfo, MaxColors, 0, nil, [raMapImage]);
  1826. Move(Pal^, Target.Palette^, MaxColors * SizeOf(TColor32Rec));
  1827. FreeImage(Images[I]);
  1828. Images[I] := Target;
  1829. end;
  1830. end;
  1831. Result := True;
  1832. except
  1833. RaiseImaging(SErrorMakePaletteForImages, [MaxColors, Length(Images)]);
  1834. end;
  1835. end;
  1836. procedure RotateImage(var Image: TImageData; Angle: Single);
  1837. var
  1838. OldFmt: TImageFormat;
  1839. procedure XShear(var Src, Dst: TImageData; Row, Offset, Weight, Bpp: Integer);
  1840. var
  1841. I, J, XPos: Integer;
  1842. PixSrc, PixLeft, PixOldLeft: TColor32Rec;
  1843. LineDst: PByteArray;
  1844. SrcPtr: PColor32;
  1845. begin
  1846. SrcPtr := @PByteArray(Src.Bits)[Row * Src.Width * Bpp];
  1847. LineDst := @PByteArray(Dst.Bits)[Row * Dst.Width * Bpp];
  1848. PixOldLeft.Color := 0;
  1849. for I := 0 to Src.Width - 1 do
  1850. begin
  1851. CopyPixel(SrcPtr, @PixSrc, Bpp);
  1852. for J := 0 to Bpp - 1 do
  1853. PixLeft.Channels[J] := MulDiv(PixSrc.Channels[J], Weight, 256);
  1854. XPos := I + Offset;
  1855. if (XPos >= 0) and (XPos < Dst.Width) then
  1856. begin
  1857. for J := 0 to Bpp - 1 do
  1858. PixSrc.Channels[J] := ClampToByte(PixSrc.Channels[J] - (PixLeft.Channels[J] - PixOldLeft.Channels[J]));
  1859. CopyPixel(@PixSrc, @LineDst[XPos * Bpp], Bpp);
  1860. end;
  1861. PixOldLeft := PixLeft;
  1862. Inc(PByte(SrcPtr), Bpp);
  1863. end;
  1864. XPos := Src.Width + Offset;
  1865. if XPos < Dst.Width then
  1866. CopyPixel(@PixOldLeft, @LineDst[XPos * Bpp], Bpp);
  1867. end;
  1868. procedure YShear(var Src, Dst: TImageData; Col, Offset, Weight, Bpp: Integer);
  1869. var
  1870. I, J, YPos: Integer;
  1871. PixSrc, PixLeft, PixOldLeft: TColor32Rec;
  1872. SrcPtr: PByte;
  1873. begin
  1874. SrcPtr := @PByteArray(Src.Bits)[Col * Bpp];
  1875. PixOldLeft.Color := 0;
  1876. for I := 0 to Src.Height - 1 do
  1877. begin
  1878. CopyPixel(SrcPtr, @PixSrc, Bpp);
  1879. for J := 0 to Bpp - 1 do
  1880. PixLeft.Channels[J] := MulDiv(PixSrc.Channels[J], Weight, 256);
  1881. YPos := I + Offset;
  1882. if (YPos >= 0) and (YPos < Dst.Height) then
  1883. begin
  1884. for J := 0 to Bpp - 1 do
  1885. PixSrc.Channels[J] := ClampToByte(PixSrc.Channels[J] - (PixLeft.Channels[J] - PixOldLeft.Channels[J]));
  1886. CopyPixel(@PixSrc, @PByteArray(Dst.Bits)[(YPos * Dst.Width + Col) * Bpp], Bpp);
  1887. end;
  1888. PixOldLeft := PixLeft;
  1889. Inc(SrcPtr, Src.Width * Bpp);
  1890. end;
  1891. YPos := Src.Height + Offset;
  1892. if YPos < Dst.Height then
  1893. CopyPixel(@PixOldLeft, @PByteArray(Dst.Bits)[(YPos * Dst.Width + Col) * Bpp], Bpp);
  1894. end;
  1895. procedure Rotate45(var Image: TImageData; Angle: Single);
  1896. var
  1897. TempImage1, TempImage2: TImageData;
  1898. AngleRad, AngleTan, AngleSin, AngleCos, Shear: Single;
  1899. I, DstWidth, DstHeight, SrcWidth, SrcHeight, Bpp: Integer;
  1900. SrcFmt, TempFormat: TImageFormat;
  1901. Info: TImageFormatInfo;
  1902. begin
  1903. AngleRad := Angle * Pi / 180;
  1904. AngleSin := Sin(AngleRad);
  1905. AngleCos := Cos(AngleRad);
  1906. AngleTan := Sin(AngleRad / 2) / Cos(AngleRad / 2);
  1907. SrcWidth := Image.Width;
  1908. SrcHeight := Image.Height;
  1909. SrcFmt := Image.Format;
  1910. if not (SrcFmt in [ifR8G8B8..ifX8R8G8B8, ifGray8..ifGray32, ifA16Gray16]) then
  1911. ConvertImage(Image, ifA8R8G8B8);
  1912. TempFormat := Image.Format;
  1913. GetImageFormatInfo(TempFormat, Info);
  1914. Bpp := Info.BytesPerPixel;
  1915. // 1st shear (horizontal)
  1916. DstWidth := Trunc(SrcWidth + SrcHeight * Abs(AngleTan) + 0.5);
  1917. DstHeight := SrcHeight;
  1918. InitImage(TempImage1);
  1919. NewImage(DstWidth, DstHeight, TempFormat, TempImage1);
  1920. for I := 0 to DstHeight - 1 do
  1921. begin
  1922. if AngleTan >= 0 then
  1923. Shear := (I + 0.5) * AngleTan
  1924. else
  1925. Shear := (I - DstHeight + 0.5) * AngleTan;
  1926. XShear(Image, TempImage1, I, Floor(Shear), Trunc(255 * (Shear - Floor(Shear)) + 1), Bpp);
  1927. end;
  1928. // 2nd shear (vertical)
  1929. FreeImage(Image);
  1930. DstHeight := Trunc(SrcWidth * Abs(AngleSin) + SrcHeight * AngleCos + 0.5) + 1;
  1931. InitImage(TempImage2);
  1932. NewImage(DstWidth, DstHeight, TempFormat, TempImage2);
  1933. if AngleSin >= 0 then
  1934. Shear := (SrcWidth - 1) * AngleSin
  1935. else
  1936. Shear := (SrcWidth - DstWidth) * -AngleSin;
  1937. for I := 0 to DstWidth - 1 do
  1938. begin
  1939. YShear(TempImage1, TempImage2, I, Floor(Shear), Trunc(255 * (Shear - Floor(Shear)) + 1), Bpp);
  1940. Shear := Shear - AngleSin;
  1941. end;
  1942. // 3rd shear (horizontal)
  1943. FreeImage(TempImage1);
  1944. DstWidth := Trunc(SrcHeight * Abs(AngleSin) + SrcWidth * AngleCos + 0.5) + 1;
  1945. NewImage(DstWidth, DstHeight, TempFormat, Image);
  1946. if AngleSin >= 0 then
  1947. Shear := (SrcWidth - 1) * AngleSin * -AngleTan
  1948. else
  1949. Shear := ((SrcWidth - 1) * -AngleSin + (1 - DstHeight)) * AngleTan;
  1950. for I := 0 to DstHeight - 1 do
  1951. begin
  1952. XShear(TempImage2, Image, I, Floor(Shear), Trunc(255 * (Shear - Floor(Shear)) + 1), Bpp);
  1953. Shear := Shear + AngleTan;
  1954. end;
  1955. FreeImage(TempImage2);
  1956. if Image.Format <> SrcFmt then
  1957. ConvertImage(Image, SrcFmt);
  1958. end;
  1959. procedure RotateMul90(var Image: TImageData; Angle: Integer);
  1960. var
  1961. RotImage: TImageData;
  1962. X, Y, BytesPerPixel: Integer;
  1963. RotPix, Pix: PByte;
  1964. begin
  1965. InitImage(RotImage);
  1966. BytesPerPixel := ImageFormatInfos[Image.Format].BytesPerPixel;
  1967. if ((Angle = 90) or (Angle = 270)) and (Image.Width <> Image.Height) then
  1968. NewImage(Image.Height, Image.Width, Image.Format, RotImage)
  1969. else
  1970. NewImage(Image.Width, Image.Height, Image.Format, RotImage);
  1971. RotPix := RotImage.Bits;
  1972. case Angle of
  1973. 90:
  1974. begin
  1975. for Y := 0 to RotImage.Height - 1 do
  1976. begin
  1977. Pix := @PByteArray(Image.Bits)[(Image.Width - Y - 1) * BytesPerPixel];
  1978. for X := 0 to RotImage.Width - 1 do
  1979. begin
  1980. CopyPixel(Pix, RotPix, BytesPerPixel);
  1981. Inc(RotPix, BytesPerPixel);
  1982. Inc(Pix, Image.Width * BytesPerPixel);
  1983. end;
  1984. end;
  1985. end;
  1986. 180:
  1987. begin
  1988. Pix := @PByteArray(Image.Bits)[((Image.Height - 1) * Image.Width +
  1989. (Image.Width - 1)) * BytesPerPixel];
  1990. for Y := 0 to RotImage.Height - 1 do
  1991. for X := 0 to RotImage.Width - 1 do
  1992. begin
  1993. CopyPixel(Pix, RotPix, BytesPerPixel);
  1994. Inc(RotPix, BytesPerPixel);
  1995. Dec(Pix, BytesPerPixel);
  1996. end;
  1997. end;
  1998. 270:
  1999. begin
  2000. for Y := 0 to RotImage.Height - 1 do
  2001. begin
  2002. Pix := @PByteArray(Image.Bits)[((Image.Height - 1) * Image.Width + Y) * BytesPerPixel];
  2003. for X := 0 to RotImage.Width - 1 do
  2004. begin
  2005. CopyPixel(Pix, RotPix, BytesPerPixel);
  2006. Inc(RotPix, BytesPerPixel);
  2007. Dec(Pix, Image.Width * BytesPerPixel);
  2008. end;
  2009. end;
  2010. end;
  2011. end;
  2012. FreeMemNil(Image.Bits);
  2013. RotImage.Palette := Image.Palette;
  2014. Image := RotImage;
  2015. end;
  2016. begin
  2017. if TestImage(Image) then
  2018. try
  2019. while Angle >= 360 do
  2020. Angle := Angle - 360;
  2021. while Angle < 0 do
  2022. Angle := Angle + 360;
  2023. if (Angle = 0) or (Abs(Angle) = 360) then
  2024. Exit;
  2025. OldFmt := Image.Format;
  2026. if ImageFormatInfos[Image.Format].IsSpecial then
  2027. ConvertImage(Image, ifDefault);
  2028. if (Angle > 45) and (Angle <= 135) then
  2029. begin
  2030. RotateMul90(Image, 90);
  2031. Angle := Angle - 90;
  2032. end
  2033. else if (Angle > 135) and (Angle <= 225) then
  2034. begin
  2035. RotateMul90(Image, 180);
  2036. Angle := Angle - 180;
  2037. end
  2038. else if (Angle > 225) and (Angle <= 315) then
  2039. begin
  2040. RotateMul90(Image, 270);
  2041. Angle := Angle - 270;
  2042. end;
  2043. if Angle <> 0 then
  2044. Rotate45(Image, Angle);
  2045. if OldFmt <> Image.Format then
  2046. ConvertImage(Image, OldFmt);
  2047. except
  2048. raise UpdateExceptMessage(GetExceptObject, SErrorRotateImage, [ImageToStr(Image), Angle]);
  2049. end;
  2050. end;
  2051. procedure RotateImageMul90(var Image: TImageData; AngleDeg: Integer);
  2052. var
  2053. RotImage: TImageData;
  2054. X, Y, BytesPerPixel: Integer;
  2055. RotPix, Pix: PByte;
  2056. begin
  2057. if TestImage(Image) then
  2058. try
  2059. InitImage(RotImage);
  2060. while AngleDeg >= 360 do
  2061. AngleDeg := AngleDeg - 360;
  2062. while AngleDeg < 0 do
  2063. AngleDeg := AngleDeg + 360;
  2064. if (AngleDeg = 0) or (Abs(AngleDeg) = 360) then
  2065. Exit;
  2066. if not ((AngleDeg mod 90) = 0) then
  2067. raise EImagingError.CreateFmt('Angle must be multiple of 90 but was: %d', [AngleDeg]);
  2068. if ((AngleDeg = 90) or (AngleDeg = 270)) and (Image.Width <> Image.Height) then
  2069. NewImage(Image.Height, Image.Width, Image.Format, RotImage)
  2070. else
  2071. NewImage(Image.Width, Image.Height, Image.Format, RotImage);
  2072. BytesPerPixel := ImageFormatInfos[Image.Format].BytesPerPixel;
  2073. RotPix := RotImage.Bits;
  2074. case AngleDeg of
  2075. 90:
  2076. begin
  2077. for Y := 0 to RotImage.Height - 1 do
  2078. begin
  2079. Pix := @PByteArray(Image.Bits)[(Image.Width - Y - 1) * BytesPerPixel];
  2080. for X := 0 to RotImage.Width - 1 do
  2081. begin
  2082. CopyPixel(Pix, RotPix, BytesPerPixel);
  2083. Inc(RotPix, BytesPerPixel);
  2084. Inc(Pix, Image.Width * BytesPerPixel);
  2085. end;
  2086. end;
  2087. end;
  2088. 180:
  2089. begin
  2090. Pix := @PByteArray(Image.Bits)[((Image.Height - 1) * Image.Width +
  2091. (Image.Width - 1)) * BytesPerPixel];
  2092. for Y := 0 to RotImage.Height - 1 do
  2093. for X := 0 to RotImage.Width - 1 do
  2094. begin
  2095. CopyPixel(Pix, RotPix, BytesPerPixel);
  2096. Inc(RotPix, BytesPerPixel);
  2097. Dec(Pix, BytesPerPixel);
  2098. end;
  2099. end;
  2100. 270:
  2101. begin
  2102. for Y := 0 to RotImage.Height - 1 do
  2103. begin
  2104. Pix := @PByteArray(Image.Bits)[((Image.Height - 1) * Image.Width + Y) * BytesPerPixel];
  2105. for X := 0 to RotImage.Width - 1 do
  2106. begin
  2107. CopyPixel(Pix, RotPix, BytesPerPixel);
  2108. Inc(RotPix, BytesPerPixel);
  2109. Dec(Pix, Image.Width * BytesPerPixel);
  2110. end;
  2111. end;
  2112. end;
  2113. end;
  2114. FreeMemNil(Image.Bits);
  2115. RotImage.Palette := Image.Palette;
  2116. Image := RotImage;
  2117. except
  2118. raise UpdateExceptMessage(GetExceptObject, 'Error while rotating image %s by %d degrees',
  2119. [ImageToStr(Image), AngleDeg]);
  2120. end;
  2121. end;
  2122. { Drawing/Pixel functions }
  2123. function CopyRect(const SrcImage: TImageData; SrcX, SrcY, Width, Height: LongInt;
  2124. var DstImage: TImageData; DstX, DstY: LongInt): Boolean;
  2125. var
  2126. Info: PImageFormatInfo;
  2127. I, SrcWidthBytes, DstWidthBytes, MoveBytes: LongInt;
  2128. SrcPointer, DstPointer: PByte;
  2129. WorkImage: TImageData;
  2130. OldFormat: TImageFormat;
  2131. begin
  2132. Result := False;
  2133. OldFormat := ifUnknown;
  2134. if TestImage(SrcImage) and TestImage(DstImage) then
  2135. try
  2136. // Make sure we are still copying image to image, not invalid pointer to protected memory
  2137. ClipCopyBounds(SrcX, SrcY, Width, Height, DstX, DstY, SrcImage.Width, SrcImage.Height,
  2138. Rect(0, 0, DstImage.Width, DstImage.Height));
  2139. if (Width > 0) and (Height > 0) then
  2140. begin
  2141. Info := ImageFormatInfos[DstImage.Format];
  2142. if Info.IsSpecial then
  2143. begin
  2144. // If dest image is in special format we convert it to default
  2145. OldFormat := Info.Format;
  2146. ConvertImage(DstImage, ifDefault);
  2147. Info := ImageFormatInfos[DstImage.Format];
  2148. end;
  2149. if SrcImage.Format <> DstImage.Format then
  2150. begin
  2151. // If images are in different format source is converted to dest's format
  2152. InitImage(WorkImage);
  2153. CloneImage(SrcImage, WorkImage);
  2154. ConvertImage(WorkImage, DstImage.Format);
  2155. end
  2156. else
  2157. WorkImage := SrcImage;
  2158. MoveBytes := Width * Info.BytesPerPixel;
  2159. DstWidthBytes := DstImage.Width * Info.BytesPerPixel;
  2160. DstPointer := @PByteArray(DstImage.Bits)[DstY * DstWidthBytes +
  2161. DstX * Info.BytesPerPixel];
  2162. SrcWidthBytes := WorkImage.Width * Info.BytesPerPixel;
  2163. SrcPointer := @PByteArray(WorkImage.Bits)[SrcY * SrcWidthBytes +
  2164. SrcX * Info.BytesPerPixel];
  2165. for I := 0 to Height - 1 do
  2166. begin
  2167. Move(SrcPointer^, DstPointer^, MoveBytes);
  2168. Inc(SrcPointer, SrcWidthBytes);
  2169. Inc(DstPointer, DstWidthBytes);
  2170. end;
  2171. // If dest image was in special format we convert it back
  2172. if OldFormat <> ifUnknown then
  2173. ConvertImage(DstImage, OldFormat);
  2174. // Working image must be freed if it is not the same as source image
  2175. if WorkImage.Bits <> SrcImage.Bits then
  2176. FreeImage(WorkImage);
  2177. Result := True;
  2178. end;
  2179. except
  2180. RaiseImaging(SErrorCopyRect, [ImageToStr(SrcImage), ImageToStr(DstImage)]);
  2181. end;
  2182. end;
  2183. function FillRect(var Image: TImageData; X, Y, Width, Height: LongInt;
  2184. FillColor: Pointer): Boolean;
  2185. var
  2186. Info: PImageFormatInfo;
  2187. I, J, ImageWidthBytes, RectWidthBytes, Bpp: Longint;
  2188. LinePointer, PixPointer: PByte;
  2189. OldFmt: TImageFormat;
  2190. begin
  2191. Result := False;
  2192. if TestImage(Image) then
  2193. try
  2194. ClipRectBounds(X, Y, Width, Height, Rect(0, 0, Image.Width, Image.Height));
  2195. if (Width > 0) and (Height > 0) then
  2196. begin
  2197. OldFmt := Image.Format;
  2198. if ImageFormatInfos[OldFmt].IsSpecial then
  2199. ConvertImage(Image, ifDefault);
  2200. Info := ImageFormatInfos[Image.Format];
  2201. Bpp := Info.BytesPerPixel;
  2202. ImageWidthBytes := Image.Width * Bpp;
  2203. RectWidthBytes := Width * Bpp;
  2204. LinePointer := @PByteArray(Image.Bits)[Y * ImageWidthBytes + X * Bpp];
  2205. for I := 0 to Height - 1 do
  2206. begin
  2207. case Bpp of
  2208. 1: FillMemoryByte(LinePointer, RectWidthBytes, PByte(FillColor)^);
  2209. 2: FillMemoryWord(LinePointer, RectWidthBytes, PWord(FillColor)^);
  2210. 4: FillMemoryUInt32(LinePointer, RectWidthBytes, PUInt32(FillColor)^);
  2211. else
  2212. PixPointer := LinePointer;
  2213. for J := 0 to Width - 1 do
  2214. begin
  2215. CopyPixel(FillColor, PixPointer, Bpp);
  2216. Inc(PixPointer, Bpp);
  2217. end;
  2218. end;
  2219. Inc(LinePointer, ImageWidthBytes);
  2220. end;
  2221. if OldFmt <> Image.Format then
  2222. ConvertImage(Image, OldFmt);
  2223. end;
  2224. Result := True;
  2225. except
  2226. RaiseImaging(SErrorFillRect, [X, Y, Width, Height, ImageToStr(Image)]);
  2227. end;
  2228. end;
  2229. function ReplaceColor(var Image: TImageData; X, Y, Width, Height: LongInt;
  2230. OldColor, NewColor: Pointer): Boolean;
  2231. var
  2232. Info: PImageFormatInfo;
  2233. I, J, WidthBytes, Bpp: Longint;
  2234. LinePointer, PixPointer: PByte;
  2235. OldFmt: TImageFormat;
  2236. begin
  2237. Assert((OldColor <> nil) and (NewColor <> nil));
  2238. Result := False;
  2239. if TestImage(Image) then
  2240. try
  2241. ClipRectBounds(X, Y, Width, Height, Rect(0, 0, Image.Width, Image.Height));
  2242. if (Width > 0) and (Height > 0) then
  2243. begin
  2244. OldFmt := Image.Format;
  2245. if ImageFormatInfos[OldFmt].IsSpecial then
  2246. ConvertImage(Image, ifDefault);
  2247. Info := ImageFormatInfos[Image.Format];
  2248. Bpp := Info.BytesPerPixel;
  2249. WidthBytes := Image.Width * Bpp;
  2250. LinePointer := @PByteArray(Image.Bits)[Y * WidthBytes + X * Bpp];
  2251. for I := 0 to Height - 1 do
  2252. begin
  2253. PixPointer := LinePointer;
  2254. for J := 0 to Width - 1 do
  2255. begin
  2256. if ComparePixels(PixPointer, OldColor, Bpp) then
  2257. CopyPixel(NewColor, PixPointer, Bpp);
  2258. Inc(PixPointer, Bpp);
  2259. end;
  2260. Inc(LinePointer, WidthBytes);
  2261. end;
  2262. if OldFmt <> Image.Format then
  2263. ConvertImage(Image, OldFmt);
  2264. end;
  2265. Result := True;
  2266. except
  2267. RaiseImaging(SErrorReplaceColor, [X, Y, Width, Height, ImageToStr(Image)]);
  2268. end;
  2269. end;
  2270. function StretchRect(const SrcImage: TImageData; SrcX, SrcY, SrcWidth,
  2271. SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth,
  2272. DstHeight: LongInt; Filter: TResizeFilter): Boolean;
  2273. var
  2274. Info: PImageFormatInfo;
  2275. WorkImage: TImageData;
  2276. OldFormat: TImageFormat;
  2277. Resampling: TSamplingFilter;
  2278. begin
  2279. Result := False;
  2280. OldFormat := ifUnknown;
  2281. if TestImage(SrcImage) and TestImage(DstImage) then
  2282. try
  2283. // Make sure we are still copying image to image, not invalid pointer to protected memory
  2284. ClipStretchBounds(SrcX, SrcY, SrcWidth, SrcHeight, DstX, DstY, DstWidth, DstHeight,
  2285. SrcImage.Width, SrcImage.Height, Rect(0, 0, DstImage.Width, DstImage.Height));
  2286. if (SrcWidth = DstWidth) and (SrcHeight = DstHeight) then
  2287. begin
  2288. // If source and dest rectangles have the same size call CopyRect
  2289. Result := CopyRect(SrcImage, SrcX, SrcY, SrcWidth, SrcHeight, DstImage, DstX, DstY);
  2290. end
  2291. else if (SrcWidth > 0) and (SrcHeight > 0) and (DstWidth > 0) and (DstHeight > 0) then
  2292. begin
  2293. // If source and dest rectangles don't have the same size we do stretch
  2294. Info := ImageFormatInfos[DstImage.Format];
  2295. if Info.IsSpecial then
  2296. begin
  2297. // If dest image is in special format we convert it to default
  2298. OldFormat := Info.Format;
  2299. ConvertImage(DstImage, ifDefault);
  2300. Info := ImageFormatInfos[DstImage.Format];
  2301. end;
  2302. if SrcImage.Format <> DstImage.Format then
  2303. begin
  2304. // If images are in different format source is converted to dest's format
  2305. InitImage(WorkImage);
  2306. CloneImage(SrcImage, WorkImage);
  2307. ConvertImage(WorkImage, DstImage.Format);
  2308. end
  2309. else
  2310. WorkImage := SrcImage;
  2311. // Only pixel resize is supported for indexed images
  2312. if Info.IsIndexed then
  2313. Filter := rfNearest;
  2314. if Filter = rfNearest then
  2315. begin
  2316. StretchNearest(WorkImage, SrcX, SrcY, SrcWidth, SrcHeight,
  2317. DstImage, DstX, DstY, DstWidth, DstHeight);
  2318. end
  2319. else
  2320. begin
  2321. Resampling := sfNearest;
  2322. case Filter of
  2323. rfBilinear: Resampling := sfLinear;
  2324. rfBicubic: Resampling := DefaultCubicFilter;
  2325. rfLanczos: Resampling := sfLanczos;
  2326. end;
  2327. StretchResample(WorkImage, SrcX, SrcY, SrcWidth, SrcHeight,
  2328. DstImage, DstX, DstY, DstWidth, DstHeight, Resampling);
  2329. end;
  2330. // If dest image was in special format we convert it back
  2331. if OldFormat <> ifUnknown then
  2332. ConvertImage(DstImage, OldFormat);
  2333. // Working image must be freed if it is not the same as source image
  2334. if WorkImage.Bits <> SrcImage.Bits then
  2335. FreeImage(WorkImage);
  2336. Result := True;
  2337. end;
  2338. except
  2339. RaiseImaging(SErrorStretchRect, [ImageToStr(SrcImage), ImageToStr(DstImage)]);
  2340. end;
  2341. end;
  2342. procedure GetPixelDirect(const Image: TImageData; X, Y: LongInt; Pixel: Pointer);
  2343. var
  2344. BytesPerPixel: LongInt;
  2345. begin
  2346. Assert(Pixel <> nil);
  2347. BytesPerPixel := ImageFormatInfos[Image.Format].BytesPerPixel;
  2348. CopyPixel(@PByteArray(Image.Bits)[(Y * Image.Width + X) * BytesPerPixel],
  2349. Pixel, BytesPerPixel);
  2350. end;
  2351. procedure SetPixelDirect(const Image: TImageData; X, Y: LongInt; Pixel: Pointer);
  2352. var
  2353. BytesPerPixel: LongInt;
  2354. begin
  2355. Assert(Pixel <> nil);
  2356. BytesPerPixel := ImageFormatInfos[Image.Format].BytesPerPixel;
  2357. CopyPixel(Pixel, @PByteArray(Image.Bits)[(Y * Image.Width + X) * BytesPerPixel],
  2358. BytesPerPixel);
  2359. end;
  2360. function GetPixel32(const Image: TImageData; X, Y: LongInt): TColor32Rec;
  2361. var
  2362. Info: PImageFormatInfo;
  2363. Data: PByte;
  2364. begin
  2365. Info := ImageFormatInfos[Image.Format];
  2366. Data := @PByteArray(Image.Bits)[(Y * Image.Width + X) * Info.BytesPerPixel];
  2367. Result := GetPixel32Generic(Data, Info, Image.Palette);
  2368. end;
  2369. procedure SetPixel32(const Image: TImageData; X, Y: LongInt; const Color: TColor32Rec);
  2370. var
  2371. Info: PImageFormatInfo;
  2372. Data: PByte;
  2373. begin
  2374. Info := ImageFormatInfos[Image.Format];
  2375. Data := @PByteArray(Image.Bits)[(Y * Image.Width + X) * Info.BytesPerPixel];
  2376. SetPixel32Generic(Data, Info, Image.Palette, Color);
  2377. end;
  2378. function GetPixelFP(const Image: TImageData; X, Y: LongInt): TColorFPRec;
  2379. var
  2380. Info: PImageFormatInfo;
  2381. Data: PByte;
  2382. begin
  2383. Info := ImageFormatInfos[Image.Format];
  2384. Data := @PByteArray(Image.Bits)[(Y * Image.Width + X) * Info.BytesPerPixel];
  2385. Result := GetPixelFPGeneric(Data, Info, Image.Palette);
  2386. end;
  2387. procedure SetPixelFP(const Image: TImageData; X, Y: LongInt; const Color: TColorFPRec);
  2388. var
  2389. Info: PImageFormatInfo;
  2390. Data: PByte;
  2391. begin
  2392. Info := ImageFormatInfos[Image.Format];
  2393. Data := @PByteArray(Image.Bits)[(Y * Image.Width + X) * Info.BytesPerPixel];
  2394. SetPixelFPGeneric(Data, Info, Image.Palette, Color);
  2395. end;
  2396. { Palette Functions }
  2397. procedure NewPalette(Entries: LongInt; var Pal: PPalette32);
  2398. begin
  2399. Assert((Entries > 2) and (Entries <= 65535));
  2400. try
  2401. GetMem(Pal, Entries * SizeOf(TColor32Rec));
  2402. FillChar(Pal^, Entries * SizeOf(TColor32Rec), $FF);
  2403. except
  2404. RaiseImaging(SErrorNewPalette, [Entries]);
  2405. end;
  2406. end;
  2407. procedure FreePalette(var Pal: PPalette32);
  2408. begin
  2409. try
  2410. FreeMemNil(Pal);
  2411. except
  2412. RaiseImaging(SErrorFreePalette, [Pal]);
  2413. end;
  2414. end;
  2415. procedure CopyPalette(SrcPal, DstPal: PPalette32; SrcIdx, DstIdx, Count: LongInt);
  2416. begin
  2417. Assert((SrcPal <> nil) and (DstPal <> nil));
  2418. Assert((SrcIdx >= 0) and (DstIdx >= 0) and (Count >= 0));
  2419. try
  2420. Move(SrcPal[SrcIdx], DstPal[DstIdx], Count * SizeOf(TColor32Rec));
  2421. except
  2422. RaiseImaging(SErrorCopyPalette, [Count, SrcPal, DstPal]);
  2423. end;
  2424. end;
  2425. function FindColor(Pal: PPalette32; Entries: LongInt; Color: TColor32):
  2426. LongInt;
  2427. var
  2428. Col: TColor32Rec;
  2429. I, MinDif, Dif: LongInt;
  2430. begin
  2431. Assert(Pal <> nil);
  2432. Result := -1;
  2433. Col.Color := Color;
  2434. try
  2435. // First try to find exact match
  2436. for I := 0 to Entries - 1 do
  2437. with Pal[I] do
  2438. begin
  2439. if (A = Col.A) and (R = Col.R) and
  2440. (G = Col.G) and (B = Col.B) then
  2441. begin
  2442. Result := I;
  2443. Exit;
  2444. end;
  2445. end;
  2446. // If exact match was not found, find nearest color
  2447. MinDif := 1020;
  2448. for I := 0 to Entries - 1 do
  2449. with Pal[I] do
  2450. begin
  2451. Dif := Abs(R - Col.R);
  2452. if Dif > MinDif then Continue;
  2453. Dif := Dif + Abs(G - Col.G);
  2454. if Dif > MinDif then Continue;
  2455. Dif := Dif + Abs(B - Col.B);
  2456. if Dif > MinDif then Continue;
  2457. Dif := Dif + Abs(A - Col.A);
  2458. if Dif < MinDif then
  2459. begin
  2460. MinDif := Dif;
  2461. Result := I;
  2462. end;
  2463. end;
  2464. except
  2465. RaiseImaging(SErrorFindColor, [Pal, Entries]);
  2466. end;
  2467. end;
  2468. procedure FillGrayscalePalette(Pal: PPalette32; Entries: LongInt);
  2469. var
  2470. I: LongInt;
  2471. begin
  2472. Assert(Pal <> nil);
  2473. try
  2474. for I := 0 to Entries - 1 do
  2475. with Pal[I] do
  2476. begin
  2477. A := $FF;
  2478. R := Byte(I);
  2479. G := Byte(I);
  2480. B := Byte(I);
  2481. end;
  2482. except
  2483. RaiseImaging(SErrorGrayscalePalette, [Pal, Entries]);
  2484. end;
  2485. end;
  2486. procedure FillCustomPalette(Pal: PPalette32; Entries: LongInt; RBits, GBits,
  2487. BBits: Byte; Alpha: Byte = $FF);
  2488. var
  2489. I, TotalBits, MaxEntries: LongInt;
  2490. begin
  2491. Assert(Pal <> nil);
  2492. TotalBits := RBits + GBits + BBits;
  2493. MaxEntries := Min(Pow2Int(TotalBits), Entries);
  2494. FillChar(Pal^, Entries * SizeOf(TColor32Rec), 0);
  2495. try
  2496. for I := 0 to MaxEntries - 1 do
  2497. with Pal[I] do
  2498. begin
  2499. A := Alpha;
  2500. if RBits > 0 then
  2501. R := ((I shr Max(0, GBits + BBits - 1)) and (1 shl RBits - 1)) * 255 div (1 shl RBits - 1);
  2502. if GBits > 0 then
  2503. G := ((I shr Max(0, BBits - 1)) and (1 shl GBits - 1)) * 255 div (1 shl GBits - 1);
  2504. if BBits > 0 then
  2505. B := ((I shr 0) and (1 shl BBits - 1)) * 255 div (1 shl BBits - 1);
  2506. end;
  2507. except
  2508. RaiseImaging(SErrorCustomPalette, [Pal, Entries]);
  2509. end;
  2510. end;
  2511. procedure SwapChannelsOfPalette(Pal: PPalette32; Entries, SrcChannel,
  2512. DstChannel: LongInt);
  2513. var
  2514. I: LongInt;
  2515. Swap: Byte;
  2516. begin
  2517. Assert(Pal <> nil);
  2518. Assert((SrcChannel in [0..3]) and (DstChannel in [0..3]));
  2519. try
  2520. for I := 0 to Entries - 1 do
  2521. with Pal[I] do
  2522. begin
  2523. Swap := Channels[SrcChannel];
  2524. Channels[SrcChannel] := Channels[DstChannel];
  2525. Channels[DstChannel] := Swap;
  2526. end;
  2527. except
  2528. RaiseImaging(SErrorSwapPalette, [Pal, Entries]);
  2529. end;
  2530. end;
  2531. { Options Functions }
  2532. function SetOption(OptionId, Value: LongInt): Boolean;
  2533. begin
  2534. Result := False;
  2535. if (OptionId >= 0) and (OptionId < Length(Options)) and
  2536. (Options[OptionID] <> nil) then
  2537. begin
  2538. Options[OptionID]^ := CheckOptionValue(OptionId, Value);
  2539. Result := True;
  2540. end;
  2541. end;
  2542. function GetOption(OptionId: LongInt): LongInt;
  2543. begin
  2544. Result := InvalidOption;
  2545. if (OptionId >= 0) and (OptionId < Length(Options)) and
  2546. (Options[OptionID] <> nil) then
  2547. begin
  2548. Result := Options[OptionID]^;
  2549. end;
  2550. end;
  2551. function PushOptions: Boolean;
  2552. begin
  2553. Result := OptionStack.Push;
  2554. end;
  2555. function PopOptions: Boolean;
  2556. begin
  2557. Result := OptionStack.Pop;
  2558. end;
  2559. { Image Format Functions }
  2560. function GetImageFormatInfo(Format: TImageFormat; out Info: TImageFormatInfo): Boolean;
  2561. begin
  2562. FillChar(Info, SizeOf(Info), 0);
  2563. if ImageFormatInfos[Format] <> nil then
  2564. begin
  2565. Info := ImageFormatInfos[Format]^;
  2566. Result := True;
  2567. end
  2568. else
  2569. Result := False;
  2570. end;
  2571. function GetPixelsSize(Format: TImageFormat; Width, Height: LongInt): Int64;
  2572. begin
  2573. if ImageFormatInfos[Format] <> nil then
  2574. Result := ImageFormatInfos[Format].GetPixelsSize(Format, Width, Height)
  2575. else
  2576. Result := 0;
  2577. end;
  2578. { IO Functions }
  2579. procedure SetUserFileIO(OpenProc: TOpenProc;
  2580. CloseProc: TCloseProc; EofProc: TEofProc; SeekProc: TSeekProc; TellProc:
  2581. TTellProc; ReadProc: TReadProc; WriteProc: TWriteProc);
  2582. begin
  2583. FileIO.Open := OpenProc;
  2584. FileIO.Close := CloseProc;
  2585. FileIO.Eof := EofProc;
  2586. FileIO.Seek := SeekProc;
  2587. FileIO.Tell := TellProc;
  2588. FileIO.Read := ReadProc;
  2589. FileIO.Write := WriteProc;
  2590. end;
  2591. procedure ResetFileIO;
  2592. begin
  2593. FileIO := OriginalFileIO;
  2594. end;
  2595. { Raw Image IO Functions }
  2596. procedure ReadRawImage(Handle: TImagingHandle; Width, Height: Integer;
  2597. Format: TImageFormat; var Image: TImageData; const Offset: Int64; RowLength: Integer);
  2598. var
  2599. WidthBytes, I: Integer;
  2600. Info: PImageFormatInfo;
  2601. begin
  2602. Info := ImageFormatInfos[Format];
  2603. // Calc scanline size
  2604. WidthBytes := Info.GetPixelsSize(Format, Width, 1);
  2605. if RowLength = 0 then
  2606. RowLength := WidthBytes;
  2607. // Create new image if needed - don't need to allocate new one if there is already
  2608. // one with desired size and format
  2609. if (Image.Width <> Width) or (Image.Height <> Height) or (Image.Format <> Format) then
  2610. NewImage(Width, Height, Format, Image);
  2611. // Move past the header
  2612. IO.Seek(Handle, Offset, smFromCurrent);
  2613. // Read scanlines from input
  2614. for I := 0 to Height - 1 do
  2615. begin
  2616. IO.Read(Handle, @PByteArray(Image.Bits)[I * WidthBytes], WidthBytes);
  2617. IO.Seek(Handle, RowLength - WidthBytes, smFromCurrent);
  2618. end;
  2619. end;
  2620. procedure ReadRawImageFromFile(const FileName: string; Width, Height: Integer;
  2621. Format: TImageFormat; var Image: TImageData; const Offset: Int64; RowLength: Integer);
  2622. var
  2623. Handle: TImagingHandle;
  2624. begin
  2625. Assert(FileName <> '');
  2626. // Set IO ops to file ops and open given file
  2627. SetFileIO;
  2628. Handle := IO.Open(PChar(FileName), omReadOnly);
  2629. try
  2630. ReadRawImage(Handle, Width, Height, Format, Image, Offset, RowLength);
  2631. finally
  2632. IO.Close(Handle);
  2633. end;
  2634. end;
  2635. procedure ReadRawImageFromStream(Stream: TStream; Width, Height: Integer;
  2636. Format: TImageFormat; var Image: TImageData; const Offset: Int64; RowLength: Integer);
  2637. var
  2638. Handle: TImagingHandle;
  2639. begin
  2640. Assert(Stream <> nil);
  2641. if Stream.Size - Stream.Position = 0 then
  2642. RaiseImaging(SErrorEmptyStream, []);
  2643. // Set IO ops to stream ops and open given stream
  2644. SetStreamIO;
  2645. Handle := IO.Open(Pointer(Stream), omReadOnly);
  2646. try
  2647. ReadRawImage(Handle, Width, Height, Format, Image, Offset, RowLength);
  2648. finally
  2649. IO.Close(Handle);
  2650. end;
  2651. end;
  2652. procedure ReadRawImageFromMemory(Data: Pointer; const DataSize: Int64; Width, Height: Integer;
  2653. Format: TImageFormat; var Image: TImageData; const Offset: Int64; RowLength: Integer);
  2654. var
  2655. Handle: TImagingHandle;
  2656. MemRec: TMemoryIORec;
  2657. begin
  2658. Assert((Data <> nil) and (DataSize > 0));
  2659. // Set IO ops to memory ops and open given stream
  2660. SetMemoryIO;
  2661. MemRec := PrepareMemIO(Data, DataSize);
  2662. Handle := IO.Open(@MemRec, omReadOnly);
  2663. try
  2664. ReadRawImage(Handle, Width, Height, Format, Image, Offset, RowLength);
  2665. finally
  2666. IO.Close(Handle);
  2667. end;
  2668. end;
  2669. procedure ReadRawImageRect(Data: Pointer; Left, Top, Width, Height: Integer;
  2670. var Image: TImageData; const Offset: Int64; RowLength: Integer);
  2671. var
  2672. DestScanBytes, RectBytes, I: Integer;
  2673. Info: PImageFormatInfo;
  2674. Src, Dest: PByte;
  2675. begin
  2676. Assert(Data <> nil);
  2677. Assert((Left + Width <= Image.Width) and (Top + Height <= Image.Height));
  2678. Info := ImageFormatInfos[Image.Format];
  2679. // Calc scanline size
  2680. DestScanBytes := Info.GetPixelsSize(Info.Format, Image.Width, 1);
  2681. RectBytes := Info.GetPixelsSize(Info.Format, Width, 1);
  2682. if RowLength = 0 then
  2683. RowLength := RectBytes;
  2684. Src := Data;
  2685. Dest := @PByteArray(Image.Bits)[Top * DestScanBytes + Info.GetPixelsSize(Info.Format, Left, 1)];
  2686. // Move past the header
  2687. Inc(Src, Offset);
  2688. // Read lines into rect in the existing image
  2689. for I := 0 to Height - 1 do
  2690. begin
  2691. Move(Src^, Dest^, RectBytes);
  2692. Inc(Src, RowLength);
  2693. Inc(Dest, DestScanBytes);
  2694. end;
  2695. end;
  2696. procedure WriteRawImage(Handle: TImagingHandle; const Image: TImageData;
  2697. const Offset: Int64; RowLength: Integer);
  2698. var
  2699. WidthBytes, I: Integer;
  2700. Info: PImageFormatInfo;
  2701. begin
  2702. Info := ImageFormatInfos[Image.Format];
  2703. // Calc scanline size
  2704. WidthBytes := Info.GetPixelsSize(Image.Format, Image.Width, 1);
  2705. if RowLength = 0 then
  2706. RowLength := WidthBytes;
  2707. // Move past the header
  2708. IO.Seek(Handle, Offset, smFromCurrent);
  2709. // Write scanlines to output
  2710. for I := 0 to Image.Height - 1 do
  2711. begin
  2712. IO.Write(Handle, @PByteArray(Image.Bits)[I * WidthBytes], WidthBytes);
  2713. IO.Seek(Handle, RowLength - WidthBytes, smFromCurrent);
  2714. end;
  2715. end;
  2716. procedure WriteRawImageToFile(const FileName: string; const Image: TImageData;
  2717. const Offset: Int64; RowLength: Integer);
  2718. var
  2719. Handle: TImagingHandle;
  2720. begin
  2721. Assert(FileName <> '');
  2722. // Set IO ops to file ops and open given file
  2723. SetFileIO;
  2724. Handle := IO.Open(PChar(FileName), omCreate);
  2725. try
  2726. WriteRawImage(Handle, Image, Offset, RowLength);
  2727. finally
  2728. IO.Close(Handle);
  2729. end;
  2730. end;
  2731. procedure WriteRawImageToStream(Stream: TStream; const Image: TImageData;
  2732. const Offset: Int64; RowLength: Integer);
  2733. var
  2734. Handle: TImagingHandle;
  2735. begin
  2736. Assert(Stream <> nil);
  2737. // Set IO ops to stream ops and open given stream
  2738. SetStreamIO;
  2739. Handle := IO.Open(Pointer(Stream), omCreate);
  2740. try
  2741. WriteRawImage(Handle, Image, Offset, RowLength);
  2742. finally
  2743. IO.Close(Handle);
  2744. end;
  2745. end;
  2746. procedure WriteRawImageToMemory(Data: Pointer; const DataSize: Int64; const Image: TImageData;
  2747. const Offset: Int64; RowLength: Integer);
  2748. var
  2749. Handle: TImagingHandle;
  2750. MemRec: TMemoryIORec;
  2751. begin
  2752. Assert((Data <> nil) and (DataSize > 0));
  2753. // Set IO ops to memory ops and open given stream
  2754. SetMemoryIO;
  2755. MemRec := PrepareMemIO(Data, DataSize);
  2756. Handle := IO.Open(@MemRec, omCreate);
  2757. try
  2758. WriteRawImage(Handle, Image, Offset, RowLength);
  2759. finally
  2760. IO.Close(Handle);
  2761. end;
  2762. end;
  2763. procedure WriteRawImageRect(Data: Pointer; Left, Top, Width, Height: Integer;
  2764. const Image: TImageData; const Offset: Int64; RowLength: Integer);
  2765. var
  2766. SrcScanBytes, RectBytes, I: Integer;
  2767. Info: PImageFormatInfo;
  2768. Src, Dest: PByte;
  2769. begin
  2770. Assert(Data <> nil);
  2771. Assert((Left + Width <= Image.Width) and (Top + Height <= Image.Height));
  2772. Info := ImageFormatInfos[Image.Format];
  2773. // Calc scanline size
  2774. SrcScanBytes := Info.GetPixelsSize(Info.Format, Image.Width, 1);
  2775. RectBytes := Info.GetPixelsSize(Info.Format, Width, 1);
  2776. if RowLength = 0 then
  2777. RowLength := RectBytes;
  2778. Src := @PByteArray(Image.Bits)[Top * SrcScanBytes + Info.GetPixelsSize(Info.Format, Left, 1)];
  2779. Dest := Data;
  2780. // Move past the header
  2781. Inc(Dest, Offset);
  2782. // Write lines from rect of the existing image
  2783. for I := 0 to Height - 1 do
  2784. begin
  2785. Move(Src^, Dest^, RectBytes);
  2786. Inc(Dest, RowLength);
  2787. Inc(Src, SrcScanBytes);
  2788. end;
  2789. end;
  2790. { Convenience/helper Functions }
  2791. procedure ResizeImageToFit(const SrcImage: TImageData; FitWidth, FitHeight: Integer;
  2792. Filter: TResizeFilter; var DestImage: TImageData);
  2793. var
  2794. CurSize, FitSize, DestSize: TSize;
  2795. begin
  2796. if not TestImage(SrcImage) then
  2797. raise EImagingError.Create(SErrorInvalidInputImage);
  2798. FitSize.CX := FitWidth;
  2799. FitSize.CY := FitHeight;
  2800. CurSize.CX := SrcImage.Width;
  2801. CurSize.CY := SrcImage.Height;
  2802. DestSize := ImagingUtility.ScaleSizeToFit(CurSize, FitSize);
  2803. NewImage(Max(DestSize.CX, 1), Max(DestSize.CY, 1), SrcImage.Format, DestImage);
  2804. if SrcImage.Palette <> nil then
  2805. CopyPalette(SrcImage.Palette, DestImage.Palette, 0, 0, ImageFormatInfos[SrcImage.Format].PaletteEntries);
  2806. StretchRect(SrcImage, 0, 0, CurSize.CX, CurSize.CY, DestImage, 0, 0,
  2807. DestSize.CX, DestSize.CY, Filter);
  2808. end;
  2809. { Color constructor functions }
  2810. function Color24(R, G, B: Byte): TColor24Rec;
  2811. begin
  2812. Result.R := R;
  2813. Result.G := G;
  2814. Result.B := B;
  2815. end;
  2816. function Color32(A, R, G, B: Byte): TColor32Rec;
  2817. begin
  2818. Result.A := A;
  2819. Result.R := R;
  2820. Result.G := G;
  2821. Result.B := B;
  2822. end;
  2823. function Color48(R, G, B: Word): TColor48Rec;
  2824. begin
  2825. Result.R := R;
  2826. Result.G := G;
  2827. Result.B := B;
  2828. end;
  2829. function Color64(A, R, G, B: Word): TColor64Rec;
  2830. begin
  2831. Result.A := A;
  2832. Result.R := R;
  2833. Result.G := G;
  2834. Result.B := B;
  2835. end;
  2836. function ColorFP(A, R, G, B: Single): TColorFPRec;
  2837. begin
  2838. Result.A := A;
  2839. Result.R := R;
  2840. Result.G := G;
  2841. Result.B := B;
  2842. end;
  2843. function ColorHF(A, R, G, B: THalfFloat): TColorHFRec;
  2844. begin
  2845. Result.A := A;
  2846. Result.R := R;
  2847. Result.G := G;
  2848. Result.B := B;
  2849. end;
  2850. function GetAlphaValue(Color32: TColor32): Byte;
  2851. begin
  2852. Result := Color32 shr 24;
  2853. end;
  2854. function GetRedValue(Color32: TColor32): Byte;
  2855. begin
  2856. Result := (Color32 shr 16) and $FF;
  2857. end;
  2858. function GetGreenValue(Color32: TColor32): Byte;
  2859. begin
  2860. Result := (Color32 shr 8) and $FF;
  2861. end;
  2862. function GetBlueValue(Color32: TColor32): Byte;
  2863. begin
  2864. Result := Color32 and $FF;
  2865. end;
  2866. { ------------------------------------------------------------------------
  2867. Other Imaging Stuff
  2868. ------------------------------------------------------------------------}
  2869. function GetFormatName(Format: TImageFormat): string;
  2870. begin
  2871. if ImageFormatInfos[Format] <> nil then
  2872. Result := ImageFormatInfos[Format].Name
  2873. else
  2874. Result := SUnknownFormat;
  2875. end;
  2876. function ImageToStr(const Image: TImageData): string;
  2877. var
  2878. ImgSize: Integer;
  2879. begin
  2880. if TestImage(Image) then
  2881. with Image do
  2882. begin
  2883. ImgSize := Size;
  2884. if ImgSize > 8192 then
  2885. ImgSize := ImgSize div 1024;
  2886. Result := SysUtils.Format(SImageInfo, [@Image, Width, Height,
  2887. GetFormatName(Format), ImgSize + 0.0, Iff(ImgSize = Size, 'B', 'KiB'), Bits,
  2888. Palette]);
  2889. end
  2890. else
  2891. Result := SysUtils.Format(SImageInfoInvalid, [@Image]);
  2892. end;
  2893. function GetVersionStr: string;
  2894. begin
  2895. Result := Format('%.1d.%.2d', [ImagingVersionMajor, ImagingVersionMinor]);
  2896. end;
  2897. function IffFormat(Condition: Boolean; const TruePart, FalsePart: TImageFormat): TImageFormat;
  2898. begin
  2899. if Condition then
  2900. Result := TruePart
  2901. else
  2902. Result := FalsePart;
  2903. end;
  2904. procedure RegisterImageFileFormat(AClass: TImageFileFormatClass);
  2905. begin
  2906. Assert(AClass <> nil);
  2907. if ImageFileFormats = nil then
  2908. ImageFileFormats := TList.Create;
  2909. if GlobalMetadata = nil then
  2910. GlobalMetadata := TMetadata.Create;
  2911. if ImageFileFormats <> nil then
  2912. ImageFileFormats.Add(AClass.Create);
  2913. end;
  2914. function RegisterOption(OptionId: LongInt; Variable: PLongInt): Boolean;
  2915. begin
  2916. Result := False;
  2917. if Options = nil then
  2918. InitOptions;
  2919. Assert(Variable <> nil);
  2920. if OptionId >= Length(Options) then
  2921. SetLength(Options, OptionId + InitialOptions);
  2922. if (OptionId >= 0) and (OptionId < Length(Options)) {and (Options[OptionId] = nil) - must be able to override existing } then
  2923. begin
  2924. Options[OptionId] := Variable;
  2925. Result := True;
  2926. end;
  2927. end;
  2928. function FindImageFileFormatByExt(const Ext: string): TImageFileFormat;
  2929. var
  2930. I: LongInt;
  2931. SearchedExt: string;
  2932. begin
  2933. Result := nil;
  2934. SearchedExt := TrimLeftSet(Ext, ['.']);
  2935. for I := ImageFileFormats.Count - 1 downto 0 do
  2936. if TImageFileFormat(ImageFileFormats[I]).Extensions.IndexOf(SearchedExt) >= 0 then
  2937. begin
  2938. Result := TImageFileFormat(ImageFileFormats[I]);
  2939. Exit;
  2940. end;
  2941. end;
  2942. function FindImageFileFormatByName(const FileName: string): TImageFileFormat;
  2943. var
  2944. I: LongInt;
  2945. begin
  2946. Result := nil;
  2947. if FileName = '' then
  2948. Exit;
  2949. for I := ImageFileFormats.Count - 1 downto 0 do
  2950. if TImageFileFormat(ImageFileFormats[I]).TestFileName(FileName) then
  2951. begin
  2952. Result := TImageFileFormat(ImageFileFormats[I]);
  2953. Exit;
  2954. end;
  2955. end;
  2956. function FindImageFileFormatByClass(AClass: TImageFileFormatClass): TImageFileFormat;
  2957. var
  2958. I: LongInt;
  2959. begin
  2960. Result := nil;
  2961. for I := 0 to ImageFileFormats.Count - 1 do
  2962. if TImageFileFormat(ImageFileFormats[I]) is AClass then
  2963. begin
  2964. Result := TObject(ImageFileFormats[I]) as TImageFileFormat;
  2965. Break;
  2966. end;
  2967. end;
  2968. function GetFileFormatCount: LongInt;
  2969. begin
  2970. Result := ImageFileFormats.Count;
  2971. end;
  2972. function GetFileFormatAtIndex(Index: LongInt): TImageFileFormat;
  2973. begin
  2974. if (Index >= 0) and (Index < ImageFileFormats.Count) then
  2975. Result := TImageFileFormat(ImageFileFormats[Index])
  2976. else
  2977. Result := nil;
  2978. end;
  2979. function GetImageFileFormatsFilter(OpenFileFilter: Boolean): string;
  2980. var
  2981. I, J, Count: LongInt;
  2982. Descriptions: string;
  2983. Filters, CurFilter: string;
  2984. FileFormat: TImageFileFormat;
  2985. begin
  2986. Descriptions := '';
  2987. Filters := '';
  2988. Count := 0;
  2989. for I := 0 to ImageFileFormats.Count - 1 do
  2990. begin
  2991. FileFormat := TObject(ImageFileFormats[I]) as TImageFileFormat;
  2992. // If we are creating filter for save dialog and this format cannot save
  2993. // files the we skip it
  2994. if not OpenFileFilter and not FileFormat.CanSave then
  2995. Continue;
  2996. CurFilter := '';
  2997. for J := 0 to FileFormat.Masks.Count - 1 do
  2998. begin
  2999. CurFilter := CurFilter + FileFormat.Masks[J];
  3000. if J < FileFormat.Masks.Count - 1 then
  3001. CurFilter := CurFilter + ';';
  3002. end;
  3003. FmtStr(Descriptions, '%s%s (%s)|%2:s', [Descriptions, FileFormat.Name, CurFilter]);
  3004. if Filters <> '' then
  3005. FmtStr(Filters, '%s;%s', [Filters, CurFilter])
  3006. else
  3007. Filters := CurFilter;
  3008. if I < ImageFileFormats.Count - 1 then
  3009. Descriptions := Descriptions + '|';
  3010. Inc(Count);
  3011. end;
  3012. if (Count > 1) and OpenFileFilter then
  3013. FmtStr(Descriptions, '%s (%s)|%1:s|%s', [SAllFilter, Filters, Descriptions]);
  3014. Result := Descriptions;
  3015. end;
  3016. function GetFilterIndexExtension(Index: LongInt; OpenFileFilter: Boolean): string;
  3017. var
  3018. I, Count: LongInt;
  3019. FileFormat: TImageFileFormat;
  3020. begin
  3021. // -1 because filter indices are in 1..n range
  3022. Index := Index - 1;
  3023. Result := '';
  3024. if OpenFileFilter then
  3025. begin
  3026. if Index > 0 then
  3027. Index := Index - 1;
  3028. end;
  3029. if (Index >= 0) and (Index < ImageFileFormats.Count) then
  3030. begin
  3031. Count := 0;
  3032. for I := 0 to ImageFileFormats.Count - 1 do
  3033. begin
  3034. FileFormat := TObject(ImageFileFormats[I]) as TImageFileFormat;
  3035. if not OpenFileFilter and not FileFormat.CanSave then
  3036. Continue;
  3037. if Index = Count then
  3038. begin
  3039. if FileFormat.Extensions.Count > 0 then
  3040. Result := FileFormat.Extensions[0];
  3041. Exit;
  3042. end;
  3043. Inc(Count);
  3044. end;
  3045. end;
  3046. end;
  3047. function GetFileNameFilterIndex(const FileName: string; OpenFileFilter: Boolean): LongInt;
  3048. var
  3049. I: LongInt;
  3050. FileFormat: TImageFileFormat;
  3051. begin
  3052. Result := 0;
  3053. for I := 0 to ImageFileFormats.Count - 1 do
  3054. begin
  3055. FileFormat := TObject(ImageFileFormats[I]) as TImageFileFormat;
  3056. if not OpenFileFilter and not FileFormat.CanSave then
  3057. Continue;
  3058. if FileFormat.TestFileName(FileName) then
  3059. begin
  3060. // +1 because filter indices are in 1..n range
  3061. Inc(Result);
  3062. if OpenFileFilter then
  3063. Inc(Result);
  3064. Exit;
  3065. end;
  3066. Inc(Result);
  3067. end;
  3068. Result := -1;
  3069. end;
  3070. function GetIO: TIOFunctions;
  3071. begin
  3072. Result := IO;
  3073. end;
  3074. procedure RaiseImaging(const Msg: string; const Args: array of const);
  3075. var
  3076. WholeMsg: string;
  3077. begin
  3078. WholeMsg := Msg;
  3079. if GetExceptObject <> nil then
  3080. begin
  3081. WholeMsg := WholeMsg + ' ' + SExceptMsg + ': ' +
  3082. GetExceptObject.Message;
  3083. end;
  3084. raise EImagingError.CreateFmt(WholeMsg, Args);
  3085. end;
  3086. procedure RaiseImaging(const Msg: string);
  3087. begin
  3088. RaiseImaging(Msg, []);
  3089. end;
  3090. { Internal unit functions }
  3091. function CheckOptionValue(OptionId, Value: LongInt): LongInt;
  3092. begin
  3093. case OptionId of
  3094. ImagingColorReductionMask:
  3095. Result := ClampInt(Value, 0, $FF);
  3096. ImagingLoadOverrideFormat, ImagingSaveOverrideFormat:
  3097. Result := Iff(ImagingFormats.IsImageFormatValid(TImageFormat(Value)),
  3098. Value, LongInt(ifUnknown));
  3099. ImagingMipMapFilter: Result := ClampInt(Value, Ord(Low(TSamplingFilter)),
  3100. Ord(High(TSamplingFilter)));
  3101. else
  3102. Result := Value;
  3103. end;
  3104. end;
  3105. procedure SetFileIO;
  3106. begin
  3107. IO := FileIO;
  3108. end;
  3109. procedure SetStreamIO;
  3110. begin
  3111. IO := StreamIO;
  3112. end;
  3113. procedure SetMemoryIO;
  3114. begin
  3115. IO := MemoryIO;
  3116. end;
  3117. procedure InitImageFormats;
  3118. begin
  3119. ImagingFormats.InitImageFormats(ImageFormatInfos);
  3120. end;
  3121. procedure FreeImageFileFormats;
  3122. var
  3123. I: LongInt;
  3124. begin
  3125. if ImageFileFormats <> nil then
  3126. for I := 0 to ImageFileFormats.Count - 1 do
  3127. TImageFileFormat(ImageFileFormats[I]).Free;
  3128. FreeAndNil(ImageFileFormats);
  3129. end;
  3130. procedure InitOptions;
  3131. begin
  3132. SetLength(Options, InitialOptions);
  3133. OptionStack := TOptionStack.Create;
  3134. end;
  3135. procedure FreeOptions;
  3136. begin
  3137. SetLength(Options, 0);
  3138. FreeAndNil(OptionStack);
  3139. end;
  3140. {
  3141. TImageFileFormat class implementation
  3142. }
  3143. constructor TImageFileFormat.Create(AMetadata: TMetadata);
  3144. begin
  3145. inherited Create;
  3146. FName := SUnknownFormat;
  3147. FExtensions := TStringList.Create;
  3148. FMasks := TStringList.Create;
  3149. if AMetadata = nil then
  3150. FMetadata := GlobalMetadata
  3151. else
  3152. FMetadata := AMetadata;
  3153. Define;
  3154. end;
  3155. destructor TImageFileFormat.Destroy;
  3156. begin
  3157. FExtensions.Free;
  3158. FMasks.Free;
  3159. inherited Destroy;
  3160. end;
  3161. procedure TImageFileFormat.Define;
  3162. begin
  3163. end;
  3164. function TImageFileFormat.PrepareLoad(Handle: TImagingHandle;
  3165. var Images: TDynImageDataArray; OnlyFirstFrame: Boolean): Boolean;
  3166. begin
  3167. FMetadata.ClearMetaItems; // Clear old metadata
  3168. FreeImagesInArray(Images);
  3169. SetLength(Images, 0);
  3170. Result := Handle <> nil;
  3171. end;
  3172. function TImageFileFormat.PostLoadCheck(var Images: TDynImageDataArray;
  3173. LoadResult: Boolean): Boolean;
  3174. var
  3175. I: LongInt;
  3176. begin
  3177. if not LoadResult then
  3178. begin
  3179. FreeImagesInArray(Images);
  3180. SetLength(Images, 0);
  3181. Result := False;
  3182. end
  3183. else
  3184. begin
  3185. Result := (Length(Images) > 0) and TestImagesInArray(Images);
  3186. if Result then
  3187. begin
  3188. // Convert to overridden format if it is set
  3189. if LoadOverrideFormat <> ifUnknown then
  3190. for I := Low(Images) to High(Images) do
  3191. ConvertImage(Images[I], LoadOverrideFormat);
  3192. end;
  3193. end;
  3194. end;
  3195. function TImageFileFormat.PrepareSave(Handle: TImagingHandle;
  3196. const Images: TDynImageDataArray; var Index: LongInt): Boolean;
  3197. var
  3198. Len, I: LongInt;
  3199. begin
  3200. CheckOptionsValidity;
  3201. Result := False;
  3202. if CanSave then
  3203. begin
  3204. Len := Length(Images);
  3205. Assert(Len > 0);
  3206. // If there are no images to be saved exit
  3207. if Len = 0 then Exit;
  3208. // Check index of image to be saved (-1 as index means save all images)
  3209. if IsMultiImageFormat then
  3210. begin
  3211. if (Index >= Len) then
  3212. Index := 0;
  3213. if Index < 0 then
  3214. begin
  3215. Index := 0;
  3216. FFirstIdx := 0;
  3217. FLastIdx := Len - 1;
  3218. end
  3219. else
  3220. begin
  3221. FFirstIdx := Index;
  3222. FLastIdx := Index;
  3223. end;
  3224. for I := FFirstIdx to FLastIdx - 1 do
  3225. begin
  3226. if not TestImage(Images[I]) then
  3227. Exit;
  3228. end;
  3229. end
  3230. else
  3231. begin
  3232. if (Index >= Len) or (Index < 0) then
  3233. Index := 0;
  3234. if not TestImage(Images[Index]) then
  3235. Exit;
  3236. end;
  3237. Result := True;
  3238. end;
  3239. end;
  3240. procedure TImageFileFormat.AddMasks(const AMasks: string);
  3241. var
  3242. I: LongInt;
  3243. Ext: string;
  3244. begin
  3245. FExtensions.Clear;
  3246. FMasks.CommaText := AMasks;
  3247. FMasks.Delimiter := ';';
  3248. for I := 0 to FMasks.Count - 1 do
  3249. begin
  3250. FMasks[I] := Trim(FMasks[I]);
  3251. Ext := GetFileExt(FMasks[I]);
  3252. if (Ext <> '') and (Ext <> '*') then
  3253. FExtensions.Add(Ext);
  3254. end;
  3255. end;
  3256. function TImageFileFormat.GetFormatInfo(Format: TImageFormat): TImageFormatInfo;
  3257. begin
  3258. Result := ImageFormatInfos[Format]^;
  3259. end;
  3260. function TImageFileFormat.GetSupportedFormats: TImageFormats;
  3261. begin
  3262. Result := FSupportedFormats;
  3263. end;
  3264. function TImageFileFormat.LoadData(Handle: TImagingHandle;
  3265. var Images: TDynImageDataArray; OnlyFirstFrame: Boolean): Boolean;
  3266. begin
  3267. Result := False;
  3268. RaiseImaging(SFileFormatCanNotLoad, [FName]);
  3269. end;
  3270. function TImageFileFormat.SaveData(Handle: TImagingHandle;
  3271. const Images: TDynImageDataArray; Index: LongInt): Boolean;
  3272. begin
  3273. Result := False;
  3274. RaiseImaging(SFileFormatCanNotSave, [FName]);
  3275. end;
  3276. procedure TImageFileFormat.ConvertToSupported(var Image: TImageData;
  3277. const Info: TImageFormatInfo);
  3278. begin
  3279. end;
  3280. function TImageFileFormat.IsSupported(const Image: TImageData): Boolean;
  3281. begin
  3282. Result := Image.Format in GetSupportedFormats;
  3283. end;
  3284. function TImageFileFormat.LoadFromFile(const FileName: string;
  3285. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  3286. var
  3287. Handle: TImagingHandle;
  3288. begin
  3289. Result := False;
  3290. if CanLoad then
  3291. try
  3292. // Set IO ops to file ops and open given file
  3293. SetFileIO;
  3294. Handle := IO.Open(PChar(FileName), omReadOnly);
  3295. try
  3296. // Test if file contains valid image and if so then load it
  3297. if TestFormat(Handle) then
  3298. begin
  3299. Result := PrepareLoad(Handle, Images, OnlyFirstLevel) and
  3300. LoadData(Handle, Images, OnlyFirstLevel);
  3301. Result := PostLoadCheck(Images, Result);
  3302. end
  3303. else
  3304. RaiseImaging(SFileNotValid, [FileName, Name]);
  3305. finally
  3306. IO.Close(Handle);
  3307. end;
  3308. except
  3309. RaiseImaging(SErrorLoadingFile, [FileName, FExtensions[0]]);
  3310. end;
  3311. end;
  3312. function TImageFileFormat.LoadFromStream(Stream: TStream;
  3313. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  3314. var
  3315. Handle: TImagingHandle;
  3316. OldPosition: Int64;
  3317. begin
  3318. Result := False;
  3319. OldPosition := Stream.Position;
  3320. if CanLoad then
  3321. try
  3322. // Set IO ops to stream ops and "open" given memory
  3323. SetStreamIO;
  3324. Handle := IO.Open(Pointer(Stream), omReadOnly);
  3325. try
  3326. // Test if stream contains valid image and if so then load it
  3327. if TestFormat(Handle) then
  3328. begin
  3329. Result := PrepareLoad(Handle, Images, OnlyFirstLevel) and
  3330. LoadData(Handle, Images, OnlyFirstLevel);
  3331. Result := PostLoadCheck(Images, Result);
  3332. end
  3333. else
  3334. RaiseImaging(SStreamNotValid, [@Stream, Name]);
  3335. finally
  3336. IO.Close(Handle);
  3337. end;
  3338. except
  3339. Stream.Position := OldPosition;
  3340. FreeImagesInArray(Images);
  3341. RaiseImaging(SErrorLoadingStream, [@Stream, FExtensions[0]]);
  3342. end;
  3343. end;
  3344. function TImageFileFormat.LoadFromMemory(Data: Pointer; Size: LongInt; var
  3345. Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  3346. var
  3347. Handle: TImagingHandle;
  3348. IORec: TMemoryIORec;
  3349. begin
  3350. Result := False;
  3351. if CanLoad then
  3352. try
  3353. // Set IO ops to memory ops and "open" given memory
  3354. SetMemoryIO;
  3355. IORec := PrepareMemIO(Data, Size);
  3356. Handle := IO.Open(@IORec,omReadOnly);
  3357. try
  3358. // Test if memory contains valid image and if so then load it
  3359. if TestFormat(Handle) then
  3360. begin
  3361. Result := PrepareLoad(Handle, Images, OnlyFirstLevel) and
  3362. LoadData(Handle, Images, OnlyFirstLevel);
  3363. Result := PostLoadCheck(Images, Result);
  3364. end
  3365. else
  3366. RaiseImaging(SMemoryNotValid, [Data, Size, Name]);
  3367. finally
  3368. IO.Close(Handle);
  3369. end;
  3370. except
  3371. RaiseImaging(SErrorLoadingMemory, [Data, Size, FExtensions[0]]);
  3372. end;
  3373. end;
  3374. function TImageFileFormat.SaveToFile(const FileName: string;
  3375. const Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  3376. var
  3377. Handle: TImagingHandle;
  3378. Len, Index, I: LongInt;
  3379. Ext, FName: string;
  3380. begin
  3381. Result := False;
  3382. if CanSave and TestImagesInArray(Images) then
  3383. try
  3384. SetFileIO;
  3385. Len := Length(Images);
  3386. if IsMultiImageFormat or
  3387. (not IsMultiImageFormat and (OnlyFirstLevel or (Len = 1))) then
  3388. begin
  3389. Handle := IO.Open(PChar(FileName), omCreate);
  3390. try
  3391. if OnlyFirstLevel then
  3392. Index := 0
  3393. else
  3394. Index := -1;
  3395. // Write multi image to one file
  3396. Result := PrepareSave(Handle, Images, Index) and SaveData(Handle, Images, Index);
  3397. finally
  3398. IO.Close(Handle);
  3399. end;
  3400. end
  3401. else
  3402. begin
  3403. // Write multi image to file sequence
  3404. Ext := ExtractFileExt(FileName);
  3405. FName := ChangeFileExt(FileName, '');
  3406. Result := True;
  3407. for I := 0 to Len - 1 do
  3408. begin
  3409. Handle := IO.Open(PChar(Format(FName + '%.3d' + Ext, [I])), omCreate);
  3410. try
  3411. Index := I;
  3412. Result := Result and PrepareSave(Handle, Images, Index) and
  3413. SaveData(Handle, Images, Index);
  3414. if not Result then
  3415. Break;
  3416. finally
  3417. IO.Close(Handle);
  3418. end;
  3419. end;
  3420. end;
  3421. except
  3422. raise UpdateExceptMessage(GetExceptObject, SErrorSavingFile, [FileName, FExtensions[0]]);
  3423. end;
  3424. end;
  3425. function TImageFileFormat.SaveToStream(Stream: TStream;
  3426. const Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  3427. var
  3428. Handle: TImagingHandle;
  3429. Len, Index, I: LongInt;
  3430. OldPosition: Int64;
  3431. begin
  3432. Result := False;
  3433. OldPosition := Stream.Position;
  3434. if CanSave and TestImagesInArray(Images) then
  3435. try
  3436. SetStreamIO;
  3437. Handle := IO.Open(PChar(Stream), omCreate);
  3438. try
  3439. if IsMultiImageFormat or OnlyFirstLevel then
  3440. begin
  3441. if OnlyFirstLevel then
  3442. Index := 0
  3443. else
  3444. Index := -1;
  3445. // Write multi image in one run
  3446. Result := PrepareSave(Handle, Images, Index) and SaveData(Handle, Images, Index);
  3447. end
  3448. else
  3449. begin
  3450. // Write multi image to sequence
  3451. Result := True;
  3452. Len := Length(Images);
  3453. for I := 0 to Len - 1 do
  3454. begin
  3455. Index := I;
  3456. Result := Result and PrepareSave(Handle, Images, Index) and
  3457. SaveData(Handle, Images, Index);
  3458. if not Result then
  3459. Break;
  3460. end;
  3461. end;
  3462. finally
  3463. IO.Close(Handle);
  3464. end;
  3465. except
  3466. Stream.Position := OldPosition;
  3467. raise UpdateExceptMessage(GetExceptObject, SErrorSavingStream, [@Stream, FExtensions[0]]);
  3468. end;
  3469. end;
  3470. function TImageFileFormat.SaveToMemory(Data: Pointer; var Size: LongInt;
  3471. const Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  3472. var
  3473. Handle: TImagingHandle;
  3474. Len, Index, I: LongInt;
  3475. IORec: TMemoryIORec;
  3476. begin
  3477. Result := False;
  3478. if CanSave and TestImagesInArray(Images) then
  3479. try
  3480. SetMemoryIO;
  3481. IORec := PrepareMemIO(Data, Size);
  3482. Handle := IO.Open(PChar(@IORec), omCreate);
  3483. try
  3484. if IsMultiImageFormat or OnlyFirstLevel then
  3485. begin
  3486. if OnlyFirstLevel then
  3487. Index := 0
  3488. else
  3489. Index := -1;
  3490. // Write multi image in one run
  3491. Result := PrepareSave(Handle, Images, Index) and SaveData(Handle, Images, Index);
  3492. end
  3493. else
  3494. begin
  3495. // Write multi image to sequence
  3496. Result := True;
  3497. Len := Length(Images);
  3498. for I := 0 to Len - 1 do
  3499. begin
  3500. Index := I;
  3501. Result := Result and PrepareSave(Handle, Images, Index) and
  3502. SaveData(Handle, Images, Index);
  3503. if not Result then
  3504. Break;
  3505. end;
  3506. end;
  3507. Size := IORec.Position;
  3508. finally
  3509. IO.Close(Handle);
  3510. end;
  3511. except
  3512. raise UpdateExceptMessage(GetExceptObject, SErrorSavingMemory, [Data, Size, FExtensions[0]]);
  3513. end;
  3514. end;
  3515. function TImageFileFormat.MakeCompatible(const Image: TImageData;
  3516. var Compatible: TImageData; out MustBeFreed: Boolean): Boolean;
  3517. begin
  3518. InitImage(Compatible);
  3519. if SaveOverrideFormat <> ifUnknown then
  3520. begin
  3521. // Save format override is active. Clone input and convert it to override format.
  3522. CloneImage(Image, Compatible);
  3523. ConvertImage(Compatible, SaveOverrideFormat);
  3524. // Now check if override format is supported by file format. If it is not
  3525. // then file format specific conversion (virtual method) is called.
  3526. Result := IsSupported(Compatible);
  3527. if not Result then
  3528. begin
  3529. ConvertToSupported(Compatible, GetFormatInfo(Compatible.Format));
  3530. Result := IsSupported(Compatible);
  3531. end;
  3532. end // Add IsCompatible function! not only checking by Format
  3533. else if IsSupported(Image) then
  3534. begin
  3535. // No save format override and input is in format supported by this
  3536. // file format. Just copy Image's fields to Compatible
  3537. Compatible := Image;
  3538. Result := True;
  3539. end
  3540. else
  3541. begin
  3542. // No override and input's format is not compatible with file format.
  3543. // Clone it and the call file format specific conversion (virtual method).
  3544. CloneImage(Image, Compatible);
  3545. ConvertToSupported(Compatible, GetFormatInfo(Compatible.Format));
  3546. Result := IsSupported(Compatible);
  3547. end;
  3548. // Tell the user that he must free Compatible after he's done with it
  3549. // (if necessary).
  3550. MustBeFreed := Image.Bits <> Compatible.Bits;
  3551. end;
  3552. function TImageFileFormat.TestFormat(Handle: TImagingHandle): Boolean;
  3553. begin
  3554. Result := False;
  3555. end;
  3556. function TImageFileFormat.TestFileName(const FileName: string): Boolean;
  3557. var
  3558. I: LongInt;
  3559. OnlyName: string;
  3560. begin
  3561. OnlyName := ExtractFileName(FileName);
  3562. // For each mask test if filename matches it
  3563. for I := 0 to FMasks.Count - 1 do
  3564. if StrMaskMatch(OnlyName, FMasks[I], False) then
  3565. begin
  3566. Result := True;
  3567. Exit;
  3568. end;
  3569. Result := False;
  3570. end;
  3571. procedure TImageFileFormat.CheckOptionsValidity;
  3572. begin
  3573. end;
  3574. function TImageFileFormat.GetCanLoad: Boolean;
  3575. begin
  3576. Result := ffLoad in FFeatures;
  3577. end;
  3578. function TImageFileFormat.GetCanSave: Boolean;
  3579. begin
  3580. Result := ffSave in FFeatures;
  3581. end;
  3582. function TImageFileFormat.GetIsMultiImageFormat: Boolean;
  3583. begin
  3584. Result := ffMultiImage in FFeatures;
  3585. end;
  3586. { TOptionStack class implementation }
  3587. constructor TOptionStack.Create;
  3588. begin
  3589. inherited Create;
  3590. FPosition := -1;
  3591. end;
  3592. destructor TOptionStack.Destroy;
  3593. var
  3594. I: LongInt;
  3595. begin
  3596. for I := 0 to OptionStackDepth - 1 do
  3597. SetLength(FStack[I], 0);
  3598. inherited Destroy;
  3599. end;
  3600. function TOptionStack.Pop: Boolean;
  3601. var
  3602. I: LongInt;
  3603. begin
  3604. Result := False;
  3605. if FPosition >= 0 then
  3606. begin
  3607. SetLength(Options, Length(FStack[FPosition]));
  3608. for I := 0 to Length(FStack[FPosition]) - 1 do
  3609. if Options[I] <> nil then
  3610. Options[I]^ := FStack[FPosition, I];
  3611. Dec(FPosition);
  3612. Result := True;
  3613. end;
  3614. end;
  3615. function TOptionStack.Push: Boolean;
  3616. var
  3617. I: LongInt;
  3618. begin
  3619. Result := False;
  3620. if FPosition < OptionStackDepth - 1 then
  3621. begin
  3622. Inc(FPosition);
  3623. SetLength(FStack[FPosition], Length(Options));
  3624. for I := 0 to Length(Options) - 1 do
  3625. if Options[I] <> nil then
  3626. FStack[FPosition, I] := Options[I]^;
  3627. Result := True;
  3628. end;
  3629. end;
  3630. { TMetadata }
  3631. procedure TMetadata.SetMetaItem(const Id: string; const Value: Variant;
  3632. ImageIndex: Integer);
  3633. begin
  3634. AddMetaToList(FLoadMetaItems, Id, Value, ImageIndex);
  3635. end;
  3636. procedure TMetadata.SetMetaItemForSaving(const Id: string; const Value: Variant;
  3637. ImageIndex: Integer);
  3638. begin
  3639. AddMetaToList(FSaveMetaItems, Id, Value, ImageIndex);
  3640. end;
  3641. procedure TMetadata.AddMetaToList(List: TStringList; const Id: string;
  3642. const Value: Variant; ImageIndex: Integer);
  3643. var
  3644. Item: TMetadataItem;
  3645. Idx: Integer;
  3646. FullId: string;
  3647. begin
  3648. FullId := GetMetaItemName(Id, ImageIndex);
  3649. if List.Find(FullId, Idx) then
  3650. (List.Objects[Idx] as TMetadataItem).Value := Value
  3651. else
  3652. begin
  3653. Item := TMetadataItem.Create;
  3654. Item.Id := Id;
  3655. Item.ImageIndex := ImageIndex;
  3656. Item.Value := Value;
  3657. List.AddObject(FullId, Item);
  3658. end;
  3659. end;
  3660. procedure TMetadata.ClearMetaItems;
  3661. begin
  3662. ClearMetaList(FLoadMetaItems);
  3663. end;
  3664. procedure TMetadata.ClearMetaItemsForSaving;
  3665. begin
  3666. ClearMetaList(FSaveMetaItems);
  3667. end;
  3668. procedure TMetadata.ClearMetaList(List: TStringList);
  3669. var
  3670. I: Integer;
  3671. begin
  3672. for I := 0 to List.Count - 1 do
  3673. List.Objects[I].Free;
  3674. List.Clear;
  3675. end;
  3676. procedure TMetadata.CopyLoadedMetaItemsForSaving;
  3677. var
  3678. I: Integer;
  3679. Copy, Orig: TMetadataItem;
  3680. begin
  3681. ClearMetaItemsForSaving;
  3682. for I := 0 to FLoadMetaItems.Count - 1 do
  3683. begin
  3684. Orig := TMetadataItem(FLoadMetaItems.Objects[I]);
  3685. Copy := TMetadataItem.Create;
  3686. Copy.Id := Orig.Id;
  3687. Copy.ImageIndex := Orig.ImageIndex;
  3688. Copy.Value := Orig.Value;
  3689. FSaveMetaItems.AddObject(GetMetaItemName(Copy.Id, Copy.ImageIndex), Copy);
  3690. end;
  3691. end;
  3692. constructor TMetadata.Create;
  3693. begin
  3694. inherited;
  3695. FLoadMetaItems := TStringList.Create;
  3696. FLoadMetaItems.Sorted := True;
  3697. FSaveMetaItems := TStringList.Create;
  3698. FSaveMetaItems.Sorted := True;
  3699. end;
  3700. destructor TMetadata.Destroy;
  3701. begin
  3702. ClearMetaItems;
  3703. ClearMetaItemsForSaving;
  3704. FLoadMetaItems.Free;
  3705. FSaveMetaItems.Free;
  3706. inherited;
  3707. end;
  3708. function TMetadata.GetMetaById(const Id: string): Variant;
  3709. var
  3710. Idx: Integer;
  3711. begin
  3712. if FLoadMetaItems.Find(Id, Idx) then
  3713. Result := (FLoadMetaItems.Objects[Idx] as TMetadataItem).Value
  3714. else
  3715. Result := Variants.Null;
  3716. end;
  3717. function TMetadata.GetMetaByIdMulti(const Id: string; ImageIndex: Integer): Variant;
  3718. begin
  3719. Result := GetMetaById(GetMetaItemName(Id, ImageIndex));
  3720. end;
  3721. function TMetadata.GetSaveMetaById(const Id: string): Variant;
  3722. var
  3723. Idx: Integer;
  3724. begin
  3725. if FSaveMetaItems.Find(Id, Idx) then
  3726. Result := (FSaveMetaItems.Objects[Idx] as TMetadataItem).Value
  3727. else
  3728. Result := Variants.Null;
  3729. end;
  3730. function TMetadata.GetSaveMetaByIdMulti(const Id: string;
  3731. ImageIndex: Integer): Variant;
  3732. begin
  3733. Result := GetSaveMetaById(GetMetaItemName(Id, ImageIndex));
  3734. end;
  3735. function TMetadata.GetMetaByIdx(Index: Integer): TMetadataItem;
  3736. begin
  3737. Result := FLoadMetaItems.Objects[Index] as TMetadataItem;
  3738. end;
  3739. function TMetadata.GetMetaCount: Integer;
  3740. begin
  3741. Result := FLoadMetaItems.Count;
  3742. end;
  3743. function TMetadata.GetMetaItemName(const Id: string;
  3744. ImageIndex: Integer): string;
  3745. begin
  3746. Result := Iff(ImageIndex = 0, Id, Format(SMetaIdForSubImage, [Id, ImageIndex]));
  3747. end;
  3748. function TMetadata.GetPhysicalPixelSize(ResUnit: TResolutionUnit; out XSize,
  3749. YSize: Double; MetaForSave: Boolean; ImageIndex: Integer): Boolean;
  3750. type
  3751. TGetter = function(const Id: string; ImageIndex: Integer): Variant of object;
  3752. var
  3753. Getter: TGetter;
  3754. XMeta, YMeta: Variant;
  3755. begin
  3756. if MetaForSave then
  3757. Getter := GetSaveMetaByIdMulti
  3758. else
  3759. Getter := GetMetaByIdMulti;
  3760. XMeta := Getter(SMetaPhysicalPixelSizeX, ImageIndex);
  3761. YMeta := Getter(SMetaPhysicalPixelSizeY, ImageIndex);
  3762. XSize := -1;
  3763. YSize := -1;
  3764. Result := not VarIsNull(XMeta) or not VarIsNull(YMeta);
  3765. if not Result then
  3766. Exit;
  3767. if not VarIsNull(XMeta) then
  3768. XSize := XMeta;
  3769. if not VarIsNull(YMeta) then
  3770. YSize := YMeta;
  3771. if XSize < 0 then
  3772. XSize := YSize;
  3773. if YSize < 0 then
  3774. YSize := XSize;
  3775. TranslateUnits(ResUnit, XSize, YSize);
  3776. end;
  3777. procedure TMetadata.SetPhysicalPixelSize(ResUnit: TResolutionUnit; XSize,
  3778. YSize: Double; MetaForSave: Boolean; ImageIndex: Integer);
  3779. type
  3780. TAdder = procedure(const Id: string; const Value: Variant; ImageIndex: Integer) of object;
  3781. var
  3782. Adder: TAdder;
  3783. begin
  3784. TranslateUnits(ResUnit, XSize, YSize);
  3785. if MetaForSave then
  3786. Adder := SetMetaItemForSaving
  3787. else
  3788. Adder := SetMetaItem;
  3789. Adder(SMetaPhysicalPixelSizeX, XSize, ImageIndex);
  3790. Adder(SMetaPhysicalPixelSizeY, YSize, ImageIndex);
  3791. end;
  3792. procedure TMetadata.TranslateUnits(ResolutionUnit: TResolutionUnit; var XRes,
  3793. YRes: Double);
  3794. var
  3795. UnitSize: Double;
  3796. begin
  3797. case ResolutionUnit of
  3798. ruDpi: UnitSize := 25400;
  3799. ruDpm: UnitSize := 1e06;
  3800. ruDpcm: UnitSize := 1e04;
  3801. else
  3802. UnitSize := 1;
  3803. end;
  3804. if ResolutionUnit <> ruSizeInMicroMeters then
  3805. begin
  3806. XRes := UnitSize / XRes;
  3807. YRes := UnitSize / YRes;
  3808. end;
  3809. end;
  3810. function TMetadata.HasMetaItem(const Id: string; ImageIndex: Integer): Boolean;
  3811. begin
  3812. Result := GetMetaByIdMulti(Id, ImageIndex) <> Variants.Null;
  3813. end;
  3814. function TMetadata.HasMetaItemForSaving(const Id: string; ImageIndex: Integer): Boolean;
  3815. begin
  3816. Result := GetSaveMetaByIdMulti(Id, ImageIndex) <> Variants.Null;
  3817. end;
  3818. initialization
  3819. {$IFDEF MEMCHECK}
  3820. {$IF CompilerVersion >= 18}
  3821. System.ReportMemoryLeaksOnShutdown := True;
  3822. {$IFEND}
  3823. {$ENDIF}
  3824. if GlobalMetadata = nil then
  3825. GlobalMetadata := TMetadata.Create;
  3826. if ImageFileFormats = nil then
  3827. ImageFileFormats := TList.Create;
  3828. InitImageFormats;
  3829. RegisterOption(ImagingColorReductionMask, @ColorReductionMask);
  3830. RegisterOption(ImagingLoadOverrideFormat, @LoadOverrideFormat);
  3831. RegisterOption(ImagingSaveOverrideFormat, @SaveOverrideFormat);
  3832. RegisterOption(ImagingMipMapFilter, @MipMapFilter);
  3833. RegisterOption(ImagingBinaryThreshold, @BinaryThreshold);
  3834. finalization
  3835. FreeOptions;
  3836. FreeImageFileFormats;
  3837. GlobalMetadata.Free;
  3838. {
  3839. File Notes (obsolete):
  3840. -- 0.80 ------------------------------------------------------
  3841. - Added new color records constructor functions (Color24(..), Color32(..)).
  3842. - Added convenience channel getters for TColor32 (GetGreenValue, ...).
  3843. -- 0.77.1 ---------------------------------------------------
  3844. - Updated IO Open functions according to changes in ImagingTypes.
  3845. - Fixed bug in SplitImage that could cause wrong size of edge chunks.
  3846. - Metadata support fixes and extensions (frame delays, animation loops).
  3847. -- 0.26.5 Changes/Bug Fixes ---------------------------------
  3848. - Started reworking exception raising to keep the original class type
  3849. (e.g. in NewImage EOutOfMemory could be raised but was hidden
  3850. by EImagingError raised afterwards in NewImage try/except).
  3851. - Fixed possible AV in Rotate45 subproc of RotateImage.
  3852. - Added ReadRawXXX and WriteRawXXX functions for raw image bits IO.
  3853. - Implemented ImagingBinaryThreshold option.
  3854. - Added support for simple image metadata loading/saving.
  3855. - Moved file format definition (name, exts, caps, ...) from
  3856. constructor to new Define method.
  3857. - Fixed some memory leaks caused by failures during image loading.
  3858. -- 0.26.3 Changes/Bug Fixes ---------------------------------
  3859. - Extended RotateImage to allow arbitrary angle rotations.
  3860. - Reversed the order file formats list is searched so
  3861. if you register a new one it will be found sooner than
  3862. built in formats.
  3863. - Fixed memory leak in ResizeImage occurring when resizing
  3864. indexed images.
  3865. -- 0.26.1 Changes/Bug Fixes ---------------------------------
  3866. - Added position/size checks to LoadFromStream functions.
  3867. - Changed conditional compilation in impl. uses section to reflect changes
  3868. in LINK symbols.
  3869. -- 0.24.3 Changes/Bug Fixes ---------------------------------
  3870. - GenerateMipMaps now generates all smaller levels from
  3871. original big image (better results when using more advanced filters).
  3872. Also conversion to compatible image format is now done here not
  3873. in FillMipMapLevel (that is called for every mipmap level).
  3874. -- 0.23 Changes/Bug Fixes -----------------------------------
  3875. - MakePaletteForImages now works correctly for indexed and special format images
  3876. - Fixed bug in StretchRect: Image was not properly stretched if
  3877. src and dst dimensions differed only in height.
  3878. - ConvertImage now fills new image with zeroes to avoid random data in
  3879. some conversions (RGB->XRGB)
  3880. - Changed RegisterOption procedure to function
  3881. - Changed bunch of palette functions from low level interface to procedure
  3882. (there was no reason for them to be functions).
  3883. - Changed FreeImage and FreeImagesInArray functions to procedures.
  3884. - Added many assertions, come try-finally, other checks, and small code
  3885. and doc changes.
  3886. -- 0.21 Changes/Bug Fixes -----------------------------------
  3887. - GenerateMipMaps threw failed assertion when input was indexed or special,
  3888. fixed.
  3889. - Added CheckOptionsValidity to TImageFileFormat and its descendants.
  3890. - Unit ImagingExtras which registers file formats in Extras package
  3891. is now automatically added to uses clause if LINK_EXTRAS symbol is
  3892. defined in ImagingOptions.inc file.
  3893. - Added EnumFileFormats function to low level interface.
  3894. - Fixed bug in SwapChannels which could cause AV when swapping alpha
  3895. channel of A8R8G8B8 images.
  3896. - Converting loaded images to ImagingOverrideFormat is now done
  3897. in PostLoadCheck method to avoid code duplicity.
  3898. - Added GetFileFormatCount and GetFileFormatAtIndex functions
  3899. - Bug in ConvertImage: if some format was converted to similar format
  3900. only with swapped channels (R16G16B16<>B16G16R16) then channels were
  3901. swapped correctly but new data format (swapped one) was not set.
  3902. - Made TImageFileFormat.MakeCompatible public non-virtual method
  3903. (and modified its function). Created new virtual
  3904. ConvertToSupported which should be overridden by descendants.
  3905. Main reason for doing this is to avoid duplicate code that was in all
  3906. TImageFileFormat's descendants.
  3907. - Changed TImageFileFormat.GetFormatInfo's result type to TImageFormatInfo.
  3908. - Split overloaded FindImageFileFormat functions to
  3909. FindImageFileFormatByClass and FindImageFileFormatByExt and created new
  3910. FindImageFileFormatByName which operates on whole filenames.
  3911. - Function GetExtensionFilterIndex renamed to GetFileNameFilterIndex
  3912. (because it now works with filenames not extensions).
  3913. - DetermineFileFormat now first searches by filename and if not found
  3914. then by data.
  3915. - Added TestFileName method to TImageFileFormat.
  3916. - Updated GetImageFileFormatsFilter to uses Masks instead of Extensions
  3917. property of TImageFileFormat. Also you can now request
  3918. OpenDialog and SaveDialog type filters
  3919. - Added Masks property and AddMasks method to TImageFileFormat.
  3920. AddMasks replaces AddExtensions, it uses filename masks instead
  3921. of some filename extensions to identify supported files.
  3922. - Changed TImageFileFormat.LoadData procedure to function and
  3923. moved various duplicate code from its descendants (check index,...)
  3924. here to TImageFileFormat helper methods.
  3925. - Changed TImageFileFormat.SaveData procedure to function and
  3926. moved various duplicate code from its descendants (check index,...)
  3927. here to TImageFileFormat helper methods.
  3928. - Removed RAISE_EXCEPTIONS define, exceptions are now raised every time
  3929. - Added MustBeFreed parameter to TImageFileFormat.MakeCompatible method
  3930. that indicates that compatible image returned by this method must be
  3931. freed after its usage.
  3932. -- 0.19 Changes/Bug Fixes -----------------------------------
  3933. - fixed bug in NewImage: if given format was ifDefault it wasn't
  3934. replaced with DefaultImageFormat constant which caused problems later
  3935. in other units
  3936. - fixed bug in RotateImage which caused that rotated special format
  3937. images were whole black
  3938. - LoadImageFromXXX and LoadMultiImageFromXXX now use DetermineXXXFormat
  3939. when choosing proper loader, this eliminated need for Ext parameter
  3940. in stream and memory loading functions
  3941. - added GetVersionStr function
  3942. - fixed bug in ResizeImage which caused indexed images to lose their
  3943. palette during process resulting in whole black image
  3944. - Clipping in ...Rect functions now uses clipping procs from ImagingUtility,
  3945. it also works better
  3946. - FillRect optimization for 8, 16, and 32 bit formats
  3947. - added pixel set/get functions to low level interface:
  3948. GetPixelDirect, SetPixelDirect, GetPixel32, SetPixel32,
  3949. GetPixelFP, SetPixelFP
  3950. - removed GetPixelBytes low level intf function - redundant
  3951. (same data can be obtained by GetImageFormatInfo)
  3952. - made small changes in many parts of library to compile
  3953. on AMD64 CPU (Linux with FPC)
  3954. - changed InitImage to procedure (function was pointless)
  3955. - Method TestFormat of TImageFileFormat class made public
  3956. (was protected)
  3957. - added function IsFileFormatSupported to low level interface
  3958. (contributed by Paul Michell)
  3959. - fixed some missing format arguments from error strings
  3960. which caused Format function to raise exception
  3961. - removed forgotten debug code that disabled filtered resizing of images with
  3962. channel bitcounts > 8
  3963. -- 0.17 Changes/Bug Fixes -----------------------------------
  3964. - changed order of parameters of CopyRect function
  3965. - GenerateMipMaps now filters mipmap levels
  3966. - ResizeImage functions was extended to allow bilinear and bicubic filtering
  3967. - added StretchRect function to low level interface
  3968. - added functions GetImageFileFormatsFilter, GetFilterIndexExtension,
  3969. and GetExtensionFilterIndex
  3970. -- 0.15 Changes/Bug Fixes -----------------------------------
  3971. - added function RotateImage to low level interface
  3972. - moved TImageFormatInfo record and types required by it to
  3973. ImagingTypes unit, changed GetImageFormatInfo low level
  3974. interface function to return TImageFormatInfo instead of short info
  3975. - added checking of options values validity before they are used
  3976. - fixed possible memory leak in CloneImage
  3977. - added ReplaceColor function to low level interface
  3978. - new function FindImageFileFormat by class added
  3979. -- 0.13 Changes/Bug Fixes -----------------------------------
  3980. - added DetermineFileFormat, DetermineStreamFormat, DetermineMemoryFormat,
  3981. GetPixelsSize functions to low level interface
  3982. - added NewPalette, CopyPalette, FreePalette functions
  3983. to low level interface
  3984. - added MapImageToPalette, FillRect, SplitImage, MakePaletteForImages
  3985. functions to low level interface
  3986. - fixed buggy FillCustomPalette function (possible div by zero and others)
  3987. - added CopyRect function to low level interface
  3988. - Member functions of TImageFormatInfo record implemented for all formats
  3989. - before saving images TestImagesInArray is called now
  3990. - added TestImagesInArray function to low level interface
  3991. - added GenerateMipMaps function to low level interface
  3992. - stream position in load/save from/to stream is now set to position before
  3993. function was called if error occurs
  3994. - when error occurred during load/save from/to file file handle
  3995. was not released
  3996. - CloneImage returned always False
  3997. }
  3998. end.