Imaging.pas 147 KB

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