| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435 |
- {
- Vampyre Imaging Library
- by Marek Mauder
- https://github.com/galfar/imaginglib
- https://imaginglib.sourceforge.io
- - - - - -
- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at https://mozilla.org/MPL/2.0.
- }
- { This unit manages information about all image data formats and contains
- low level format conversion, manipulation, and other related functions.}
- unit ImagingFormats;
- {$I ImagingOptions.inc}
- interface
- uses
- ImagingTypes, Imaging, ImagingUtility;
- type
- TImageFormatInfoArray = array[TImageFormat] of PImageFormatInfo;
- PImageFormatInfoArray = ^TImageFormatInfoArray;
- { Additional image manipulation functions (usually used internally by Imaging unit) }
- type
- { Color reduction operations.}
- TReduceColorsAction = (raCreateHistogram, raUpdateHistogram, raMakeColorMap,
- raMapImage);
- TReduceColorsActions = set of TReduceColorsAction;
- const
- AllReduceColorsActions = [raCreateHistogram, raUpdateHistogram,
- raMakeColorMap, raMapImage];
- { Reduces the number of colors of source. Src is bits of source image
- (ARGB or floating point) and Dst is in some indexed format. MaxColors
- is the number of colors to which reduce and DstPal is palette to which
- the resulting colors are written and it must be allocated to at least
- MaxColors entries. ChannelMask is 'anded' with every pixel's channel value
- when creating color histogram. If $FF is used all 8bits of color channels
- are used which can be slow for large images with many colors so you can
- use lower masks to speed it up.}
- procedure ReduceColorsMedianCut(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo; MaxColors: LongInt; ChannelMask: Byte;
- DstPal: PPalette32; Actions: TReduceColorsActions = AllReduceColorsActions);
- { Stretches rectangle in source image to rectangle in destination image
- using nearest neighbor filtering. It is fast but results look blocky
- because there is no interpolation used. SrcImage and DstImage must be
- in the same data format. Works for all data formats except special formats.}
- procedure StretchNearest(const SrcImage: TImageData; SrcX, SrcY, SrcWidth,
- SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth,
- DstHeight: LongInt);
- type
- { Built-in sampling filters.}
- TSamplingFilter = (sfNearest, sfLinear, sfCosine, sfHermite, sfQuadratic,
- sfGaussian, sfSpline, sfLanczos, sfMitchell, sfCatmullRom);
- { Type of custom sampling function}
- TFilterFunction = function(Value: Single): Single;
- const
- { Default resampling filter used for bicubic resizing.}
- DefaultCubicFilter = sfCatmullRom;
- var
- { Built-in filter functions.}
- SamplingFilterFunctions: array[TSamplingFilter] of TFilterFunction;
- { Default radii of built-in filter functions.}
- SamplingFilterRadii: array[TSamplingFilter] of Single;
- { Stretches rectangle in source image to rectangle in destination image
- with resampling. One of built-in resampling filters defined by
- Filter is used. Set WrapEdges to True for seamlessly tileable images.
- SrcImage and DstImage must be in the same data format.
- Works for all data formats except special and indexed formats.}
- procedure StretchResample(const SrcImage: TImageData; SrcX, SrcY, SrcWidth,
- SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth,
- DstHeight: LongInt; Filter: TSamplingFilter; WrapEdges: Boolean = False); overload;
- { Stretches rectangle in source image to rectangle in destination image
- with resampling. You can use custom sampling function and filter radius.
- Set WrapEdges to True for seamlessly tileable images. SrcImage and DstImage
- must be in the same data format.
- Works for all data formats except special and indexed formats.}
- procedure StretchResample(const SrcImage: TImageData; SrcX, SrcY, SrcWidth,
- SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth,
- DstHeight: LongInt; Filter: TFilterFunction; Radius: Single;
- WrapEdges: Boolean = False); overload;
- { Helper for functions that create mipmap levels. BiggerLevel is
- valid image and SmallerLevel is empty zeroed image. SmallerLevel is created
- with Width and Height dimensions and it is filled with pixels of BiggerLevel
- using resampling filter specified by ImagingMipMapFilter option.
- Uses StretchNearest and StretchResample internally so the same image data format
- limitations apply.}
- procedure FillMipMapLevel(const BiggerLevel: TImageData; Width, Height: LongInt;
- var SmallerLevel: TImageData);
- { Various helper & support functions }
- { Copies Src pixel to Dest pixel. It is faster than System.Move procedure.}
- procedure CopyPixel(Src, Dest: Pointer; BytesPerPixel: LongInt); {$IFDEF USE_INLINE}inline;{$ENDIF}
- { Compares Src pixel and Dest pixel. It is faster than SysUtils.CompareMem function.}
- function ComparePixels(PixelA, PixelB: Pointer; BytesPerPixel: LongInt): Boolean; {$IFDEF USE_INLINE}inline;{$ENDIF}
- { Translates pixel color in SrcFormat to DstFormat.}
- procedure TranslatePixel(SrcPixel, DstPixel: Pointer; SrcFormat,
- DstFormat: TImageFormat; SrcPalette, DstPalette: PPalette32);
- { Clamps floating point pixel channel values to [0.0, 1.0] range.}
- procedure ClampFloatPixel(var PixF: TColorFPRec); {$IFDEF USE_INLINE}inline;{$ENDIF}
- { Helper function that converts pixel in any format to 32bit ARGB pixel.
- For common formats it's faster than calling GetPixel32 etc.}
- procedure ConvertToPixel32(SrcPix: PByte; DestPix: PColor32Rec;
- const SrcInfo: TImageFormatInfo; SrcPalette: PPalette32 = nil); {$IFDEF USE_INLINE}inline;{$ENDIF}
- { Adds padding bytes at the ends of scanlines. Bpp is the number of bytes per
- pixel of source and WidthBytes is the number of bytes per scanlines of dest.}
- procedure AddPadBytes(DataIn: Pointer; DataOut: Pointer; Width, Height,
- Bpp, WidthBytes: LongInt);
- { Removes padding from image with scanlines that have aligned sizes. Bpp is
- the number of bytes per pixel of dest and WidthBytes is the number of bytes
- per scanlines of source.}
- procedure RemovePadBytes(DataIn: Pointer; DataOut: Pointer; Width, Height,
- Bpp, WidthBytes: LongInt);
- { Converts 1bit image data to 8bit. Used mostly by file loaders for formats
- supporting 1bit images. Scaling of pixel values to 8bits is optional
- (indexed formats don't need this).}
- procedure Convert1To8(DataIn, DataOut: PByte; Width, Height,
- WidthBytes: LongInt; ScaleTo8Bits: Boolean);
- { Converts 2bit image data to 8bit. Used mostly by file loaders for formats
- supporting 2bit images. Scaling of pixel values to 8bits is optional
- (indexed formats don't need this).}
- procedure Convert2To8(DataIn, DataOut: PByte; Width, Height,
- WidthBytes: LongInt; ScaleTo8Bits: Boolean);
- { Converts 4bit image data to 8bit. Used mostly by file loaders for formats
- supporting 4bit images. Scaling of pixel values to 8bits is optional
- (indexed formats don't need this).}
- procedure Convert4To8(DataIn, DataOut: PByte; Width, Height,
- WidthBytes: LongInt; ScaleTo8Bits: Boolean);
- { Helper function for image file loaders. Some 15 bit images (targas, bitmaps)
- may contain 1 bit alpha but there is no indication of it. This function checks
- all 16 bit(should be X1R5G5B5 or A1R5G5B5 format) pixels and some of them have
- alpha bit set it returns True, otherwise False.}
- function Has16BitImageAlpha(NumPixels: LongInt; Data: PWord): Boolean;
- { Helper function for image file loaders. This function checks is similar
- to Has16BitImageAlpha but works with A8R8G8B8/X8R8G8B8 format.}
- function Has32BitImageAlpha(NumPixels: LongInt; Data: PUInt32): Boolean;
- { Checks if there is any relevant alpha data (any entry has alpha <> 255)
- in the given palette.}
- function PaletteHasAlpha(Palette: PPalette32; PaletteEntries: Integer): Boolean;
- { Checks if given palette has only grayscale entries.}
- function PaletteIsGrayScale(Palette: PPalette32; PaletteEntries: Integer): Boolean;
- { Provides indexed access to each line of pixels. Does not work with special
- format images.}
- function GetScanLine(ImageBits: Pointer; const FormatInfo: TImageFormatInfo;
- LineWidth, Index: LongInt): Pointer; {$IFDEF USE_INLINE}inline;{$ENDIF}
- { Returns True if Format is valid image data format identifier.}
- function IsImageFormatValid(Format: TImageFormat): Boolean;
- { Converts 16bit half floating point value to 32bit Single.}
- function HalfToFloat(Half: THalfFloat): Single;
- { Converts 32bit Single to 16bit half floating point.}
- function FloatToHalf(Float: Single): THalfFloat;
- { Converts half float color value to single-precision floating point color.}
- function ColorHalfToFloat(ColorHF: TColorHFRec): TColorFPRec; {$IFDEF USE_INLINE}inline;{$ENDIF}
- { Converts single-precision floating point color to half float color.}
- function ColorFloatToHalf(ColorFP: TColorFPRec): TColorHFRec; {$IFDEF USE_INLINE}inline;{$ENDIF}
- { Converts ARGB color to grayscale. }
- function Color32ToGray(Color32: TColor32): Byte; {$IFDEF USE_INLINE}inline;{$ENDIF}
- { Makes image PalEntries x 1 big where each pixel has color of one pal entry.}
- procedure VisualizePalette(Pal: PPalette32; Entries: Integer; out PalImage: TImageData);
- type
- TPointRec = record
- Pos: Integer;
- Weight: Single;
- end;
- TCluster = array of TPointRec;
- TMappingTable = array of TCluster;
- { Helper function for resampling.}
- function BuildMappingTable(DstLow, DstHigh, SrcLow, SrcHigh, SrcImageWidth: LongInt;
- Filter: TFilterFunction; Radius: Single; WrapEdges: Boolean): TMappingTable;
- { Helper function for resampling.}
- procedure FindExtremes(const Map: TMappingTable; var MinPos, MaxPos: LongInt);
- { Pixel readers/writers for different image formats }
- { Returns pixel of image in any ARGB format. Channel values are scaled to 16 bits.}
- procedure ChannelGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo;
- var Pix: TColor64Rec);
- { Sets pixel of image in any ARGB format. Channel values must be scaled to 16 bits.}
- procedure ChannelSetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo;
- const Pix: TColor64Rec);
- { Returns pixel of image in any grayscale format. Gray value is scaled to 64 bits
- and alpha to 16 bits.}
- procedure GrayGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo;
- var Gray: TColor64Rec; var Alpha: Word);
- { Sets pixel of image in any grayscale format. Gray value must be scaled to 64 bits
- and alpha to 16 bits.}
- procedure GraySetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo;
- const Gray: TColor64Rec; Alpha: Word);
- { Returns pixel of image in any floating point format. Channel values are
- in range <0.0, 1.0>.}
- procedure FloatGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo;
- var Pix: TColorFPRec);
- { Sets pixel of image in any floating point format. Channel values must be
- in range <0.0, 1.0>.}
- procedure FloatSetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo;
- const Pix: TColorFPRec);
- { Returns pixel of image in any indexed format. Returned value is index to
- the palette.}
- procedure IndexGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo;
- var Index: UInt32);
- { Sets pixel of image in any indexed format. Index is index to the palette.}
- procedure IndexSetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo;
- Index: UInt32);
- { Pixel readers/writers for 32bit and FP colors}
- { Function for getting pixel colors. Native pixel is read from Image and
- then translated to 32 bit ARGB.}
- function GetPixel32Generic(Bits: Pointer; Info: PImageFormatInfo;
- Palette: PPalette32): TColor32Rec;
- { Procedure for setting pixel colors. Input 32 bit ARGB color is translated to
- native format and then written to Image.}
- procedure SetPixel32Generic(Bits: Pointer; Info: PImageFormatInfo;
- Palette: PPalette32; const Color: TColor32Rec);
- { Function for getting pixel colors. Native pixel is read from Image and
- then translated to FP ARGB.}
- function GetPixelFPGeneric(Bits: Pointer; Info: PImageFormatInfo;
- Palette: PPalette32): TColorFPRec;
- { Procedure for setting pixel colors. Input FP ARGB color is translated to
- native format and then written to Image.}
- procedure SetPixelFPGeneric(Bits: Pointer; Info: PImageFormatInfo;
- Palette: PPalette32; const Color: TColorFPRec);
- { Image format conversion functions }
- { Converts any ARGB format to any ARGB format.}
- procedure ChannelToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo);
- { Converts any ARGB format to any grayscale format.}
- procedure ChannelToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo);
- { Converts any ARGB format to any floating point format.}
- procedure ChannelToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo);
- { Converts any ARGB format to any indexed format.}
- procedure ChannelToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo; DstPal: PPalette32);
- { Converts any grayscale format to any grayscale format.}
- procedure GrayToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo);
- { Converts any grayscale format to any ARGB format.}
- procedure GrayToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo);
- { Converts any grayscale format to any floating point format.}
- procedure GrayToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo);
- { Converts any grayscale format to any indexed format.}
- procedure GrayToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo; DstPal: PPalette32);
- { Converts any floating point format to any floating point format.}
- procedure FloatToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo);
- { Converts any floating point format to any ARGB format.}
- procedure FloatToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo);
- { Converts any floating point format to any grayscale format.}
- procedure FloatToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo);
- { Converts any floating point format to any indexed format.}
- procedure FloatToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo; DstPal: PPalette32);
- { Converts any indexed format to any indexed format.}
- procedure IndexToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo; SrcPal, DstPal: PPalette32);
- { Converts any indexed format to any ARGB format.}
- procedure IndexToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo; SrcPal: PPalette32);
- { Converts any indexed format to any grayscale format.}
- procedure IndexToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo; SrcPal: PPalette32);
- { Converts any indexed format to any floating point format.}
- procedure IndexToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo; SrcPal: PPalette32);
- { Special formats conversion functions }
- { Converts image to/from/between special image formats (dxtc, ...).}
- procedure ConvertSpecial(var Image: TImageData; SrcInfo,
- DstInfo: PImageFormatInfo);
- { Inits all image format information. Called internally on startup.}
- procedure InitImageFormats(var Infos: TImageFormatInfoArray);
- const
- // Grayscale conversion channel weights
- GrayConv: TColorFPRec = (B: 0.114; G: 0.587; R: 0.299; A: 0.0);
- // Constants for converting integer colors to floating point
- OneDiv8Bit: Single = 1.0 / 255.0;
- OneDiv16Bit: Single = 1.0 / 65535.0;
- implementation
- { TImageFormatInfo member functions }
- { Returns size in bytes of image in given standard format where
- Size = Width * Height * Bpp.}
- function GetStdPixelsSize(Format: TImageFormat; Width, Height: Integer): Int64; forward;
- { Checks if Width and Height are valid for given standard format.}
- procedure CheckStdDimensions(Format: TImageFormat; var Width, Height: Integer); forward;
- { Returns size in bytes of image in given DXT format.}
- function GetDXTPixelsSize(Format: TImageFormat; Width, Height: Integer): Int64; forward;
- { Checks if Width and Height are valid for given DXT format. If they are
- not valid, they are changed to pass the check.}
- procedure CheckDXTDimensions(Format: TImageFormat; var Width, Height: Integer); forward;
- { Returns size in bytes of image in BTC format.}
- function GetBTCPixelsSize(Format: TImageFormat; Width, Height: Integer): Int64; forward;
- { Returns size in bytes of image in binary format (1bit image).}
- function GetBinaryPixelsSize(Format: TImageFormat; Width, Height: Integer): Int64; forward;
- function GetBCPixelsSize(Format: TImageFormat; Width, Height: Integer): Int64; forward;
- procedure CheckBCDimensions(Format: TImageFormat; var Width, Height: Integer); forward;
- { Optimized pixel readers/writers for 32bit and FP colors to be stored in TImageFormatInfo }
- function GetPixel32ifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColor32Rec; forward;
- procedure SetPixel32ifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColor32Rec); forward;
- function GetPixelFPifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec; forward;
- procedure SetPixelFPifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec); forward;
- function GetPixel32Channel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColor32Rec; forward;
- procedure SetPixel32Channel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColor32Rec); forward;
- function GetPixelFPChannel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec; forward;
- procedure SetPixelFPChannel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec); forward;
- function GetPixelFPFloat32(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec; forward;
- procedure SetPixelFPFloat32(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec); forward;
- var
- PFR3G3B2: TPixelFormatInfo;
- PFX5R1G1B1: TPixelFormatInfo;
- PFR5G6B5: TPixelFormatInfo;
- PFA1R5G5B5: TPixelFormatInfo;
- PFA4R4G4B4: TPixelFormatInfo;
- PFX1R5G5B5: TPixelFormatInfo;
- PFX4R4G4B4: TPixelFormatInfo;
- FInfos: PImageFormatInfoArray;
- var
- // Free Pascal generates hundreds of warnings here
- {$WARNINGS OFF}
- // indexed formats
- Index8Info: TImageFormatInfo = (
- Format: ifIndex8;
- Name: 'Index8';
- BytesPerPixel: 1;
- ChannelCount: 1;
- PaletteEntries: 256;
- HasAlphaChannel: True;
- IsIndexed: True;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPGeneric;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPGeneric);
- // grayscale formats
- Gray8Info: TImageFormatInfo = (
- Format: ifGray8;
- Name: 'Gray8';
- BytesPerPixel: 1;
- ChannelCount: 1;
- HasGrayChannel: True;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Channel8Bit;
- GetPixelFP: GetPixelFPChannel8Bit;
- SetPixel32: SetPixel32Channel8Bit;
- SetPixelFP: SetPixelFPChannel8Bit);
- A8Gray8Info: TImageFormatInfo = (
- Format: ifA8Gray8;
- Name: 'A8Gray8';
- BytesPerPixel: 2;
- ChannelCount: 2;
- HasGrayChannel: True;
- HasAlphaChannel: True;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Channel8Bit;
- GetPixelFP: GetPixelFPChannel8Bit;
- SetPixel32: SetPixel32Channel8Bit;
- SetPixelFP: SetPixelFPChannel8Bit);
- Gray16Info: TImageFormatInfo = (
- Format: ifGray16;
- Name: 'Gray16';
- BytesPerPixel: 2;
- ChannelCount: 1;
- HasGrayChannel: True;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPGeneric;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPGeneric);
- Gray32Info: TImageFormatInfo = (
- Format: ifGray32;
- Name: 'Gray32';
- BytesPerPixel: 4;
- ChannelCount: 1;
- HasGrayChannel: True;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPGeneric;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPGeneric);
- Gray64Info: TImageFormatInfo = (
- Format: ifGray64;
- Name: 'Gray64';
- BytesPerPixel: 8;
- ChannelCount: 1;
- HasGrayChannel: True;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPGeneric;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPGeneric);
- A16Gray16Info: TImageFormatInfo = (
- Format: ifA16Gray16;
- Name: 'A16Gray16';
- BytesPerPixel: 4;
- ChannelCount: 2;
- HasGrayChannel: True;
- HasAlphaChannel: True;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPGeneric;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPGeneric);
- // ARGB formats
- X5R1G1B1Info: TImageFormatInfo = (
- Format: ifX5R1G1B1;
- Name: 'X5R1G1B1';
- BytesPerPixel: 1;
- ChannelCount: 3;
- UsePixelFormat: True;
- PixelFormat: @PFX5R1G1B1;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPGeneric;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPGeneric);
- R3G3B2Info: TImageFormatInfo = (
- Format: ifR3G3B2;
- Name: 'R3G3B2';
- BytesPerPixel: 1;
- ChannelCount: 3;
- UsePixelFormat: True;
- PixelFormat: @PFR3G3B2;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPGeneric;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPGeneric);
- R5G6B5Info: TImageFormatInfo = (
- Format: ifR5G6B5;
- Name: 'R5G6B5';
- BytesPerPixel: 2;
- ChannelCount: 3;
- UsePixelFormat: True;
- PixelFormat: @PFR5G6B5;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPGeneric;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPGeneric);
- A1R5G5B5Info: TImageFormatInfo = (
- Format: ifA1R5G5B5;
- Name: 'A1R5G5B5';
- BytesPerPixel: 2;
- ChannelCount: 4;
- HasAlphaChannel: True;
- UsePixelFormat: True;
- PixelFormat: @PFA1R5G5B5;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPGeneric;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPGeneric);
- A4R4G4B4Info: TImageFormatInfo = (
- Format: ifA4R4G4B4;
- Name: 'A4R4G4B4';
- BytesPerPixel: 2;
- ChannelCount: 4;
- HasAlphaChannel: True;
- UsePixelFormat: True;
- PixelFormat: @PFA4R4G4B4;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPGeneric;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPGeneric);
- X1R5G5B5Info: TImageFormatInfo = (
- Format: ifX1R5G5B5;
- Name: 'X1R5G5B5';
- BytesPerPixel: 2;
- ChannelCount: 3;
- UsePixelFormat: True;
- PixelFormat: @PFX1R5G5B5;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPGeneric;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPGeneric);
- X4R4G4B4Info: TImageFormatInfo = (
- Format: ifX4R4G4B4;
- Name: 'X4R4G4B4';
- BytesPerPixel: 2;
- ChannelCount: 3;
- UsePixelFormat: True;
- PixelFormat: @PFX4R4G4B4;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPGeneric;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPGeneric);
- R8G8B8Info: TImageFormatInfo = (
- Format: ifR8G8B8;
- Name: 'R8G8B8';
- BytesPerPixel: 3;
- ChannelCount: 3;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Channel8Bit;
- GetPixelFP: GetPixelFPChannel8Bit;
- SetPixel32: SetPixel32Channel8Bit;
- SetPixelFP: SetPixelFPChannel8Bit);
- A8R8G8B8Info: TImageFormatInfo = (
- Format: ifA8R8G8B8;
- Name: 'A8R8G8B8';
- BytesPerPixel: 4;
- ChannelCount: 4;
- HasAlphaChannel: True;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32ifA8R8G8B8;
- GetPixelFP: GetPixelFPifA8R8G8B8;
- SetPixel32: SetPixel32ifA8R8G8B8;
- SetPixelFP: SetPixelFPifA8R8G8B8);
- X8R8G8B8Info: TImageFormatInfo = (
- Format: ifX8R8G8B8;
- Name: 'X8R8G8B8';
- BytesPerPixel: 4;
- ChannelCount: 3;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Channel8Bit;
- GetPixelFP: GetPixelFPChannel8Bit;
- SetPixel32: SetPixel32Channel8Bit;
- SetPixelFP: SetPixelFPChannel8Bit);
- R16G16B16Info: TImageFormatInfo = (
- Format: ifR16G16B16;
- Name: 'R16G16B16';
- BytesPerPixel: 6;
- ChannelCount: 3;
- RBSwapFormat: ifB16G16R16;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPGeneric;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPGeneric);
- A16R16G16B16Info: TImageFormatInfo = (
- Format: ifA16R16G16B16;
- Name: 'A16R16G16B16';
- BytesPerPixel: 8;
- ChannelCount: 4;
- HasAlphaChannel: True;
- RBSwapFormat: ifA16B16G16R16;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPGeneric;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPGeneric);
- B16G16R16Info: TImageFormatInfo = (
- Format: ifB16G16R16;
- Name: 'B16G16R16';
- BytesPerPixel: 6;
- ChannelCount: 3;
- IsRBSwapped: True;
- RBSwapFormat: ifR16G16B16;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPGeneric;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPGeneric);
- A16B16G16R16Info: TImageFormatInfo = (
- Format: ifA16B16G16R16;
- Name: 'A16B16G16R16';
- BytesPerPixel: 8;
- ChannelCount: 4;
- HasAlphaChannel: True;
- IsRBSwapped: True;
- RBSwapFormat: ifA16R16G16B16;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPGeneric;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPGeneric);
- // floating point formats
- R32FInfo: TImageFormatInfo = (
- Format: ifR32F;
- Name: 'R32F';
- BytesPerPixel: 4;
- ChannelCount: 1;
- IsFloatingPoint: True;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPFloat32;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPFloat32);
- A32R32G32B32FInfo: TImageFormatInfo = (
- Format: ifA32R32G32B32F;
- Name: 'A32R32G32B32F';
- BytesPerPixel: 16;
- ChannelCount: 4;
- HasAlphaChannel: True;
- IsFloatingPoint: True;
- RBSwapFormat: ifA32B32G32R32F;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPFloat32;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPFloat32);
- A32B32G32R32FInfo: TImageFormatInfo = (
- Format: ifA32B32G32R32F;
- Name: 'A32B32G32R32F';
- BytesPerPixel: 16;
- ChannelCount: 4;
- HasAlphaChannel: True;
- IsFloatingPoint: True;
- IsRBSwapped: True;
- RBSwapFormat: ifA32R32G32B32F;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPFloat32;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPFloat32);
- R16FInfo: TImageFormatInfo = (
- Format: ifR16F;
- Name: 'R16F';
- BytesPerPixel: 2;
- ChannelCount: 1;
- IsFloatingPoint: True;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPGeneric;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPGeneric);
- A16R16G16B16FInfo: TImageFormatInfo = (
- Format: ifA16R16G16B16F;
- Name: 'A16R16G16B16F';
- BytesPerPixel: 8;
- ChannelCount: 4;
- HasAlphaChannel: True;
- IsFloatingPoint: True;
- RBSwapFormat: ifA16B16G16R16F;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPGeneric;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPGeneric);
- A16B16G16R16FInfo: TImageFormatInfo = (
- Format: ifA16B16G16R16F;
- Name: 'A16B16G16R16F';
- BytesPerPixel: 8;
- ChannelCount: 4;
- HasAlphaChannel: True;
- IsFloatingPoint: True;
- IsRBSwapped: True;
- RBSwapFormat: ifA16R16G16B16F;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPGeneric;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPGeneric);
- R32G32B32FInfo: TImageFormatInfo = (
- Format: ifR32G32B32F;
- Name: 'R32G32B32F';
- BytesPerPixel: 12;
- ChannelCount: 3;
- IsFloatingPoint: True;
- RBSwapFormat: ifB32G32R32F;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPFloat32;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPFloat32);
- B32G32R32FInfo: TImageFormatInfo = (
- Format: ifB32G32R32F;
- Name: 'B32G32R32F';
- BytesPerPixel: 12;
- ChannelCount: 3;
- IsFloatingPoint: True;
- IsRBSwapped: True;
- RBSwapFormat: ifR32G32B32F;
- GetPixelsSize: GetStdPixelsSize;
- CheckDimensions: CheckStdDimensions;
- GetPixel32: GetPixel32Generic;
- GetPixelFP: GetPixelFPFloat32;
- SetPixel32: SetPixel32Generic;
- SetPixelFP: SetPixelFPFloat32);
- // special formats
- DXT1Info: TImageFormatInfo = (
- Format: ifDXT1;
- Name: 'DXT1';
- ChannelCount: 4;
- HasAlphaChannel: True;
- IsSpecial: True;
- GetPixelsSize: GetDXTPixelsSize;
- CheckDimensions: CheckDXTDimensions;
- SpecialNearestFormat: ifA8R8G8B8);
- DXT3Info: TImageFormatInfo = (
- Format: ifDXT3;
- Name: 'DXT3';
- ChannelCount: 4;
- HasAlphaChannel: True;
- IsSpecial: True;
- GetPixelsSize: GetDXTPixelsSize;
- CheckDimensions: CheckDXTDimensions;
- SpecialNearestFormat: ifA8R8G8B8);
- DXT5Info: TImageFormatInfo = (
- Format: ifDXT5;
- Name: 'DXT5';
- ChannelCount: 4;
- HasAlphaChannel: True;
- IsSpecial: True;
- GetPixelsSize: GetDXTPixelsSize;
- CheckDimensions: CheckDXTDimensions;
- SpecialNearestFormat: ifA8R8G8B8);
- BTCInfo: TImageFormatInfo = (
- Format: ifBTC;
- Name: 'BTC';
- ChannelCount: 1;
- HasAlphaChannel: False;
- IsSpecial: True;
- GetPixelsSize: GetBTCPixelsSize;
- CheckDimensions: CheckDXTDimensions;
- SpecialNearestFormat: ifGray8);
- ATI1NInfo: TImageFormatInfo = (
- Format: ifATI1N;
- Name: 'ATI1N';
- ChannelCount: 1;
- HasAlphaChannel: False;
- IsSpecial: True;
- GetPixelsSize: GetDXTPixelsSize;
- CheckDimensions: CheckDXTDimensions;
- SpecialNearestFormat: ifGray8);
- ATI2NInfo: TImageFormatInfo = (
- Format: ifATI2N;
- Name: 'ATI2N';
- ChannelCount: 2;
- HasAlphaChannel: False;
- IsSpecial: True;
- GetPixelsSize: GetDXTPixelsSize;
- CheckDimensions: CheckDXTDimensions;
- SpecialNearestFormat: ifA8R8G8B8);
- BinaryInfo: TImageFormatInfo = (
- Format: ifBinary;
- Name: 'Binary';
- ChannelCount: 1;
- HasAlphaChannel: False;
- IsSpecial: True;
- GetPixelsSize: GetBinaryPixelsSize;
- CheckDimensions: CheckStdDimensions;
- SpecialNearestFormat: ifGray8);
- { Passtrough formats }
- {ETC1Info: TImageFormatInfo = (
- Format: ifETC1;
- Name: 'ETC1';
- ChannelCount: 3;
- HasAlphaChannel: False;
- IsSpecial: True;
- IsPassthrough: True;
- GetPixelsSize: GetBCPixelsSize;
- CheckDimensions: CheckBCDimensions;
- SpecialNearestFormat: ifR8G8B8);
- ETC2RGBInfo: TImageFormatInfo = (
- Format: ifETC2RGB;
- Name: 'ETC2RGB';
- ChannelCount: 3;
- HasAlphaChannel: False;
- IsSpecial: True;
- IsPassthrough: True;
- GetPixelsSize: GetBCPixelsSize;
- CheckDimensions: CheckBCDimensions;
- SpecialNearestFormat: ifR8G8B8);
- ETC2RGBAInfo: TImageFormatInfo = (
- Format: ifETC2RGBA;
- Name: 'ETC2RGBA';
- ChannelCount: 4;
- HasAlphaChannel: True;
- IsSpecial: True;
- IsPassthrough: True;
- GetPixelsSize: GetBCPixelsSize;
- CheckDimensions: CheckBCDimensions;
- SpecialNearestFormat: ifA8R8G8B8);
- ETC2PAInfo: TImageFormatInfo = (
- Format: ifETC2PA;
- Name: 'ETC2PA';
- ChannelCount: 4;
- HasAlphaChannel: True;
- IsSpecial: True;
- IsPassthrough: True;
- GetPixelsSize: GetBCPixelsSize;
- CheckDimensions: CheckBCDimensions;
- SpecialNearestFormat: ifA8R8G8B8);
- DXBC6Info: TImageFormatInfo = (
- Format: ifDXBC6;
- Name: 'DXBC6';
- ChannelCount: 4;
- HasAlphaChannel: True;
- IsSpecial: True;
- IsPassthrough: True;
- GetPixelsSize: GetBCPixelsSize;
- CheckDimensions: CheckBCDimensions;
- SpecialNearestFormat: ifA8R8G8B8);
- DXBC7Info: TImageFormatInfo = (
- Format: ifDXBC6;
- Name: 'DXBC7';
- ChannelCount: 4;
- HasAlphaChannel: True;
- IsSpecial: True;
- IsPassthrough: True;
- GetPixelsSize: GetBCPixelsSize;
- CheckDimensions: CheckBCDimensions;
- SpecialNearestFormat: ifA8R8G8B8);
- PVRTCInfo: TImageFormatInfo = (
- Format: ifPVRTC;
- Name: 'PVRTC';
- ChannelCount: 4;
- HasAlphaChannel: True;
- IsSpecial: True;
- IsPassthrough: True;
- GetPixelsSize: GetBCPixelsSize;
- CheckDimensions: CheckBCDimensions;
- SpecialNearestFormat: ifA8R8G8B8);}
- {$WARNINGS ON}
- function PixelFormat(ABitCount, RBitCount, GBitCount, BBitCount: Byte): TPixelFormatInfo; forward;
- procedure InitImageFormats(var Infos: TImageFormatInfoArray);
- begin
- FInfos := @Infos;
- Infos[ifDefault] := @A8R8G8B8Info;
- // indexed formats
- Infos[ifIndex8] := @Index8Info;
- // grayscale formats
- Infos[ifGray8] := @Gray8Info;
- Infos[ifA8Gray8] := @A8Gray8Info;
- Infos[ifGray16] := @Gray16Info;
- Infos[ifGray32] := @Gray32Info;
- Infos[ifGray64] := @Gray64Info;
- Infos[ifA16Gray16] := @A16Gray16Info;
- // ARGB formats
- Infos[ifX5R1G1B1] := @X5R1G1B1Info;
- Infos[ifR3G3B2] := @R3G3B2Info;
- Infos[ifR5G6B5] := @R5G6B5Info;
- Infos[ifA1R5G5B5] := @A1R5G5B5Info;
- Infos[ifA4R4G4B4] := @A4R4G4B4Info;
- Infos[ifX1R5G5B5] := @X1R5G5B5Info;
- Infos[ifX4R4G4B4] := @X4R4G4B4Info;
- Infos[ifR8G8B8] := @R8G8B8Info;
- Infos[ifA8R8G8B8] := @A8R8G8B8Info;
- Infos[ifX8R8G8B8] := @X8R8G8B8Info;
- Infos[ifR16G16B16] := @R16G16B16Info;
- Infos[ifA16R16G16B16] := @A16R16G16B16Info;
- Infos[ifB16G16R16] := @B16G16R16Info;
- Infos[ifA16B16G16R16] := @A16B16G16R16Info;
- // floating point formats
- Infos[ifR32F] := @R32FInfo;
- Infos[ifA32R32G32B32F] := @A32R32G32B32FInfo;
- Infos[ifA32B32G32R32F] := @A32B32G32R32FInfo;
- Infos[ifR16F] := @R16FInfo;
- Infos[ifA16R16G16B16F] := @A16R16G16B16FInfo;
- Infos[ifA16B16G16R16F] := @A16B16G16R16FInfo;
- Infos[ifR32G32B32F] := @R32G32B32FInfo;
- Infos[ifB32G32R32F] := @B32G32R32FInfo;
- // special formats
- Infos[ifDXT1] := @DXT1Info;
- Infos[ifDXT3] := @DXT3Info;
- Infos[ifDXT5] := @DXT5Info;
- Infos[ifBTC] := @BTCInfo;
- Infos[ifATI1N] := @ATI1NInfo;
- Infos[ifATI2N] := @ATI2NInfo;
- Infos[ifBinary] := @BinaryInfo;
- PFR3G3B2 := PixelFormat(0, 3, 3, 2);
- PFX5R1G1B1 := PixelFormat(0, 1, 1, 1);
- PFR5G6B5 := PixelFormat(0, 5, 6, 5);
- PFA1R5G5B5 := PixelFormat(1, 5, 5, 5);
- PFA4R4G4B4 := PixelFormat(4, 4, 4, 4);
- PFX1R5G5B5 := PixelFormat(0, 5, 5, 5);
- PFX4R4G4B4 := PixelFormat(0, 4, 4, 4);
- end;
- { Internal unit helper functions }
- function PixelFormat(ABitCount, RBitCount, GBitCount, BBitCount: Byte): TPixelFormatInfo;
- begin
- Result.ABitMask := ((1 shl ABitCount) - 1) shl (RBitCount + GBitCount +
- BBitCount);
- Result.RBitMask := ((1 shl RBitCount) - 1) shl (GBitCount + BBitCount);
- Result.GBitMask := ((1 shl GBitCount) - 1) shl (BBitCount);
- Result.BBitMask := (1 shl BBitCount) - 1;
- Result.ABitCount := ABitCount;
- Result.RBitCount := RBitCount;
- Result.GBitCount := GBitCount;
- Result.BBitCount := BBitCount;
- Result.AShift := RBitCount + GBitCount + BBitCount;
- Result.RShift := GBitCount + BBitCount;
- Result.GShift := BBitCount;
- Result.BShift := 0;
- Result.ARecDiv := Max(1, Pow2Int(Result.ABitCount) - 1);
- Result.RRecDiv := Max(1, Pow2Int(Result.RBitCount) - 1);
- Result.GRecDiv := Max(1, Pow2Int(Result.GBitCount) - 1);
- Result.BRecDiv := Max(1, Pow2Int(Result.BBitCount) - 1);
- end;
- function PixelFormatMask(ABitMask, RBitMask, GBitMask, BBitMask: UInt32): TPixelFormatInfo;
- function GetBitCount(B: UInt32): UInt32;
- var
- I: UInt32;
- begin
- I := 0;
- while (I < 31) and (((1 shl I) and B) = 0) do
- Inc(I);
- Result := 0;
- while ((1 shl I) and B) <> 0 do
- begin
- Inc(I);
- Inc(Result);
- end;
- end;
- begin
- Result := PixelFormat(GetBitCount(ABitMask), GetBitCount(RBitMask),
- GetBitCount(GBitMask), GetBitCount(BBitMask));
- end;
- function PFSetARGB(const PF: TPixelFormatInfo; A, R, G, B: Byte): TColor32;
- {$IFDEF USE_INLINE}inline;{$ENDIF}
- begin
- with PF do
- Result :=
- (A shl ABitCount shr 8 shl AShift) or
- (R shl RBitCount shr 8 shl RShift) or
- (G shl GBitCount shr 8 shl GShift) or
- (B shl BBitCount shr 8 shl BShift);
- end;
- procedure PFGetARGB(const PF: TPixelFormatInfo; Color: UInt32;
- var A, R, G, B: Byte); {$IFDEF USE_INLINE}inline;{$ENDIF}
- begin
- with PF do
- begin
- A := (Color and ABitMask shr AShift) * 255 div ARecDiv;
- R := (Color and RBitMask shr RShift) * 255 div RRecDiv;
- G := (Color and GBitMask shr GShift) * 255 div GRecDiv;
- B := (Color and BBitMask shl BShift) * 255 div BRecDiv;
- end;
- end;
- function PFSetColor(const PF: TPixelFormatInfo; ARGB: TColor32): UInt32;
- {$IFDEF USE_INLINE}inline;{$ENDIF}
- begin
- with PF do
- Result :=
- (Byte(ARGB shr 24) shl ABitCount shr 8 shl AShift) or
- (Byte(ARGB shr 16) shl RBitCount shr 8 shl RShift) or
- (Byte(ARGB shr 8) shl GBitCount shr 8 shl GShift) or
- (Byte(ARGB) shl BBitCount shr 8 shl BShift);
- end;
- function PFGetColor(const PF: TPixelFormatInfo; Color: UInt32): TColor32;
- {$IFDEF USE_INLINE}inline;{$ENDIF}
- begin
- with PF, TColor32Rec(Result) do
- begin
- A := (Color and ABitMask shr AShift) * 255 div ARecDiv;
- R := (Color and RBitMask shr RShift) * 255 div RRecDiv;
- G := (Color and GBitMask shr GShift) * 255 div GRecDiv;
- B := (Color and BBitMask shl BShift) * 255 div BRecDiv;
- end;
- end;
- { Additional image manipulation functions (usually used internally by Imaging unit) }
- const
- MaxPossibleColors = 4096;
- HashSize = 32768;
- AlphaWeight = 1024;
- RedWeight = 612;
- GreenWeight = 1202;
- BlueWeight = 234;
- type
- PColorBin = ^TColorBin;
- TColorBin = record
- Color: TColor32Rec;
- Number: Integer;
- Next: PColorBin;
- end;
- THashTable = array[0..HashSize - 1] of PColorBin;
- TColorBox = record
- AMin, AMax,
- RMin, RMax,
- GMin, GMax,
- BMin, BMax: Integer;
- Total: Integer;
- Represented: TColor32Rec;
- List: PColorBin;
- end;
- var
- Table: THashTable;
- Box: array[0..MaxPossibleColors - 1] of TColorBox;
- Boxes: Integer;
- procedure ReduceColorsMedianCut(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo; MaxColors: LongInt; ChannelMask: Byte;
- DstPal: PPalette32; Actions: TReduceColorsActions);
- procedure CreateHistogram (Src: PByte; SrcInfo: PImageFormatInfo;
- ChannelMask: Byte);
- var
- A, R, G, B: Byte;
- I, Addr: LongInt;
- PC: PColorBin;
- Col: TColor32Rec;
- begin
- for I := 0 to NumPixels - 1 do
- begin
- Col := GetPixel32Generic(Src, SrcInfo, nil);
- A := Col.A and ChannelMask;
- R := Col.R and ChannelMask;
- G := Col.G and ChannelMask;
- B := Col.B and ChannelMask;
- Addr := (A + 11 * B + 59 * R + 119 * G) mod HashSize;
- PC := Table[Addr];
- while (PC <> nil) and ((PC.Color.R <> R) or (PC.Color.G <> G) or
- (PC.Color.B <> B) or (PC.Color.A <> A)) do
- PC := PC.Next;
- if PC = nil then
- begin
- New(PC);
- PC.Color.R := R;
- PC.Color.G := G;
- PC.Color.B := B;
- PC.Color.A := A;
- PC.Number := 1;
- PC.Next := Table[Addr];
- Table[Addr] := PC;
- end
- else
- Inc(PC^.Number);
- Inc(Src, SrcInfo.BytesPerPixel);
- end;
- end;
- procedure InitBox (var Box : TColorBox);
- begin
- Box.AMin := 256;
- Box.RMin := 256;
- Box.GMin := 256;
- Box.BMin := 256;
- Box.AMax := -1;
- Box.RMax := -1;
- Box.GMax := -1;
- Box.BMax := -1;
- Box.Total := 0;
- Box.List := nil;
- end;
- procedure ChangeBox (var Box: TColorBox; const C: TColorBin);
- begin
- with C.Color do
- begin
- if A < Box.AMin then Box.AMin := A;
- if A > Box.AMax then Box.AMax := A;
- if B < Box.BMin then Box.BMin := B;
- if B > Box.BMax then Box.BMax := B;
- if G < Box.GMin then Box.GMin := G;
- if G > Box.GMax then Box.GMax := G;
- if R < Box.RMin then Box.RMin := R;
- if R > Box.RMax then Box.RMax := R;
- end;
- Inc(Box.Total, C.Number);
- end;
- procedure MakeColorMap;
- var
- I, J: LongInt;
- CP, Pom: PColorBin;
- Cut, LargestIdx, Largest, Size, S: LongInt;
- CutA, CutR, CutG, CutB: Boolean;
- SumA, SumR, SumG, SumB: LongInt;
- Temp: TColorBox;
- begin
- I := 0;
- Boxes := 1;
- LargestIdx := 0;
- while (I < HashSize) and (Table[I] = nil) do
- Inc(i);
- if I < HashSize then
- begin
- // put all colors into Box[0]
- InitBox(Box[0]);
- repeat
- CP := Table[I];
- while CP.Next <> nil do
- begin
- ChangeBox(Box[0], CP^);
- CP := CP.Next;
- end;
- ChangeBox(Box[0], CP^);
- CP.Next := Box[0].List;
- Box[0].List := Table[I];
- Table[I] := nil;
- repeat
- Inc(I)
- until (I = HashSize) or (Table[I] <> nil);
- until I = HashSize;
- // now all colors are in Box[0]
- repeat
- // cut one color box
- Largest := 0;
- for I := 0 to Boxes - 1 do
- with Box[I] do
- begin
- Size := (AMax - AMin) * AlphaWeight;
- S := (RMax - RMin) * RedWeight;
- if S > Size then
- Size := S;
- S := (GMax - GMin) * GreenWeight;
- if S > Size then
- Size := S;
- S := (BMax - BMin) * BlueWeight;
- if S > Size then
- Size := S;
- if Size > Largest then
- begin
- Largest := Size;
- LargestIdx := I;
- end;
- end;
- if Largest > 0 then
- begin
- // cutting Box[LargestIdx] into Box[LargestIdx] and Box[Boxes]
- CutR := False;
- CutG := False;
- CutB := False;
- CutA := False;
- with Box[LargestIdx] do
- begin
- if (AMax - AMin) * AlphaWeight = Largest then
- begin
- Cut := (AMax + AMin) shr 1;
- CutA := True;
- end
- else
- if (RMax - RMin) * RedWeight = Largest then
- begin
- Cut := (RMax + RMin) shr 1;
- CutR := True;
- end
- else
- if (GMax - GMin) * GreenWeight = Largest then
- begin
- Cut := (GMax + GMin) shr 1;
- CutG := True;
- end
- else
- begin
- Cut := (BMax + BMin) shr 1;
- CutB := True;
- end;
- CP := List;
- end;
- InitBox(Box[LargestIdx]);
- InitBox(Box[Boxes]);
- repeat
- // distribute one color
- Pom := CP.Next;
- with CP.Color do
- begin
- if (CutA and (A <= Cut)) or (CutR and (R <= Cut)) or
- (CutG and (G <= Cut)) or (CutB and (B <= Cut)) then
- I := LargestIdx
- else
- I := Boxes;
- end;
- CP.Next := Box[i].List;
- Box[i].List := CP;
- ChangeBox(Box[i], CP^);
- CP := Pom;
- until CP = nil;
- Inc(Boxes);
- end;
- until (Boxes = MaxColors) or (Largest = 0);
- // compute box representation
- for I := 0 to Boxes - 1 do
- begin
- SumR := 0;
- SumG := 0;
- SumB := 0;
- SumA := 0;
- repeat
- CP := Box[I].List;
- Inc(SumR, CP.Color.R * CP.Number);
- Inc(SumG, CP.Color.G * CP.Number);
- Inc(SumB, CP.Color.B * CP.Number);
- Inc(SumA, CP.Color.A * CP.Number);
- Box[I].List := CP.Next;
- Dispose(CP);
- until Box[I].List = nil;
- with Box[I] do
- begin
- Represented.A := SumA div Total;
- Represented.R := SumR div Total;
- Represented.G := SumG div Total;
- Represented.B := SumB div Total;
- AMin := AMin and ChannelMask;
- RMin := RMin and ChannelMask;
- GMin := GMin and ChannelMask;
- BMin := BMin and ChannelMask;
- AMax := (AMax and ChannelMask) + (not ChannelMask);
- RMax := (RMax and ChannelMask) + (not ChannelMask);
- GMax := (GMax and ChannelMask) + (not ChannelMask);
- BMax := (BMax and ChannelMask) + (not ChannelMask);
- end;
- end;
- // sort color boxes
- for I := 0 to Boxes - 2 do
- begin
- Largest := 0;
- for J := I to Boxes - 1 do
- if Box[J].Total > Largest then
- begin
- Largest := Box[J].Total;
- LargestIdx := J;
- end;
- if LargestIdx <> I then
- begin
- Temp := Box[I];
- Box[I] := Box[LargestIdx];
- Box[LargestIdx] := Temp;
- end;
- end;
- end;
- end;
- procedure FillOutputPalette;
- var
- I: LongInt;
- begin
- FillChar(DstPal^, SizeOf(TColor32Rec) * MaxColors, $FF);
- for I := 0 to MaxColors - 1 do
- begin
- if I < Boxes then
- with Box[I].Represented do
- begin
- DstPal[I].A := A;
- DstPal[I].R := R;
- DstPal[I].G := G;
- DstPal[I].B := B;
- end
- else
- DstPal[I].Color := $FF000000;
- end;
- end;
- function MapColor(const Col: TColor32Rec) : LongInt;
- var
- I: LongInt;
- begin
- I := 0;
- with Col do
- while (I < Boxes) and ((Box[I].AMin > A) or (Box[I].AMax < A) or
- (Box[I].RMin > R) or (Box[I].RMax < R) or (Box[I].GMin > G) or
- (Box[I].GMax < G) or (Box[I].BMin > B) or (Box[I].BMax < B)) do
- Inc(I);
- if I = Boxes then
- MapColor := 0
- else
- MapColor := I;
- end;
- procedure MapImage(Src, Dst: PByte; SrcInfo, DstInfo: PImageFormatInfo);
- var
- I: LongInt;
- Col: TColor32Rec;
- begin
- for I := 0 to NumPixels - 1 do
- begin
- Col := GetPixel32Generic(Src, SrcInfo, nil);
- IndexSetDstPixel(Dst, DstInfo, MapColor(Col));
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end;
- end;
- begin
- MaxColors := ClampInt(MaxColors, 2, MaxPossibleColors);
- if (raUpdateHistogram in Actions) or (raMapImage in Actions) then
- begin
- Assert(not SrcInfo.IsSpecial);
- Assert(not SrcInfo.IsIndexed);
- end;
- if raCreateHistogram in Actions then
- FillChar(Table, SizeOf(Table), 0);
- if raUpdateHistogram in Actions then
- CreateHistogram(Src, SrcInfo, ChannelMask);
- if raMakeColorMap in Actions then
- begin
- MakeColorMap;
- FillOutputPalette;
- end;
- if raMapImage in Actions then
- MapImage(Src, Dst, SrcInfo, DstInfo);
- end;
- procedure StretchNearest(const SrcImage: TImageData; SrcX, SrcY, SrcWidth,
- SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth,
- DstHeight: LongInt);
- var
- Info: TImageFormatInfo;
- ScaleX, ScaleY, X, Y, Xp, Yp: LongInt;
- DstPixel, SrcLine: PByte;
- begin
- GetImageFormatInfo(SrcImage.Format, Info);
- Assert(SrcImage.Format = DstImage.Format);
- Assert(not Info.IsSpecial);
- // Use integers instead of floats for source image pixel coords
- // Xp and Yp coords must be shifted right to get read source image coords
- ScaleX := (SrcWidth shl 16) div DstWidth;
- ScaleY := (SrcHeight shl 16) div DstHeight;
- Yp := 0;
- for Y := 0 to DstHeight - 1 do
- begin
- Xp := 0;
- SrcLine := @PByteArray(SrcImage.Bits)[((SrcY + Yp shr 16) * SrcImage.Width + SrcX) * Info.BytesPerPixel];
- DstPixel := @PByteArray(DstImage.Bits)[((DstY + Y) * DstImage.Width + DstX) * Info.BytesPerPixel];
- for X := 0 to DstWidth - 1 do
- begin
- case Info.BytesPerPixel of
- 1: PByte(DstPixel)^ := PByteArray(SrcLine)[Xp shr 16];
- 2: PWord(DstPixel)^ := PWordArray(SrcLine)[Xp shr 16];
- 3: PColor24Rec(DstPixel)^ := PPalette24(SrcLine)[Xp shr 16];
- 4: PColor32(DstPixel)^ := PUInt32Array(SrcLine)[Xp shr 16];
- 6: PColor48Rec(DstPixel)^ := PColor48RecArray(SrcLine)[Xp shr 16];
- 8: PColor64(DstPixel)^ := PInt64Array(SrcLine)[Xp shr 16];
- 16: PColorFPRec(DstPixel)^ := PColorFPRecArray(SrcLine)[Xp shr 16];
- end;
- Inc(DstPixel, Info.BytesPerPixel);
- Inc(Xp, ScaleX);
- end;
- Inc(Yp, ScaleY);
- end;
- end;
- { Filter function for nearest filtering. Also known as box filter.}
- function FilterNearest(Value: Single): Single;
- begin
- if (Value > -0.5) and (Value <= 0.5) then
- Result := 1
- else
- Result := 0;
- end;
- { Filter function for linear filtering. Also known as triangle or Bartlett filter.}
- function FilterLinear(Value: Single): Single;
- begin
- if Value < 0.0 then
- Value := -Value;
- if Value < 1.0 then
- Result := 1.0 - Value
- else
- Result := 0.0;
- end;
- { Cosine filter.}
- function FilterCosine(Value: Single): Single;
- begin
- Result := 0;
- if Abs(Value) < 1 then
- Result := (Cos(Value * Pi) + 1) / 2;
- end;
- { f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 }
- function FilterHermite(Value: Single): Single;
- begin
- if Value < 0.0 then
- Value := -Value;
- if Value < 1 then
- Result := (2 * Value - 3) * Sqr(Value) + 1
- else
- Result := 0;
- end;
- { Quadratic filter. Also known as Bell.}
- function FilterQuadratic(Value: Single): Single;
- begin
- if Value < 0.0 then
- Value := -Value;
- if Value < 0.5 then
- Result := 0.75 - Sqr(Value)
- else
- if Value < 1.5 then
- begin
- Value := Value - 1.5;
- Result := 0.5 * Sqr(Value);
- end
- else
- Result := 0.0;
- end;
- { Gaussian filter.}
- function FilterGaussian(Value: Single): Single;
- begin
- Result := Exp(-2.0 * Sqr(Value)) * Sqrt(2.0 / Pi);
- end;
- { 4th order (cubic) b-spline filter.}
- function FilterSpline(Value: Single): Single;
- var
- Temp: Single;
- begin
- if Value < 0.0 then
- Value := -Value;
- if Value < 1.0 then
- begin
- Temp := Sqr(Value);
- Result := 0.5 * Temp * Value - Temp + 2.0 / 3.0;
- end
- else
- if Value < 2.0 then
- begin
- Value := 2.0 - Value;
- Result := Sqr(Value) * Value / 6.0;
- end
- else
- Result := 0.0;
- end;
- { Lanczos-windowed sinc filter.}
- function FilterLanczos(Value: Single): Single;
- function SinC(Value: Single): Single;
- begin
- if Value <> 0.0 then
- begin
- Value := Value * Pi;
- Result := Sin(Value) / Value;
- end
- else
- Result := 1.0;
- end;
- begin
- if Value < 0.0 then
- Value := -Value;
- if Value < 3.0 then
- Result := SinC(Value) * SinC(Value / 3.0)
- else
- Result := 0.0;
- end;
- { Mitchell cubic filter.}
- function FilterMitchell(Value: Single): Single;
- const
- B = 1.0 / 3.0;
- C = 1.0 / 3.0;
- var
- Temp: Single;
- begin
- if Value < 0.0 then
- Value := -Value;
- Temp := Sqr(Value);
- if Value < 1.0 then
- begin
- Value := (((12.0 - 9.0 * B - 6.0 * C) * (Value * Temp)) +
- ((-18.0 + 12.0 * B + 6.0 * C) * Temp) +
- (6.0 - 2.0 * B));
- Result := Value / 6.0;
- end
- else
- if Value < 2.0 then
- begin
- Value := (((-B - 6.0 * C) * (Value * Temp)) +
- ((6.0 * B + 30.0 * C) * Temp) +
- ((-12.0 * B - 48.0 * C) * Value) +
- (8.0 * B + 24.0 * C));
- Result := Value / 6.0;
- end
- else
- Result := 0.0;
- end;
- { CatmullRom spline filter.}
- function FilterCatmullRom(Value: Single): Single;
- begin
- if Value < 0.0 then
- Value := -Value;
- if Value < 1.0 then
- Result := 0.5 * (2.0 + Sqr(Value) * (-5.0 + 3.0 * Value))
- else
- if Value < 2.0 then
- Result := 0.5 * (4.0 + Value * (-8.0 + Value * (5.0 - Value)))
- else
- Result := 0.0;
- end;
- procedure StretchResample(const SrcImage: TImageData; SrcX, SrcY, SrcWidth,
- SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth,
- DstHeight: LongInt; Filter: TSamplingFilter; WrapEdges: Boolean);
- begin
- // Calls the other function with filter function and radius defined by Filter
- StretchResample(SrcImage, SrcX, SrcY, SrcWidth, SrcHeight, DstImage, DstX, DstY,
- DstWidth, DstHeight, SamplingFilterFunctions[Filter], SamplingFilterRadii[Filter],
- WrapEdges);
- end;
- var
- FullEdge: Boolean = True;
- { The following resampling code is modified and extended code from Graphics32
- library by Alex A. Denisov.}
- function BuildMappingTable(DstLow, DstHigh, SrcLow, SrcHigh, SrcImageWidth: LongInt;
- Filter: TFilterFunction; Radius: Single; WrapEdges: Boolean): TMappingTable;
- var
- I, J, K, N: LongInt;
- Left, Right, SrcWidth, DstWidth: LongInt;
- Weight, Scale, Center: Single;
- begin
- Result := nil;
- SrcWidth := SrcHigh - SrcLow;
- DstWidth := DstHigh - DstLow;
- // Check some special cases
- if SrcWidth = 1 then
- begin
- SetLength(Result, DstWidth);
- for I := 0 to DstWidth - 1 do
- begin
- SetLength(Result[I], 1);
- Result[I][0].Pos := 0;
- Result[I][0].Weight := 1.0;
- end;
- Exit;
- end
- else
- if (SrcWidth = 0) or (DstWidth = 0) then
- Exit;
- if FullEdge then
- Scale := DstWidth / SrcWidth
- else
- Scale := (DstWidth - 1) / (SrcWidth - 1);
- SetLength(Result, DstWidth);
- // Pre-calculate filter contributions for a row or column
- if Scale = 0.0 then
- begin
- Assert(Length(Result) = 1);
- SetLength(Result[0], 1);
- Result[0][0].Pos := (SrcLow + SrcHigh) div 2;
- Result[0][0].Weight := 1.0;
- end
- else if Scale < 1.0 then
- begin
- // Sub-sampling - scales from bigger to smaller
- Radius := Radius / Scale;
- for I := 0 to DstWidth - 1 do
- begin
- if FullEdge then
- Center := SrcLow - 0.5 + (I + 0.5) / Scale
- else
- Center := SrcLow + I / Scale;
- Left := Floor(Center - Radius);
- Right := Ceil(Center + Radius);
- for J := Left to Right do
- begin
- Weight := Filter((Center - J) * Scale) * Scale;
- if Weight <> 0.0 then
- begin
- K := Length(Result[I]);
- SetLength(Result[I], K + 1);
- Result[I][K].Pos := ClampInt(J, SrcLow, SrcHigh - 1);
- Result[I][K].Weight := Weight;
- end;
- end;
- if Length(Result[I]) = 0 then
- begin
- SetLength(Result[I], 1);
- Result[I][0].Pos := Floor(Center);
- Result[I][0].Weight := 1.0;
- end;
- end;
- end
- else // if Scale > 1.0 then
- begin
- // Super-sampling - scales from smaller to bigger
- Scale := 1.0 / Scale;
- for I := 0 to DstWidth - 1 do
- begin
- if FullEdge then
- Center := SrcLow - 0.5 + (I + 0.5) * Scale
- else
- Center := SrcLow + I * Scale;
- Left := Floor(Center - Radius);
- Right := Ceil(Center + Radius);
- for J := Left to Right do
- begin
- Weight := Filter(Center - J);
- if Weight <> 0.0 then
- begin
- K := Length(Result[I]);
- SetLength(Result[I], K + 1);
- if WrapEdges then
- begin
- if J < 0 then
- N := SrcImageWidth + J
- else if J >= SrcImageWidth then
- N := J - SrcImageWidth
- else
- N := ClampInt(J, SrcLow, SrcHigh - 1);
- end
- else
- N := ClampInt(J, SrcLow, SrcHigh - 1);
- Result[I][K].Pos := N;
- Result[I][K].Weight := Weight;
- end;
- end;
- end;
- end;
- end;
- procedure FindExtremes(const Map: TMappingTable; var MinPos, MaxPos: LongInt);
- var
- I, J: LongInt;
- begin
- if Length(Map) > 0 then
- begin
- MinPos := Map[0][0].Pos;
- MaxPos := MinPos;
- for I := 0 to Length(Map) - 1 do
- for J := 0 to Length(Map[I]) - 1 do
- begin
- if MinPos > Map[I][J].Pos then
- MinPos := Map[I][J].Pos;
- if MaxPos < Map[I][J].Pos then
- MaxPos := Map[I][J].Pos;
- end;
- end;
- end;
- procedure StretchResample(const SrcImage: TImageData; SrcX, SrcY, SrcWidth,
- SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth,
- DstHeight: LongInt; Filter: TFilterFunction; Radius: Single; WrapEdges: Boolean);
- var
- MapX, MapY: TMappingTable;
- I, J, X, Y: LongInt;
- XMinimum, XMaximum: LongInt;
- LineBufferFP: array of TColorFPRec;
- ClusterX, ClusterY: TCluster;
- Weight, AccumA, AccumR, AccumG, AccumB: Single;
- DstLine: PByte;
- SrcFloat: TColorFPRec;
- Info: TImageFormatInfo;
- BytesPerChannel: Integer;
- begin
- GetImageFormatInfo(SrcImage.Format, Info);
- Assert(SrcImage.Format = DstImage.Format);
- Assert(not Info.IsSpecial and not Info.IsIndexed);
- BytesPerChannel := Info.BytesPerPixel div Info.ChannelCount;
- // Create horizontal and vertical mapping tables
- MapX := BuildMappingTable(DstX, DstX + DstWidth, SrcX, SrcX + SrcWidth,
- SrcImage.Width, Filter, Radius, WrapEdges);
- MapY := BuildMappingTable(DstY, DstY + DstHeight, SrcY, SrcY + SrcHeight,
- SrcImage.Height, Filter, Radius, WrapEdges);
- if (MapX = nil) or (MapY = nil) then
- Exit;
- try
- // Find min and max X coords of pixels that will contribute to target image
- FindExtremes(MapX, XMinimum, XMaximum);
- SetLength(LineBufferFP, XMaximum - XMinimum + 1);
- for J := 0 to DstHeight - 1 do
- begin
- // First for each pixel in the current line sample vertically
- // and store results in LineBuffer. Then sample horizontally
- // using values in LineBuffer.
- ClusterY := MapY[J];
- for X := XMinimum to XMaximum do
- begin
- // Clear accumulators
- AccumA := 0;
- AccumR := 0;
- AccumG := 0;
- AccumB := 0;
- // For each pixel in line compute weighted sum of pixels
- // in source column that will contribute to this pixel
- for Y := 0 to Length(ClusterY) - 1 do
- begin
- // Accumulate this pixel's weighted value
- Weight := ClusterY[Y].Weight;
- SrcFloat := Info.GetPixelFP(@PByteArray(SrcImage.Bits)[(ClusterY[Y].Pos * SrcImage.Width + X) * Info.BytesPerPixel], @Info, nil);
- AccumA := AccumA + SrcFloat.A * Weight;
- AccumR := AccumR + SrcFloat.R * Weight;
- AccumG := AccumG + SrcFloat.G * Weight;
- AccumB := AccumB + SrcFloat.B * Weight;
- end;
- // Store accumulated value for this pixel in buffer
- with LineBufferFP[X - XMinimum] do
- begin
- A := AccumA;
- R := AccumR;
- G := AccumG;
- B := AccumB;
- end;
- end;
- DstLine := @PByteArray(DstImage.Bits)[((J + DstY) * DstImage.Width + DstX) * Info.BytesPerPixel];
- // Now compute final colors for target pixels in the current row
- // by sampling horizontally
- for I := 0 to DstWidth - 1 do
- begin
- ClusterX := MapX[I];
- // Clear accumulator
- AccumA := 0;
- AccumR := 0;
- AccumG := 0;
- AccumB := 0;
- // Compute weighted sum of values (which are already
- // computed weighted sums of pixels in source columns stored in LineBuffer)
- // that will contribute to the current target pixel
- for X := 0 to Length(ClusterX) - 1 do
- begin
- Weight := ClusterX[X].Weight;
- with LineBufferFP[ClusterX[X].Pos - XMinimum] do
- begin
- AccumA := AccumA + A * Weight;
- AccumR := AccumR + R * Weight;
- AccumG := AccumG + G * Weight;
- AccumB := AccumB + B * Weight;
- end;
- end;
- // Now compute final color to be written to dest image
- SrcFloat.A := AccumA;
- SrcFloat.R := AccumR;
- SrcFloat.G := AccumG;
- SrcFloat.B := AccumB;
- Info.SetPixelFP(DstLine, @Info, nil, SrcFloat);
- Inc(DstLine, Info.BytesPerPixel);
- end;
- end;
- finally
- MapX := nil;
- MapY := nil;
- end;
- end;
- procedure FillMipMapLevel(const BiggerLevel: TImageData; Width, Height: LongInt;
- var SmallerLevel: TImageData);
- var
- Filter: TSamplingFilter;
- Info: TImageFormatInfo;
- CompatibleCopy: TImageData;
- begin
- Assert(TestImage(BiggerLevel));
- Filter := TSamplingFilter(GetOption(ImagingMipMapFilter));
- // If we have special format image we must create copy to allow pixel access
- GetImageFormatInfo(BiggerLevel.Format, Info);
- if Info.IsSpecial then
- begin
- InitImage(CompatibleCopy);
- CloneImage(BiggerLevel, CompatibleCopy);
- ConvertImage(CompatibleCopy, ifDefault);
- end
- else
- CompatibleCopy := BiggerLevel;
- // Create new smaller image
- NewImage(Width, Height, CompatibleCopy.Format, SmallerLevel);
- GetImageFormatInfo(CompatibleCopy.Format, Info);
- // If input is indexed we must copy its palette
- if Info.IsIndexed then
- CopyPalette(CompatibleCopy.Palette, SmallerLevel.Palette, 0, 0, Info.PaletteEntries);
- if (Filter = sfNearest) or Info.IsIndexed then
- begin
- StretchNearest(CompatibleCopy, 0, 0, CompatibleCopy.Width, CompatibleCopy.Height,
- SmallerLevel, 0, 0, Width, Height);
- end
- else
- begin
- StretchResample(CompatibleCopy, 0, 0, CompatibleCopy.Width, CompatibleCopy.Height,
- SmallerLevel, 0, 0, Width, Height, Filter);
- end;
- // Free copy and convert result to special format if necessary
- if CompatibleCopy.Format <> BiggerLevel.Format then
- begin
- ConvertImage(SmallerLevel, BiggerLevel.Format);
- FreeImage(CompatibleCopy);
- end;
- end;
- { Various format support functions }
- procedure CopyPixel(Src, Dest: Pointer; BytesPerPixel: LongInt);
- begin
- case BytesPerPixel of
- 1: PByte(Dest)^ := PByte(Src)^;
- 2: PWord(Dest)^ := PWord(Src)^;
- 3: PColor24Rec(Dest)^ := PColor24Rec(Src)^;
- 4: PUInt32(Dest)^ := PUInt32(Src)^;
- 6: PColor48Rec(Dest)^ := PColor48Rec(Src)^;
- 8: PInt64(Dest)^ := PInt64(Src)^;
- 12: PColor96FPRec(Dest)^ := PColor96FPRec(Src)^;
- 16: PColorFPRec(Dest)^ := PColorFPRec(Src)^;
- end;
- end;
- function ComparePixels(PixelA, PixelB: Pointer; BytesPerPixel: LongInt): Boolean;
- begin
- case BytesPerPixel of
- 1: Result := PByte(PixelA)^ = PByte(PixelB)^;
- 2: Result := PWord(PixelA)^ = PWord(PixelB)^;
- 3: Result := (PWord(PixelA)^ = PWord(PixelB)^) and (PColor24Rec(PixelA).R = PColor24Rec(PixelB).R);
- 4: Result := PUInt32(PixelA)^ = PUInt32(PixelB)^;
- 6: Result := (PUInt32(PixelA)^ = PUInt32(PixelB)^) and (PColor48Rec(PixelA).R = PColor48Rec(PixelB).R);
- 8: Result := PInt64(PixelA)^ = PInt64(PixelB)^;
- 12: Result := (PFloatHelper(PixelA).Data = PFloatHelper(PixelB).Data) and
- (PFloatHelper(PixelA).Data32 = PFloatHelper(PixelB).Data32);
- 16: Result := (PFloatHelper(PixelA).Data = PFloatHelper(PixelB).Data) and
- (PFloatHelper(PixelA).Data64 = PFloatHelper(PixelB).Data64);
- else
- Result := False;
- end;
- end;
- procedure TranslatePixel(SrcPixel, DstPixel: Pointer; SrcFormat,
- DstFormat: TImageFormat; SrcPalette, DstPalette: PPalette32);
- var
- SrcInfo, DstInfo: PImageFormatInfo;
- PixFP: TColorFPRec;
- begin
- SrcInfo := FInfos[SrcFormat];
- DstInfo := FInfos[DstFormat];
- PixFP := GetPixelFPGeneric(SrcPixel, SrcInfo, SrcPalette);
- SetPixelFPGeneric(DstPixel, DstInfo, DstPalette, PixFP);
- end;
- procedure ClampFloatPixel(var PixF: TColorFPRec);
- begin
- if PixF.A > 1.0 then
- PixF.A := 1.0;
- if PixF.R > 1.0 then
- PixF.R := 1.0;
- if PixF.G > 1.0 then
- PixF.G := 1.0;
- if PixF.B > 1.0 then
- PixF.B := 1.0;
- if PixF.A < 0.0 then
- PixF.A := 0.0;
- if PixF.R < 0.0 then
- PixF.R := 0.0;
- if PixF.G < 0.0 then
- PixF.G := 0.0;
- if PixF.B < 0.0 then
- PixF.B := 0.0;
- end;
- procedure ConvertToPixel32(SrcPix: PByte; DestPix: PColor32Rec;
- const SrcInfo: TImageFormatInfo; SrcPalette: PPalette32);
- begin
- case SrcInfo.Format of
- ifIndex8:
- begin
- DestPix^ := SrcPalette[SrcPix^];
- end;
- ifGray8:
- begin
- DestPix.R := SrcPix^;
- DestPix.G := SrcPix^;
- DestPix.B := SrcPix^;
- DestPix.A := 255;
- end;
- ifA8Gray8:
- begin
- DestPix.R := SrcPix^;
- DestPix.G := SrcPix^;
- DestPix.B := SrcPix^;
- DestPix.A := PWordRec(SrcPix).High;
- end;
- ifGray16:
- begin
- DestPix.R := PWord(SrcPix)^ shr 8;
- DestPix.G := DestPix.R;
- DestPix.B := DestPix.R;
- DestPix.A := 255;
- end;
- ifR8G8B8:
- begin
- DestPix.Color24Rec := PColor24Rec(SrcPix)^;
- DestPix.A := 255;
- end;
- ifA8R8G8B8:
- begin
- DestPix^ := PColor32Rec(SrcPix)^;
- end;
- ifR16G16B16:
- begin
- DestPix.R := PColor48Rec(SrcPix).R shr 8;
- DestPix.G := PColor48Rec(SrcPix).G shr 8;
- DestPix.B := PColor48Rec(SrcPix).B shr 8;
- DestPix.A := 255;
- end;
- ifA16R16G16B16:
- begin
- DestPix.R := PColor64Rec(SrcPix).R shr 8;
- DestPix.G := PColor64Rec(SrcPix).G shr 8;
- DestPix.B := PColor64Rec(SrcPix).B shr 8;
- DestPix.A := PColor64Rec(SrcPix).A shr 8;
- end;
- else
- DestPix^ := SrcInfo.GetPixel32(SrcPix, @SrcInfo, SrcPalette);
- end;
- end;
- procedure AddPadBytes(DataIn: Pointer; DataOut: Pointer; Width, Height,
- Bpp, WidthBytes: LongInt);
- var
- I, W: LongInt;
- begin
- W := Width * Bpp;
- for I := 0 to Height - 1 do
- Move(PByteArray(DataIn)[I * W], PByteArray(DataOut)[I * WidthBytes], W);
- end;
- procedure RemovePadBytes(DataIn: Pointer; DataOut: Pointer; Width, Height,
- Bpp, WidthBytes: LongInt);
- var
- I, W: LongInt;
- begin
- W := Width * Bpp;
- for I := 0 to Height - 1 do
- Move(PByteArray(DataIn)[I * WidthBytes], PByteArray(DataOut)[I * W], W);
- end;
- procedure Convert1To8(DataIn, DataOut: PByte; Width, Height,
- WidthBytes: LongInt; ScaleTo8Bits: Boolean);
- const
- Mask1: array[0..7] of Byte = ($80, $40, $20, $10, $08, $04, $02, $01);
- Shift1: array[0..7] of Byte = (7, 6, 5, 4, 3, 2, 1, 0);
- Scaling: Byte = 255;
- var
- X, Y: LongInt;
- InArray: PByteArray absolute DataIn;
- begin
- for Y := 0 to Height - 1 do
- for X := 0 to Width - 1 do
- begin
- DataOut^ := (InArray[Y * WidthBytes + X shr 3] and Mask1[X and 7]) shr Shift1[X and 7];
- if ScaleTo8Bits then
- DataOut^ := DataOut^ * Scaling;
- Inc(DataOut);
- end;
- end;
- procedure Convert2To8(DataIn, DataOut: PByte; Width, Height,
- WidthBytes: LongInt; ScaleTo8Bits: Boolean);
- const
- Mask2: array[0..3] of Byte = ($C0, $30, $0C, $03);
- Shift2: array[0..3] of Byte = (6, 4, 2, 0);
- Scaling: Byte = 85;
- var
- X, Y: LongInt;
- InArray: PByteArray absolute DataIn;
- begin
- for Y := 0 to Height - 1 do
- for X := 0 to Width - 1 do
- begin
- DataOut^ := (InArray[Y * WidthBytes + X shr 2] and Mask2[X and 3]) shr Shift2[X and 3];
- if ScaleTo8Bits then
- DataOut^ := DataOut^ * Scaling;
- Inc(DataOut);
- end;
- end;
- procedure Convert4To8(DataIn, DataOut: PByte; Width, Height,
- WidthBytes: LongInt; ScaleTo8Bits: Boolean);
- const
- Mask4: array[0..1] of Byte = ($F0, $0F);
- Shift4: array[0..1] of Byte = (4, 0);
- Scaling: Byte = 17;
- var
- X, Y: LongInt;
- InArray: PByteArray absolute DataIn;
- begin
- for Y := 0 to Height - 1 do
- for X := 0 to Width - 1 do
- begin
- DataOut^ := (InArray[Y * WidthBytes + X shr 1] and Mask4[X and 1]) shr Shift4[X and 1];
- if ScaleTo8Bits then
- DataOut^ := DataOut^ * Scaling;
- Inc(DataOut);
- end;
- end;
- function Has16BitImageAlpha(NumPixels: LongInt; Data: PWord): Boolean;
- var
- I: LongInt;
- begin
- Result := False;
- for I := 0 to NumPixels - 1 do
- begin
- if Data^ >= 1 shl 15 then
- begin
- Result := True;
- Exit;
- end;
- Inc(Data);
- end;
- end;
- function Has32BitImageAlpha(NumPixels: LongInt; Data: PUInt32): Boolean;
- var
- I: LongInt;
- begin
- Result := False;
- for I := 0 to NumPixels - 1 do
- begin
- if Data^ >= 1 shl 24 then
- begin
- Result := True;
- Exit;
- end;
- Inc(Data);
- end;
- end;
- function PaletteHasAlpha(Palette: PPalette32; PaletteEntries: Integer): Boolean;
- var
- I: Integer;
- begin
- for I := 0 to PaletteEntries - 1 do
- begin
- if Palette[I].A <> 255 then
- begin
- Result := True;
- Exit;
- end;
- end;
- Result := False;
- end;
- function PaletteIsGrayScale(Palette: PPalette32; PaletteEntries: Integer): Boolean;
- var
- I: Integer;
- begin
- for I := 0 to PaletteEntries - 1 do
- begin
- if (Palette[I].R <> Palette[I].G) or (Palette[I].R <> Palette[I].B) then
- begin
- Result := False;
- Exit;
- end;
- end;
- Result := True;
- end;
- function GetScanLine(ImageBits: Pointer; const FormatInfo: TImageFormatInfo;
- LineWidth, Index: LongInt): Pointer;
- var
- LineBytes: LongInt;
- begin
- Assert(not FormatInfo.IsSpecial);
- LineBytes := FormatInfo.GetPixelsSize(FormatInfo.Format, LineWidth, 1);
- Result := @PByteArray(ImageBits)[Index * LineBytes];
- end;
- function IsImageFormatValid(Format: TImageFormat): Boolean;
- begin
- Result := FInfos[Format] <> nil;
- end;
- const
- HalfMin: Single = 5.96046448e-08; // Smallest positive half
- HalfMinNorm: Single = 6.10351562e-05; // Smallest positive normalized half
- HalfMax: Single = 65504.0; // Largest positive half
- HalfEpsilon: Single = 0.00097656; // Smallest positive e for which half (1.0 + e) != half (1.0)
- HalfNaN: THalfFloat = 65535;
- HalfPosInf: THalfFloat = 31744;
- HalfNegInf: THalfFloat = 64512;
- {
- Half/Float conversions inspired by half class from OpenEXR library.
- Float (Pascal Single type) is an IEEE 754 single-precision
- floating point number.
- Bit layout of Single:
- 31 (msb)
- |
- | 30 23
- | | |
- | | | 22 0 (lsb)
- | | | | |
- X XXXXXXXX XXXXXXXXXXXXXXXXXXXXXXX
- s e m
- Bit layout of half:
- 15 (msb)
- |
- | 14 10
- | | |
- | | | 9 0 (lsb)
- | | | | |
- X XXXXX XXXXXXXXXX
- s e m
- S is the sign-bit, e is the exponent and m is the significand (mantissa).
- }
- function HalfToFloat(Half: THalfFloat): Single;
- var
- Dst, Sign, Mantissa: UInt32;
- Exp: Int32;
- begin
- // Extract sign, exponent, and mantissa from half number
- Sign := Half shr 15;
- Exp := (Half and $7C00) shr 10;
- Mantissa := Half and 1023;
- if (Exp > 0) and (Exp < 31) then
- begin
- // Common normalized number
- Exp := Exp + (127 - 15);
- Mantissa := Mantissa shl 13;
- Dst := (Sign shl 31) or (UInt32(Exp) shl 23) or Mantissa;
- // Result := Power(-1, Sign) * Power(2, Exp - 15) * (1 + Mantissa / 1024);
- end
- else if (Exp = 0) and (Mantissa = 0) then
- begin
- // Zero - preserve sign
- Dst := Sign shl 31;
- end
- else if (Exp = 0) and (Mantissa <> 0) then
- begin
- // Denormalized number - renormalize it
- while (Mantissa and $00000400) = 0 do
- begin
- Mantissa := Mantissa shl 1;
- Dec(Exp);
- end;
- Inc(Exp);
- Mantissa := Mantissa and not $00000400;
- // Now assemble normalized number
- Exp := Exp + (127 - 15);
- Mantissa := Mantissa shl 13;
- Dst := (Sign shl 31) or (UInt32(Exp) shl 23) or Mantissa;
- // Result := Power(-1, Sign) * Power(2, -14) * (Mantissa / 1024);
- end
- else if (Exp = 31) and (Mantissa = 0) then
- begin
- // +/- infinity
- Dst := (Sign shl 31) or $7F800000;
- end
- else //if (Exp = 31) and (Mantisa <> 0) then
- begin
- // Not a number - preserve sign and mantissa
- Dst := (Sign shl 31) or $7F800000 or (Mantissa shl 13);
- end;
- // Reinterpret LongWord as Single
- Result := PSingle(@Dst)^;
- end;
- function FloatToHalf(Float: Single): THalfFloat;
- var
- Src: UInt32;
- Sign, Exp, Mantissa: Int32;
- begin
- Src := PUInt32(@Float)^;
- // Extract sign, exponent, and mantissa from Single number
- Sign := Src shr 31;
- Exp := Int32((Src and $7F800000) shr 23) - 127 + 15;
- Mantissa := Src and $007FFFFF;
- if (Exp > 0) and (Exp < 30) then
- begin
- // Simple case - round the significand and combine it with the sign and exponent
- Result := (Sign shl 15) or (Exp shl 10) or ((Mantissa + $00001000) shr 13);
- end
- else if Src = 0 then
- begin
- // Input float is zero - return zero
- Result := 0;
- end
- else
- begin
- // Difficult case - lengthy conversion
- if Exp <= 0 then
- begin
- if Exp < -10 then
- begin
- // Input float's value is less than HalfMin, return zero
- Result := 0;
- end
- else
- begin
- // Float is a normalized Single whose magnitude is less than HalfNormMin.
- // We convert it to denormalized half.
- Mantissa := (Mantissa or $00800000) shr (1 - Exp);
- // Round to nearest
- if (Mantissa and $00001000) > 0 then
- Mantissa := Mantissa + $00002000;
- // Assemble Sign and Mantissa (Exp is zero to get denormalized number)
- Result := (Sign shl 15) or (Mantissa shr 13);
- end;
- end
- else if Exp = 255 - 127 + 15 then
- begin
- if Mantissa = 0 then
- begin
- // Input float is infinity, create infinity half with original sign
- Result := (Sign shl 15) or $7C00;
- end
- else
- begin
- // Input float is NaN, create half NaN with original sign and mantissa
- Result := (Sign shl 15) or $7C00 or (Mantissa shr 13);
- end;
- end
- else
- begin
- // Exp is > 0 so input float is normalized Single
- // Round to nearest
- if (Mantissa and $00001000) > 0 then
- begin
- Mantissa := Mantissa + $00002000;
- if (Mantissa and $00800000) > 0 then
- begin
- Mantissa := 0;
- Exp := Exp + 1;
- end;
- end;
- if Exp > 30 then
- begin
- // Exponent overflow - return infinity half
- Result := (Sign shl 15) or $7C00;
- end
- else
- // Assemble normalized half
- Result := (Sign shl 15) or (Exp shl 10) or (Mantissa shr 13);
- end;
- end;
- end;
- function ColorHalfToFloat(ColorHF: TColorHFRec): TColorFPRec;
- begin
- Result.A := HalfToFloat(ColorHF.A);
- Result.R := HalfToFloat(ColorHF.R);
- Result.G := HalfToFloat(ColorHF.G);
- Result.B := HalfToFloat(ColorHF.B);
- end;
- function ColorFloatToHalf(ColorFP: TColorFPRec): TColorHFRec;
- begin
- Result.A := FloatToHalf(ColorFP.A);
- Result.R := FloatToHalf(ColorFP.R);
- Result.G := FloatToHalf(ColorFP.G);
- Result.B := FloatToHalf(ColorFP.B);
- end;
- function Color32ToGray(Color32: TColor32): Byte;
- begin
- Result := Round(GrayConv.R * TColor32Rec(Color32).R +
- GrayConv.G * TColor32Rec(Color32).G +
- GrayConv.B * TColor32Rec(Color32).B);
- end;
- procedure VisualizePalette(Pal: PPalette32; Entries: Integer; out PalImage: TImageData);
- var
- I: Integer;
- Pix: PColor32;
- begin
- InitImage(PalImage);
- NewImage(Entries, 1, ifA8R8G8B8, PalImage);
- Pix := PalImage.Bits;
- for I := 0 to Entries - 1 do
- begin
- Pix^ := Pal[I].Color;
- Inc(Pix);
- end;
- end;
- { Pixel readers/writers for different image formats }
- procedure ChannelGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo;
- var Pix: TColor64Rec);
- var
- A, R, G, B: Byte;
- begin
- FillChar(Pix, SizeOf(Pix), 0);
- // returns 64 bit color value with 16 bits for each channel
- case SrcInfo.BytesPerPixel of
- 1:
- begin
- PFGetARGB(SrcInfo.PixelFormat^, Src^, A, R, G, B);
- Pix.A := A shl 8;
- Pix.R := R shl 8;
- Pix.G := G shl 8;
- Pix.B := B shl 8;
- end;
- 2:
- begin
- PFGetARGB(SrcInfo.PixelFormat^, PWord(Src)^, A, R, G, B);
- Pix.A := A shl 8;
- Pix.R := R shl 8;
- Pix.G := G shl 8;
- Pix.B := B shl 8;
- end;
- 3:
- with Pix do
- begin
- R := MulDiv(PColor24Rec(Src).R, 65535, 255);
- G := MulDiv(PColor24Rec(Src).G, 65535, 255);
- B := MulDiv(PColor24Rec(Src).B, 65535, 255);
- end;
- 4:
- with Pix do
- begin
- A := MulDiv(PColor32Rec(Src).A, 65535, 255);
- R := MulDiv(PColor32Rec(Src).R, 65535, 255);
- G := MulDiv(PColor32Rec(Src).G, 65535, 255);
- B := MulDiv(PColor32Rec(Src).B, 65535, 255);
- end;
- 6:
- with Pix do
- begin
- R := PColor48Rec(Src).R;
- G := PColor48Rec(Src).G;
- B := PColor48Rec(Src).B;
- end;
- 8: Pix.Color := PColor64(Src)^;
- end;
- // if src has no alpha, we set it to max (otherwise we would have to
- // test if dest has alpha or not in each ChannelToXXX function)
- if not SrcInfo.HasAlphaChannel then
- Pix.A := 65535;
- if SrcInfo.IsRBSwapped then
- SwapValues(Pix.R, Pix.B);
- end;
- procedure ChannelSetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo;
- const Pix: TColor64Rec);
- var
- PixW: TColor64Rec;
- begin
- PixW := Pix;
- if DstInfo.IsRBSwapped then
- SwapValues(PixW.R, PixW.B);
- // Pix contains 64 bit color value with 16 bit for each channel
- case DstInfo.BytesPerPixel of
- 1: Dst^ := PFSetARGB(DstInfo.PixelFormat^, PixW.A shr 8,
- PixW.R shr 8, PixW.G shr 8, PixW.B shr 8);
- 2: PWord(Dst)^ := PFSetARGB(DstInfo.PixelFormat^, PixW.A shr 8,
- PixW.R shr 8, PixW.G shr 8, PixW.B shr 8);
- 3:
- with PColor24Rec(Dst)^ do
- begin
- R := MulDiv(PixW.R, 255, 65535);
- G := MulDiv(PixW.G, 255, 65535);
- B := MulDiv(PixW.B, 255, 65535);
- end;
- 4:
- with PColor32Rec(Dst)^ do
- begin
- A := MulDiv(PixW.A, 255, 65535);
- R := MulDiv(PixW.R, 255, 65535);
- G := MulDiv(PixW.G, 255, 65535);
- B := MulDiv(PixW.B, 255, 65535);
- end;
- 6:
- with PColor48Rec(Dst)^ do
- begin
- R := PixW.R;
- G := PixW.G;
- B := PixW.B;
- end;
- 8: PColor64(Dst)^ := PixW.Color;
- end;
- end;
- procedure GrayGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo;
- var Gray: TColor64Rec; var Alpha: Word);
- begin
- FillChar(Gray, SizeOf(Gray), 0);
- // Source alpha is scaled to 16 bits and stored in Alpha,
- // grayscale value is scaled to 64 bits and stored in Gray
- case SrcInfo.BytesPerPixel of
- 1: Gray.A := MulDiv(Src^, 65535, 255);
- 2:
- if SrcInfo.HasAlphaChannel then
- with PWordRec(Src)^ do
- begin
- Alpha := MulDiv(High, 65535, 255);
- Gray.A := MulDiv(Low, 65535, 255);
- end
- else
- Gray.A := PWord(Src)^;
- 4:
- if SrcInfo.HasAlphaChannel then
- with PUInt32Rec(Src)^ do
- begin
- Alpha := High;
- Gray.A := Low;
- end
- else
- with PUInt32Rec(Src)^ do
- begin
- Gray.A := High;
- Gray.R := Low;
- end;
- 8: Gray.Color := PColor64(Src)^;
- end;
- // if src has no alpha, we set it to max (otherwise we would have to
- // test if dest has alpha or not in each GrayToXXX function)
- if not SrcInfo.HasAlphaChannel then
- Alpha := 65535;
- end;
- procedure GraySetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo;
- const Gray: TColor64Rec; Alpha: Word);
- begin
- // Gray contains grayscale value scaled to 64 bits, Alpha contains
- // alpha value scaled to 16 bits
- case DstInfo.BytesPerPixel of
- 1: Dst^ := MulDiv(Gray.A, 255, 65535);
- 2:
- if DstInfo.HasAlphaChannel then
- with PWordRec(Dst)^ do
- begin
- High := MulDiv(Alpha, 255, 65535);
- Low := MulDiv(Gray.A, 255, 65535);
- end
- else
- PWord(Dst)^ := Gray.A;
- 4:
- if DstInfo.HasAlphaChannel then
- with PUInt32Rec(Dst)^ do
- begin
- High := Alpha;
- Low := Gray.A;
- end
- else
- with PUInt32Rec(Dst)^ do
- begin
- High := Gray.A;
- Low := Gray.R;
- end;
- 8: PColor64(Dst)^ := Gray.Color;
- end;
- end;
- procedure FloatGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo;
- var Pix: TColorFPRec);
- var
- PixHF: TColorHFRec;
- begin
- Assert(SrcInfo.BytesPerPixel in [2, 4, 8, 12, 16]);
- if SrcInfo.BytesPerPixel in [4, 12, 16] then
- begin
- // IEEE 754 single-precision channels
- FillChar(Pix, SizeOf(Pix), 0);
- case SrcInfo.BytesPerPixel of
- 4: Pix.R := PSingle(Src)^;
- 12: Pix.Color96Rec := PColor96FPRec(Src)^;
- 16: Pix := PColorFPRec(Src)^;
- end;
- end
- else
- begin
- // Half float channels
- FillChar(PixHF, SizeOf(PixHF), 0);
- case SrcInfo.BytesPerPixel of
- 2: PixHF.R := PHalfFloat(Src)^;
- 8: PixHF := PColorHFRec(Src)^;
- end;
- Pix := ColorHalfToFloat(PixHF);
- end;
- // If src has no alpha, we set it to max (otherwise we would have to
- // test if dest has alpha or not in each FloatToXXX function)
- if not SrcInfo.HasAlphaChannel then
- Pix.A := 1.0;
- if SrcInfo.IsRBSwapped then
- SwapValues(Pix.R, Pix.B);
- end;
- procedure FloatSetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo;
- const Pix: TColorFPRec);
- var
- PixW: TColorFPRec;
- PixHF: TColorHFRec;
- begin
- Assert(DstInfo.BytesPerPixel in [2, 4, 8, 12, 16]);
- PixW := Pix;
- if DstInfo.IsRBSwapped then
- SwapValues(PixW.R, PixW.B);
- if DstInfo.BytesPerPixel in [4, 12, 16] then
- begin
- case DstInfo.BytesPerPixel of
- 4: PSingle(Dst)^ := PixW.R;
- 12: PColor96FPRec(Dst)^:= PixW.Color96Rec;
- 16: PColorFPRec(Dst)^ := PixW;
- end;
- end
- else
- begin
- PixHF := ColorFloatToHalf(PixW);
- case DstInfo.BytesPerPixel of
- 2: PHalfFloat(Dst)^ := PixHF.R;
- 8: PColorHFRec(Dst)^ := PixHF;
- end;
- end;
- end;
- procedure IndexGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo;
- var Index: UInt32);
- begin
- case SrcInfo.BytesPerPixel of
- 1: Index := Src^;
- end;
- end;
- procedure IndexSetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo;
- Index: UInt32);
- begin
- case DstInfo.BytesPerPixel of
- 1: Dst^ := Byte(Index);
- 2: PWord(Dst)^ := Word(Index);
- 4: PUInt32(Dst)^ := Index;
- end;
- end;
- { Pixel readers/writers for 32bit and FP colors}
- function GetPixel32Generic(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColor32Rec;
- var
- Pix64: TColor64Rec;
- PixF: TColorFPRec;
- Alpha: Word;
- Index: UInt32;
- begin
- if Info.Format = ifA8R8G8B8 then
- begin
- Result := PColor32Rec(Bits)^
- end
- else if Info.Format = ifR8G8B8 then
- begin
- PColor24Rec(@Result)^ := PColor24Rec(Bits)^;
- Result.A := $FF;
- end
- else if Info.IsFloatingPoint then
- begin
- FloatGetSrcPixel(Bits, Info, PixF);
- Result.A := ClampToByte(Round(PixF.A * 255.0));
- Result.R := ClampToByte(Round(PixF.R * 255.0));
- Result.G := ClampToByte(Round(PixF.G * 255.0));
- Result.B := ClampToByte(Round(PixF.B * 255.0));
- end
- else if Info.HasGrayChannel then
- begin
- GrayGetSrcPixel(Bits, Info, Pix64, Alpha);
- Result.A := MulDiv(Alpha, 255, 65535);
- Result.R := MulDiv(Pix64.A, 255, 65535);
- Result.G := MulDiv(Pix64.A, 255, 65535);
- Result.B := MulDiv(Pix64.A, 255, 65535);
- end
- else if Info.IsIndexed then
- begin
- IndexGetSrcPixel(Bits, Info, Index);
- Result := Palette[Index];
- end
- else
- begin
- ChannelGetSrcPixel(Bits, Info, Pix64);
- Result.A := MulDiv(Pix64.A, 255, 65535);
- Result.R := MulDiv(Pix64.R, 255, 65535);
- Result.G := MulDiv(Pix64.G, 255, 65535);
- Result.B := MulDiv(Pix64.B, 255, 65535);
- end;
- end;
- procedure SetPixel32Generic(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColor32Rec);
- var
- Pix64: TColor64Rec;
- PixF: TColorFPRec;
- Alpha: Word;
- Index: UInt32;
- begin
- if Info.Format = ifA8R8G8B8 then
- begin
- PColor32Rec(Bits)^ := Color
- end
- else if Info.Format = ifR8G8B8 then
- begin
- PColor24Rec(Bits)^ := Color.Color24Rec;
- end
- else if Info.IsFloatingPoint then
- begin
- PixF.A := Color.A * OneDiv8Bit;
- PixF.R := Color.R * OneDiv8Bit;
- PixF.G := Color.G * OneDiv8Bit;
- PixF.B := Color.B * OneDiv8Bit;
- FloatSetDstPixel(Bits, Info, PixF);
- end
- else if Info.HasGrayChannel then
- begin
- Alpha := MulDiv(Color.A, 65535, 255);
- Pix64.Color := 0;
- Pix64.A := MulDiv(Round(GrayConv.R * Color.R + GrayConv.G * Color.G +
- GrayConv.B * Color.B), 65535, 255);
- GraySetDstPixel(Bits, Info, Pix64, Alpha);
- end
- else if Info.IsIndexed then
- begin
- Index := FindColor(Palette, Info.PaletteEntries, Color.Color);
- IndexSetDstPixel(Bits, Info, Index);
- end
- else
- begin
- Pix64.A := MulDiv(Color.A, 65535, 255);
- Pix64.R := MulDiv(Color.R, 65535, 255);
- Pix64.G := MulDiv(Color.G, 65535, 255);
- Pix64.B := MulDiv(Color.B, 65535, 255);
- ChannelSetDstPixel(Bits, Info, Pix64);
- end;
- end;
- function GetPixelFPGeneric(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec;
- var
- Pix32: TColor32Rec;
- Pix64: TColor64Rec;
- Alpha: Word;
- Index: UInt32;
- begin
- if Info.IsFloatingPoint then
- begin
- FloatGetSrcPixel(Bits, Info, Result);
- end
- else if Info.HasGrayChannel then
- begin
- GrayGetSrcPixel(Bits, Info, Pix64, Alpha);
- Result.A := Alpha * OneDiv16Bit;
- Result.R := Pix64.A * OneDiv16Bit;
- Result.G := Pix64.A * OneDiv16Bit;
- Result.B := Pix64.A * OneDiv16Bit;
- end
- else if Info.IsIndexed then
- begin
- IndexGetSrcPixel(Bits, Info, Index);
- Pix32 := Palette[Index];
- Result.A := Pix32.A * OneDiv8Bit;
- Result.R := Pix32.R * OneDiv8Bit;
- Result.G := Pix32.G * OneDiv8Bit;
- Result.B := Pix32.B * OneDiv8Bit;
- end
- else
- begin
- ChannelGetSrcPixel(Bits, Info, Pix64);
- Result.A := Pix64.A * OneDiv16Bit;
- Result.R := Pix64.R * OneDiv16Bit;
- Result.G := Pix64.G * OneDiv16Bit;
- Result.B := Pix64.B * OneDiv16Bit;
- end;
- end;
- procedure SetPixelFPGeneric(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec);
- var
- Pix32: TColor32Rec;
- Pix64: TColor64Rec;
- Alpha: Word;
- Index: UInt32;
- begin
- if Info.IsFloatingPoint then
- begin
- FloatSetDstPixel(Bits, Info, Color);
- end
- else if Info.HasGrayChannel then
- begin
- Alpha := ClampToWord(Round(Color.A * 65535.0));
- Pix64.Color := 0;
- Pix64.A := ClampToWord(Round((GrayConv.R * Color.R + GrayConv.G * Color.G +
- GrayConv.B * Color.B) * 65535.0));
- GraySetDstPixel(Bits, Info, Pix64, Alpha);
- end
- else if Info.IsIndexed then
- begin
- Pix32.A := ClampToByte(Round(Color.A * 255.0));
- Pix32.R := ClampToByte(Round(Color.R * 255.0));
- Pix32.G := ClampToByte(Round(Color.G * 255.0));
- Pix32.B := ClampToByte(Round(Color.B * 255.0));
- Index := FindColor(Palette, Info.PaletteEntries, Pix32.Color);
- IndexSetDstPixel(Bits, Info, Index);
- end
- else
- begin
- Pix64.A := ClampToWord(Round(Color.A * 65535.0));
- Pix64.R := ClampToWord(Round(Color.R * 65535.0));
- Pix64.G := ClampToWord(Round(Color.G * 65535.0));
- Pix64.B := ClampToWord(Round(Color.B * 65535.0));
- ChannelSetDstPixel(Bits, Info, Pix64);
- end;
- end;
- { Image format conversion functions }
- procedure ChannelToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo);
- var
- I: LongInt;
- Pix64: TColor64Rec;
- begin
- // two most common conversions (RGB->ARGB and ARGB->RGB for 24/32 bit
- // images) are made separately from general ARGB conversion to
- // make them faster
- if (SrcInfo.BytesPerPixel = 3) and (DstInfo.BytesPerPixel = 4) then
- for I := 0 to NumPixels - 1 do
- begin
- PColor24Rec(Dst)^ := PColor24Rec(Src)^;
- if DstInfo.HasAlphaChannel then
- PColor32Rec(Dst).A := 255;
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end
- else
- if (SrcInfo.BytesPerPixel = 4) and (DstInfo.BytesPerPixel = 3) then
- for I := 0 to NumPixels - 1 do
- begin
- PColor24Rec(Dst)^ := PColor24Rec(Src)^;
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end
- else
- for I := 0 to NumPixels - 1 do
- begin
- // general ARGB conversion
- ChannelGetSrcPixel(Src, SrcInfo, Pix64);
- ChannelSetDstPixel(Dst, DstInfo, Pix64);
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end;
- end;
- procedure ChannelToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo);
- var
- I: LongInt;
- Pix64: TColor64Rec;
- Alpha: Word;
- begin
- // two most common conversions (R8G8B8->Gray8 nad A8R8G8B8->Gray8)
- // are made separately from general conversions to make them faster
- if (SrcInfo.BytesPerPixel in [3, 4]) and (DstInfo.Format = ifGray8) then
- for I := 0 to NumPixels - 1 do
- begin
- Dst^ := Round(GrayConv.R * PColor24Rec(Src).R + GrayConv.G * PColor24Rec(Src).G +
- GrayConv.B * PColor24Rec(Src).B);
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end
- else
- for I := 0 to NumPixels - 1 do
- begin
- ChannelGetSrcPixel(Src, SrcInfo, Pix64);
- // alpha is saved from source pixel to Alpha,
- // Gray value is computed and set to highest word of Pix64 so
- // Pix64.Color contains grayscale value scaled to 64 bits
- Alpha := Pix64.A;
- with GrayConv do
- Pix64.A := Round(R * Pix64.R + G * Pix64.G + B * Pix64.B);
- GraySetDstPixel(Dst, DstInfo, Pix64, Alpha);
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end;
- end;
- procedure ChannelToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo);
- var
- I: LongInt;
- Pix64: TColor64Rec;
- PixF: TColorFPRec;
- begin
- for I := 0 to NumPixels - 1 do
- begin
- ChannelGetSrcPixel(Src, SrcInfo, Pix64);
- // floating point channel values are scaled to 1.0
- PixF.A := Pix64.A * OneDiv16Bit;
- PixF.R := Pix64.R * OneDiv16Bit;
- PixF.G := Pix64.G * OneDiv16Bit;
- PixF.B := Pix64.B * OneDiv16Bit;
- FloatSetDstPixel(Dst, DstInfo, PixF);
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end;
- end;
- procedure ChannelToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo; DstPal: PPalette32);
- begin
- ReduceColorsMedianCut(NumPixels, Src, Dst, SrcInfo, DstInfo, DstInfo.PaletteEntries,
- GetOption(ImagingColorReductionMask), DstPal);
- end;
- procedure GrayToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo);
- var
- I: LongInt;
- Gray: TColor64Rec;
- Alpha: Word;
- begin
- // two most common conversions (Gray8->Gray16 nad Gray16->Gray8)
- // are made separately from general conversions to make them faster
- if (SrcInfo.Format = ifGray8) and (DstInfo.Format = ifGray16) then
- begin
- for I := 0 to NumPixels - 1 do
- PWordArray(Dst)[I] := PByteArray(Src)[I] shl 8;
- end
- else
- begin
- if (DstInfo.Format = ifGray8) and (SrcInfo.Format = ifGray16) then
- begin
- for I := 0 to NumPixels - 1 do
- PByteArray(Dst)[I] := PWordArray(Src)[I] shr 8;
- end
- else
- for I := 0 to NumPixels - 1 do
- begin
- // general grayscale conversion
- GrayGetSrcPixel(Src, SrcInfo, Gray, Alpha);
- GraySetDstPixel(Dst, DstInfo, Gray, Alpha);
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end;
- end;
- end;
- procedure GrayToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo);
- var
- I: LongInt;
- Pix64: TColor64Rec;
- Alpha: Word;
- begin
- // two most common conversions (Gray8->R8G8B8 nad Gray8->A8R8G8B8)
- // are made separately from general conversions to make them faster
- if (DstInfo.BytesPerPixel in [3, 4]) and (SrcInfo.Format = ifGray8) then
- for I := 0 to NumPixels - 1 do
- begin
- PColor24Rec(Dst).R := Src^;
- PColor24Rec(Dst).G := Src^;
- PColor24Rec(Dst).B := Src^;
- if DstInfo.HasAlphaChannel then
- PColor32Rec(Dst).A := $FF;
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end
- else
- for I := 0 to NumPixels - 1 do
- begin
- GrayGetSrcPixel(Src, SrcInfo, Pix64, Alpha);
- // most significant word of grayscale value is used for
- // each channel and alpha channel is set to Alpha
- Pix64.R := Pix64.A;
- Pix64.G := Pix64.A;
- Pix64.B := Pix64.A;
- Pix64.A := Alpha;
- ChannelSetDstPixel(Dst, DstInfo, Pix64);
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end;
- end;
- procedure GrayToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo);
- var
- I: LongInt;
- Gray: TColor64Rec;
- PixF: TColorFPRec;
- Alpha: Word;
- begin
- for I := 0 to NumPixels - 1 do
- begin
- GrayGetSrcPixel(Src, SrcInfo, Gray, Alpha);
- // most significant word of grayscale value is used for
- // each channel and alpha channel is set to Alpha
- // then all is scaled to 0..1
- PixF.R := Gray.A * OneDiv16Bit;
- PixF.G := Gray.A * OneDiv16Bit;
- PixF.B := Gray.A * OneDiv16Bit;
- PixF.A := Alpha * OneDiv16Bit;
- FloatSetDstPixel(Dst, DstInfo, PixF);
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end;
- end;
- procedure GrayToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo; DstPal: PPalette32);
- var
- I: LongInt;
- Idx: UInt32;
- Gray: TColor64Rec;
- Alpha, Shift: Word;
- begin
- FillGrayscalePalette(DstPal, DstInfo.PaletteEntries);
- Shift := Log2Int(DstInfo.PaletteEntries);
- // most common conversion (Gray8->Index8)
- // is made separately from general conversions to make it faster
- if (SrcInfo.Format = ifGray8) and (DstInfo.Format = ifIndex8) then
- for I := 0 to NumPixels - 1 do
- begin
- Dst^ := Src^;
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end
- else
- for I := 0 to NumPixels - 1 do
- begin
- // gray value is read from src and index to precomputed
- // grayscale palette is computed and written to dst
- // (we assume here that there will be no more than 65536 palette
- // entries in dst format, gray value is shifted so the highest
- // gray value match the highest possible index in palette)
- GrayGetSrcPixel(Src, SrcInfo, Gray, Alpha);
- Idx := Gray.A shr (16 - Shift);
- IndexSetDstPixel(Dst, DstInfo, Idx);
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end;
- end;
- procedure FloatToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo);
- var
- I: LongInt;
- PixF: TColorFPRec;
- begin
- for I := 0 to NumPixels - 1 do
- begin
- // general floating point conversion
- FloatGetSrcPixel(Src, SrcInfo, PixF);
- FloatSetDstPixel(Dst, DstInfo, PixF);
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end;
- end;
- procedure FloatToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo);
- var
- I: LongInt;
- Pix64: TColor64Rec;
- PixF: TColorFPRec;
- begin
- for I := 0 to NumPixels - 1 do
- begin
- FloatGetSrcPixel(Src, SrcInfo, PixF);
- ClampFloatPixel(PixF);
- // floating point channel values are scaled to 1.0
- Pix64.A := ClampToWord(Round(PixF.A * 65535));
- Pix64.R := ClampToWord(Round(PixF.R * 65535));
- Pix64.G := ClampToWord(Round(PixF.G * 65535));
- Pix64.B := ClampToWord(Round(PixF.B * 65535));
- ChannelSetDstPixel(Dst, DstInfo, Pix64);
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end;
- end;
- procedure FloatToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo);
- var
- I: LongInt;
- PixF: TColorFPRec;
- Gray: TColor64Rec;
- Alpha: Word;
- begin
- for I := 0 to NumPixels - 1 do
- begin
- FloatGetSrcPixel(Src, SrcInfo, PixF);
- ClampFloatPixel(PixF);
- // alpha is saved from source pixel to Alpha,
- // Gray value is computed and set to highest word of Pix64 so
- // Pix64.Color contains grayscale value scaled to 64 bits
- Alpha := ClampToWord(Round(PixF.A * 65535.0));
- Gray.A := ClampToWord(Round((GrayConv.R * PixF.R + GrayConv.G * PixF.G +
- GrayConv.B * PixF.B) * 65535.0));
- GraySetDstPixel(Dst, DstInfo, Gray, Alpha);
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end;
- end;
- procedure FloatToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo; DstPal: PPalette32);
- begin
- ReduceColorsMedianCut(NumPixels, Src, Dst, SrcInfo, DstInfo, DstInfo.PaletteEntries,
- GetOption(ImagingColorReductionMask), DstPal);
- end;
- procedure IndexToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo; SrcPal, DstPal: PPalette32);
- var
- I: LongInt;
- begin
- // there is only one indexed format now, so it is just a copy
- for I := 0 to NumPixels - 1 do
- begin
- Dst^ := Src^;
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end;
- for I := 0 to SrcInfo.PaletteEntries - 1 do
- DstPal[I] := SrcPal[I];
- end;
- procedure IndexToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo; SrcPal: PPalette32);
- var
- I: LongInt;
- Pix64: TColor64Rec;
- Idx: UInt32;
- begin
- // two most common conversions (Index8->R8G8B8 nad Index8->A8R8G8B8)
- // are made separately from general conversions to make them faster
- if (SrcInfo.Format = ifIndex8) and (DstInfo.Format in [ifR8G8B8, ifA8R8G8B8]) then
- for I := 0 to NumPixels - 1 do
- begin
- with PColor24Rec(Dst)^ do
- begin
- R := SrcPal[Src^].R;
- G := SrcPal[Src^].G;
- B := SrcPal[Src^].B;
- end;
- if DstInfo.Format = ifA8R8G8B8 then
- PColor32Rec(Dst).A := SrcPal[Src^].A;
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end
- else
- for I := 0 to NumPixels - 1 do
- begin
- // index to palette is read from source and color
- // is retrieved from palette entry. Color is then
- // scaled to 16bits and written to dest
- IndexGetSrcPixel(Src, SrcInfo, Idx);
- with Pix64 do
- begin
- A := SrcPal[Idx].A shl 8;
- R := SrcPal[Idx].R shl 8;
- G := SrcPal[Idx].G shl 8;
- B := SrcPal[Idx].B shl 8;
- end;
- ChannelSetDstPixel(Dst, DstInfo, Pix64);
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end;
- end;
- procedure IndexToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo; SrcPal: PPalette32);
- var
- I: LongInt;
- Gray: TColor64Rec;
- Alpha: Word;
- Idx: UInt32;
- begin
- // most common conversion (Index8->Gray8)
- // is made separately from general conversions to make it faster
- if (SrcInfo.Format = ifIndex8) and (DstInfo.Format = ifGray8) then
- begin
- for I := 0 to NumPixels - 1 do
- begin
- Dst^ := Round(GrayConv.R * SrcPal[Src^].R + GrayConv.G * SrcPal[Src^].G +
- GrayConv.B * SrcPal[Src^].B);
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end
- end
- else
- for I := 0 to NumPixels - 1 do
- begin
- // index to palette is read from source and color
- // is retrieved from palette entry. Color is then
- // transformed to grayscale and assigned to the highest
- // byte of Gray value
- IndexGetSrcPixel(Src, SrcInfo, Idx);
- Alpha := SrcPal[Idx].A shl 8;
- Gray.A := MulDiv(Round(GrayConv.R * SrcPal[Idx].R + GrayConv.G * SrcPal[Idx].G +
- GrayConv.B * SrcPal[Idx].B), 65535, 255);
- GraySetDstPixel(Dst, DstInfo, Gray, Alpha);
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end;
- end;
- procedure IndexToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
- DstInfo: PImageFormatInfo; SrcPal: PPalette32);
- var
- I: LongInt;
- Idx: UInt32;
- PixF: TColorFPRec;
- begin
- for I := 0 to NumPixels - 1 do
- begin
- // index to palette is read from source and color
- // is retrieved from palette entry. Color is then
- // scaled to 0..1 and written to dest
- IndexGetSrcPixel(Src, SrcInfo, Idx);
- with PixF do
- begin
- A := SrcPal[Idx].A * OneDiv8Bit;
- R := SrcPal[Idx].R * OneDiv8Bit;
- G := SrcPal[Idx].G * OneDiv8Bit;
- B := SrcPal[Idx].B * OneDiv8Bit;
- end;
- FloatSetDstPixel(Dst, DstInfo, PixF);
- Inc(Src, SrcInfo.BytesPerPixel);
- Inc(Dst, DstInfo.BytesPerPixel);
- end;
- end;
- { Special formats conversion functions }
- type
- // DXT RGB color block
- TDXTColorBlock = packed record
- Color0, Color1: Word;
- Mask: UInt32;
- end;
- PDXTColorBlock = ^TDXTColorBlock;
- // DXT explicit alpha for a block
- TDXTAlphaBlockExp = packed record
- Alphas: array[0..3] of Word;
- end;
- PDXTAlphaBlockExp = ^TDXTAlphaBlockExp;
- // DXT interpolated alpha for a block
- TDXTAlphaBlockInt = packed record
- Alphas: array[0..7] of Byte;
- end;
- PDXTAlphaBlockInt = ^TDXTAlphaBlockInt;
- TPixelInfo = record
- Color: Word;
- Alpha: Byte;
- Orig: TColor32Rec;
- end;
- TPixelBlock = array[0..15] of TPixelInfo;
- function DecodeCol(Color: Word): TColor32Rec;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- begin
- Result.A := $FF;
- { Result.R := ((Color and $F800) shr 11) shl 3;
- Result.G := ((Color and $07E0) shr 5) shl 2;
- Result.B := (Color and $001F) shl 3;}
- // this color expansion is slower but gives better results
- Result.R := (Color shr 11) * 255 div 31;
- Result.G := ((Color shr 5) and $3F) * 255 div 63;
- Result.B := (Color and $1F) * 255 div 31;
- end;
- procedure DecodeDXT1(SrcBits, DestBits: PByte; Width, Height: LongInt);
- var
- Sel, X, Y, I, J, K: LongInt;
- Block: TDXTColorBlock;
- Colors: array[0..3] of TColor32Rec;
- begin
- for Y := 0 to Height div 4 - 1 do
- for X := 0 to Width div 4 - 1 do
- begin
- Block := PDXTColorBlock(SrcBits)^;
- Inc(SrcBits, SizeOf(Block));
- // we read and decode endpoint colors
- Colors[0] := DecodeCol(Block.Color0);
- Colors[1] := DecodeCol(Block.Color1);
- // and interpolate between them
- if Block.Color0 > Block.Color1 then
- begin
- // interpolation for block without alpha
- Colors[2].A := $FF;
- Colors[2].R := (Colors[0].R shl 1 + Colors[1].R + 1) div 3;
- Colors[2].G := (Colors[0].G shl 1 + Colors[1].G + 1) div 3;
- Colors[2].B := (Colors[0].B shl 1 + Colors[1].B + 1) div 3;
- Colors[3].A := $FF;
- Colors[3].R := (Colors[0].R + Colors[1].R shl 1 + 1) div 3;
- Colors[3].G := (Colors[0].G + Colors[1].G shl 1 + 1) div 3;
- Colors[3].B := (Colors[0].B + Colors[1].B shl 1 + 1) div 3;
- end
- else
- begin
- // interpolation for block with alpha
- Colors[2].A := $FF;
- Colors[2].R := (Colors[0].R + Colors[1].R) shr 1;
- Colors[2].G := (Colors[0].G + Colors[1].G) shr 1;
- Colors[2].B := (Colors[0].B + Colors[1].B) shr 1;
- Colors[3].A := 0;
- Colors[3].R := (Colors[0].R + Colors[1].R shl 1 + 1) div 3;
- Colors[3].G := (Colors[0].G + Colors[1].G shl 1 + 1) div 3;
- Colors[3].B := (Colors[0].B + Colors[1].B shl 1 + 1) div 3;
- end;
- // we distribute the dxt block colors across the 4x4 block of the
- // destination image according to the dxt block mask
- K := 0;
- for J := 0 to 3 do
- for I := 0 to 3 do
- begin
- Sel := (Block.Mask and (3 shl (K shl 1))) shr (K shl 1);
- if ((X shl 2 + I) < Width) and ((Y shl 2 + J) < Height) then
- PPalette32(DestBits)[(Y shl 2 + J) * Width + X shl 2 + I] :=
- Colors[Sel];
- Inc(K);
- end;
- end;
- end;
- procedure DecodeDXT3(SrcBits, DestBits: PByte; Width, Height: LongInt);
- var
- Sel, X, Y, I, J, K: LongInt;
- Block: TDXTColorBlock;
- AlphaBlock: TDXTAlphaBlockExp;
- Colors: array[0..3] of TColor32Rec;
- AWord: Word;
- begin
- for Y := 0 to Height div 4 - 1 do
- for X := 0 to Width div 4 - 1 do
- begin
- AlphaBlock := PDXTAlphaBlockExp(SrcBits)^;
- Inc(SrcBits, SizeOf(AlphaBlock));
- Block := PDXTColorBlock(SrcBits)^;
- Inc(SrcBits, SizeOf(Block));
- // we read and decode endpoint colors
- Colors[0] := DecodeCol(Block.Color0);
- Colors[1] := DecodeCol(Block.Color1);
- // and interpolate between them
- Colors[2].R := (Colors[0].R shl 1 + Colors[1].R + 1) div 3;
- Colors[2].G := (Colors[0].G shl 1 + Colors[1].G + 1) div 3;
- Colors[2].B := (Colors[0].B shl 1 + Colors[1].B + 1) div 3;
- Colors[3].R := (Colors[0].R + Colors[1].R shl 1 + 1) div 3;
- Colors[3].G := (Colors[0].G + Colors[1].G shl 1 + 1) div 3;
- Colors[3].B := (Colors[0].B + Colors[1].B shl 1 + 1) div 3;
- // we distribute the dxt block colors and alphas
- // across the 4x4 block of the destination image
- // according to the dxt block mask and alpha block
- K := 0;
- for J := 0 to 3 do
- begin
- AWord := AlphaBlock.Alphas[J];
- for I := 0 to 3 do
- begin
- Sel := (Block.Mask and (3 shl (K shl 1))) shr (K shl 1);
- if (X shl 2 + I < Width) and (Y shl 2 + J < Height) then
- begin
- Colors[Sel].A := AWord and $0F;
- Colors[Sel].A := Colors[Sel].A or (Colors[Sel].A shl 4);
- PPalette32(DestBits)[(Y shl 2 + J) * Width + X shl 2 + I] :=
- Colors[Sel];
- end;
- Inc(K);
- AWord := AWord shr 4;
- end;
- end;
- end;
- end;
- procedure GetInterpolatedAlphas(var AlphaBlock: TDXTAlphaBlockInt);
- begin
- with AlphaBlock do
- if Alphas[0] > Alphas[1] then
- begin
- // Interpolation of six alphas
- Alphas[2] := (6 * Alphas[0] + 1 * Alphas[1] + 3) div 7;
- Alphas[3] := (5 * Alphas[0] + 2 * Alphas[1] + 3) div 7;
- Alphas[4] := (4 * Alphas[0] + 3 * Alphas[1] + 3) div 7;
- Alphas[5] := (3 * Alphas[0] + 4 * Alphas[1] + 3) div 7;
- Alphas[6] := (2 * Alphas[0] + 5 * Alphas[1] + 3) div 7;
- Alphas[7] := (1 * Alphas[0] + 6 * Alphas[1] + 3) div 7;
- end
- else
- begin
- // Interpolation of four alphas, two alphas are set directly
- Alphas[2] := (4 * Alphas[0] + 1 * Alphas[1] + 2) div 5;
- Alphas[3] := (3 * Alphas[0] + 2 * Alphas[1] + 2) div 5;
- Alphas[4] := (2 * Alphas[0] + 3 * Alphas[1] + 2) div 5;
- Alphas[5] := (1 * Alphas[0] + 4 * Alphas[1] + 2) div 5;
- Alphas[6] := 0;
- Alphas[7] := $FF;
- end;
- end;
- procedure DecodeDXT5(SrcBits, DestBits: PByte; Width, Height: LongInt);
- var
- Sel, X, Y, I, J, K: LongInt;
- Block: TDXTColorBlock;
- AlphaBlock: TDXTAlphaBlockInt;
- Colors: array[0..3] of TColor32Rec;
- AMask: array[0..1] of UInt32;
- begin
- for Y := 0 to Height div 4 - 1 do
- for X := 0 to Width div 4 - 1 do
- begin
- AlphaBlock := PDXTAlphaBlockInt(SrcBits)^;
- Inc(SrcBits, SizeOf(AlphaBlock));
- Block := PDXTColorBlock(SrcBits)^;
- Inc(SrcBits, SizeOf(Block));
- // we read and decode endpoint colors
- Colors[0] := DecodeCol(Block.Color0);
- Colors[1] := DecodeCol(Block.Color1);
- // and interpolate between them
- Colors[2].R := (Colors[0].R shl 1 + Colors[1].R + 1) div 3;
- Colors[2].G := (Colors[0].G shl 1 + Colors[1].G + 1) div 3;
- Colors[2].B := (Colors[0].B shl 1 + Colors[1].B + 1) div 3;
- Colors[3].R := (Colors[0].R + Colors[1].R shl 1 + 1) div 3;
- Colors[3].G := (Colors[0].G + Colors[1].G shl 1 + 1) div 3;
- Colors[3].B := (Colors[0].B + Colors[1].B shl 1 + 1) div 3;
- // 6 bit alpha mask is copied into two long words for
- // easier usage
- AMask[0] := PUInt32(@AlphaBlock.Alphas[2])^ and $00FFFFFF;
- AMask[1] := PUInt32(@AlphaBlock.Alphas[5])^ and $00FFFFFF;
- // alpha interpolation between two endpoint alphas
- GetInterpolatedAlphas(AlphaBlock);
- // we distribute the dxt block colors and alphas
- // across the 4x4 block of the destination image
- // accroding to the dxt block mask and alpha block mask
- K := 0;
- for J := 0 to 3 do
- for I := 0 to 3 do
- begin
- Sel := (Block.Mask and (3 shl (K shl 1))) shr (K shl 1);
- if ((X shl 2 + I) < Width) and ((Y shl 2 + J) < Height) then
- begin
- Colors[Sel].A := AlphaBlock.Alphas[AMask[J shr 1] and 7];
- PPalette32(DestBits)[(Y shl 2 + J) * Width + (X shl 2 + I)] :=
- Colors[Sel];
- end;
- Inc(K);
- AMask[J shr 1] := AMask[J shr 1] shr 3;
- end;
- end;
- end;
- procedure GetBlock(var Block: TPixelBlock; SrcBits: Pointer; XPos, YPos,
- Width, Height: LongInt);
- var
- X, Y, I: LongInt;
- Src: PColor32Rec;
- begin
- I := 0;
- // 4x4 pixel block is filled with information about every
- // pixel in the block: alpha, original color, 565 color
- for Y := 0 to 3 do
- for X := 0 to 3 do
- begin
- Src := @PPalette32(SrcBits)[(YPos shl 2 + Y) * Width + XPos shl 2 + X];
- Block[I].Color := ((Src.R shr 3) shl 11) or ((Src.G shr 2) shl 5) or
- (Src.B shr 3);
- Block[I].Alpha := Src.A;
- Block[I].Orig := Src^;
- Inc(I);
- end;
- end;
- function ColorDistance(const C1, C2: TColor32Rec): LongInt;
- {$IFDEF USE_INLINE} inline;{$ENDIF}
- begin
- Result := (C1.R - C2.R) * (C1.R - C2.R) +
- (C1.G - C2.G) * (C1.G - C2.G) + (C1.B - C2.B) * (C1.B - C2.B);
- end;
- procedure GetEndpoints(const Block: TPixelBlock; var Ep0, Ep1: Word);
- var
- I, J, Farthest, Dist: LongInt;
- Colors: array[0..15] of TColor32Rec;
- begin
- // we choose two colors from the pixel block which has the
- // largest distance between them
- for I := 0 to 15 do
- Colors[I] := Block[I].Orig;
- Farthest := -1;
- for I := 0 to 15 do
- for J := I + 1 to 15 do
- begin
- Dist := ColorDistance(Colors[I], Colors[J]);
- if Dist > Farthest then
- begin
- Farthest := Dist;
- Ep0 := Block[I].Color;
- Ep1 := Block[J].Color;
- end;
- end;
- end;
- procedure GetAlphaEndpoints(const Block: TPixelBlock; var Min, Max: Byte);
- var
- I: LongInt;
- begin
- Min := 255;
- Max := 0;
- // we choose the lowest and the highest alpha values
- for I := 0 to 15 do
- begin
- if Block[I].Alpha < Min then
- Min := Block[I].Alpha;
- if Block[I].Alpha > Max then
- Max := Block[I].Alpha;
- end;
- end;
- procedure FixEndpoints(var Ep0, Ep1: Word; HasAlpha: Boolean);
- var
- Temp: Word;
- begin
- // if dxt block has alpha information, Ep0 must be smaller
- // than Ep1, if the block has no alpha Ep1 must be smaller
- if HasAlpha then
- begin
- if Ep0 > Ep1 then
- begin
- Temp := Ep0;
- Ep0 := Ep1;
- Ep1 := Temp;
- end;
- end
- else
- if Ep0 < Ep1 then
- begin
- Temp := Ep0;
- Ep0 := Ep1;
- Ep1 := Temp;
- end;
- end;
- function GetColorMask(Ep0, Ep1: Word; NumCols: LongInt;
- const Block: TPixelBlock): UInt32;
- var
- I, J, Closest, Dist: LongInt;
- Colors: array[0..3] of TColor32Rec;
- Mask: array[0..15] of Byte;
- begin
- // we decode endpoint colors
- Colors[0] := DecodeCol(Ep0);
- Colors[1] := DecodeCol(Ep1);
- // and interpolate colors between (3 for DXT1 with alpha, 4 for the others)
- if NumCols = 3 then
- begin
- Colors[2].R := (Colors[0].R + Colors[1].R) shr 1;
- Colors[2].G := (Colors[0].G + Colors[1].G) shr 1;
- Colors[2].B := (Colors[0].B + Colors[1].B) shr 1;
- Colors[3].R := (Colors[0].R + Colors[1].R) shr 1;
- Colors[3].G := (Colors[0].G + Colors[1].G) shr 1;
- Colors[3].B := (Colors[0].B + Colors[1].B) shr 1;
- end
- else
- begin
- Colors[2].R := (Colors[0].R shl 1 + Colors[1].R + 1) div 3;
- Colors[2].G := (Colors[0].G shl 1 + Colors[1].G + 1) div 3;
- Colors[2].B := (Colors[0].B shl 1 + Colors[1].B + 1) div 3;
- Colors[3].R := (Colors[0].R + Colors[1].R shl 1 + 1) div 3;
- Colors[3].G := (Colors[0].G + Colors[1].G shl 1 + 1) div 3;
- Colors[3].B := (Colors[0].B + Colors[1].B shl 1 + 1) div 3;
- end;
- for I := 0 to 15 do
- begin
- // this is only for DXT1 with alpha
- if (Block[I].Alpha < 128) and (NumCols = 3) then
- begin
- Mask[I] := 3;
- Continue;
- end;
- // for each of the 16 input pixels the nearest color in the
- // 4 dxt colors is found
- Closest := MaxInt;
- for J := 0 to NumCols - 1 do
- begin
- Dist := ColorDistance(Block[I].Orig, Colors[J]);
- if Dist < Closest then
- begin
- Closest := Dist;
- Mask[I] := J;
- end;
- end;
- end;
- Result := 0;
- for I := 0 to 15 do
- Result := Result or (Mask[I] shl (I shl 1));
- end;
- procedure GetAlphaMask(Ep0, Ep1: Byte; var Block: TPixelBlock; Mask: PByteArray);
- var
- Alphas: array[0..7] of Byte;
- M: array[0..15] of Byte;
- I, J, Closest, Dist: LongInt;
- begin
- Alphas[0] := Ep0;
- Alphas[1] := Ep1;
- // interpolation between two given alpha endpoints
- // (I use 6 interpolated values mode)
- Alphas[2] := (6 * Alphas[0] + 1 * Alphas[1] + 3) div 7;
- Alphas[3] := (5 * Alphas[0] + 2 * Alphas[1] + 3) div 7;
- Alphas[4] := (4 * Alphas[0] + 3 * Alphas[1] + 3) div 7;
- Alphas[5] := (3 * Alphas[0] + 4 * Alphas[1] + 3) div 7;
- Alphas[6] := (2 * Alphas[0] + 5 * Alphas[1] + 3) div 7;
- Alphas[7] := (1 * Alphas[0] + 6 * Alphas[1] + 3) div 7;
- // the closest interpolated values for each of the input alpha
- // is found
- for I := 0 to 15 do
- begin
- Closest := MaxInt;
- for J := 0 to 7 do
- begin
- Dist := Abs(Alphas[J] - Block[I].Alpha);
- if Dist < Closest then
- begin
- Closest := Dist;
- M[I] := J;
- end;
- end;
- end;
- Mask[0] := M[0] or (M[1] shl 3) or ((M[2] and 3) shl 6);
- Mask[1] := ((M[2] and 4) shr 2) or (M[3] shl 1) or (M[4] shl 4) or
- ((M[5] and 1) shl 7);
- Mask[2] := ((M[5] and 6) shr 1) or (M[6] shl 2) or (M[7] shl 5);
- Mask[3] := M[8] or (M[9] shl 3) or ((M[10] and 3) shl 6);
- Mask[4] := ((M[10] and 4) shr 2) or (M[11] shl 1) or (M[12] shl 4) or
- ((M[13] and 1) shl 7);
- Mask[5] := ((M[13] and 6) shr 1) or (M[14] shl 2) or (M[15] shl 5);
- end;
- procedure EncodeDXT1(SrcBits: PByte; DestBits: PByte; Width, Height: LongInt);
- var
- X, Y, I: LongInt;
- HasAlpha: Boolean;
- Block: TDXTColorBlock;
- Pixels: TPixelBlock;
- begin
- for Y := 0 to Height div 4 - 1 do
- for X := 0 to Width div 4 - 1 do
- begin
- GetBlock(Pixels, SrcBits, X, Y, Width, Height);
- HasAlpha := False;
- for I := 0 to 15 do
- if Pixels[I].Alpha < 128 then
- begin
- HasAlpha := True;
- Break;
- end;
- GetEndpoints(Pixels, Block.Color0, Block.Color1);
- FixEndpoints(Block.Color0, Block.Color1, HasAlpha);
- if HasAlpha then
- Block.Mask := GetColorMask(Block.Color0, Block.Color1, 3, Pixels)
- else
- Block.Mask := GetColorMask(Block.Color0, Block.Color1, 4, Pixels);
- PDXTColorBlock(DestBits)^ := Block;
- Inc(DestBits, SizeOf(Block));
- end;
- end;
- procedure EncodeDXT3(SrcBits: Pointer; DestBits: PByte; Width, Height: LongInt);
- var
- X, Y, I: LongInt;
- Block: TDXTColorBlock;
- AlphaBlock: TDXTAlphaBlockExp;
- Pixels: TPixelBlock;
- begin
- for Y := 0 to Height div 4 - 1 do
- for X := 0 to Width div 4 - 1 do
- begin
- GetBlock(Pixels, SrcBits, X, Y, Width, Height);
- for I := 0 to 7 do
- PByteArray(@AlphaBlock.Alphas)[I] :=
- (Pixels[I shl 1].Alpha shr 4) or ((Pixels[I shl 1 + 1].Alpha shr 4) shl 4);
- GetEndpoints(Pixels, Block.Color0, Block.Color1);
- FixEndpoints(Block.Color0, Block.Color1, False);
- Block.Mask := GetColorMask(Block.Color0, Block.Color1, 4, Pixels);
- PDXTAlphaBlockExp(DestBits)^ := AlphaBlock;
- Inc(DestBits, SizeOf(AlphaBlock));
- PDXTColorBlock(DestBits)^ := Block;
- Inc(DestBits, SizeOf(Block));
- end;
- end;
- procedure EncodeDXT5(SrcBits: Pointer; DestBits: PByte; Width, Height: LongInt);
- var
- X, Y: LongInt;
- Block: TDXTColorBlock;
- AlphaBlock: TDXTAlphaBlockInt;
- Pixels: TPixelBlock;
- begin
- for Y := 0 to Height div 4 - 1 do
- for X := 0 to Width div 4 - 1 do
- begin
- GetBlock(Pixels, SrcBits, X, Y, Width, Height);
- GetEndpoints(Pixels, Block.Color0, Block.Color1);
- FixEndpoints(Block.Color0, Block.Color1, False);
- Block.Mask := GetColorMask(Block.Color0, Block.Color1, 4, Pixels);
- GetAlphaEndPoints(Pixels, AlphaBlock.Alphas[1], AlphaBlock.Alphas[0]);
- GetAlphaMask(AlphaBlock.Alphas[0], AlphaBlock.Alphas[1], Pixels,
- PByteArray(@AlphaBlock.Alphas[2]));
- PDXTAlphaBlockInt(DestBits)^ := AlphaBlock;
- Inc(DestBits, SizeOf(AlphaBlock));
- PDXTColorBlock(DestBits)^ := Block;
- Inc(DestBits, SizeOf(Block));
- end;
- end;
- type
- TBTCBlock = packed record
- MLower, MUpper: Byte;
- BitField: Word;
- end;
- PBTCBlock = ^TBTCBlock;
- procedure EncodeBTC(SrcBits: Pointer; DestBits: PByte; Width, Height: Integer);
- var
- X, Y, I, J: Integer;
- Block: TBTCBlock;
- M, MLower, MUpper, K: Integer;
- Pixels: array[0..15] of Byte;
- begin
- for Y := 0 to Height div 4 - 1 do
- for X := 0 to Width div 4 - 1 do
- begin
- M := 0;
- MLower := 0;
- MUpper := 0;
- FillChar(Block, SizeOf(Block), 0);
- K := 0;
- // Store 4x4 pixels and compute average, lower, and upper intensity levels
- for I := 0 to 3 do
- for J := 0 to 3 do
- begin
- Pixels[K] := PByteArray(SrcBits)[(Y shl 2 + I) * Width + X shl 2 + J];
- Inc(M, Pixels[K]);
- Inc(K);
- end;
- M := M div 16;
- K := 0;
- // Now compute upper and lower levels, number of upper pixels,
- // and update bit field (1 when pixel is above avg. level M)
- for I := 0 to 15 do
- begin
- if Pixels[I] > M then
- begin
- Inc(MUpper, Pixels[I]);
- Inc(K);
- Block.BitField := Block.BitField or (1 shl I);
- end
- else
- Inc(MLower, Pixels[I]);
- end;
- // Scale levels and save them to block
- if K > 0 then
- Block.MUpper := ClampToByte(MUpper div K)
- else
- Block.MUpper := 0;
- Block.MLower := ClampToByte(MLower div (16 - K));
- // Finally save block to dest data
- PBTCBlock(DestBits)^ := Block;
- Inc(DestBits, SizeOf(Block));
- end;
- end;
- procedure GetOneChannelBlock(var Block: TPixelBlock; SrcBits: Pointer; XPos, YPos,
- Width, Height, BytesPP, ChannelIdx: Integer);
- var
- X, Y, I: Integer;
- Src: PByte;
- begin
- I := 0;
- // 4x4 pixel block is filled with information about every pixel in the block,
- // but only one channel value is stored in Alpha field
- for Y := 0 to 3 do
- for X := 0 to 3 do
- begin
- Src := @PByteArray(SrcBits)[(YPos * 4 + Y) * Width * BytesPP +
- (XPos * 4 + X) * BytesPP + ChannelIdx];
- Block[I].Alpha := Src^;
- Inc(I);
- end;
- end;
- procedure EncodeATI1N(SrcBits: Pointer; DestBits: PByte; Width, Height: Integer);
- var
- X, Y: Integer;
- AlphaBlock: TDXTAlphaBlockInt;
- Pixels: TPixelBlock;
- begin
- for Y := 0 to Height div 4 - 1 do
- for X := 0 to Width div 4 - 1 do
- begin
- // Encode one channel
- GetOneChannelBlock(Pixels, SrcBits, X, Y, Width, Height, 1, 0);
- GetAlphaEndPoints(Pixels, AlphaBlock.Alphas[1], AlphaBlock.Alphas[0]);
- GetAlphaMask(AlphaBlock.Alphas[0], AlphaBlock.Alphas[1], Pixels,
- PByteArray(@AlphaBlock.Alphas[2]));
- PDXTAlphaBlockInt(DestBits)^ := AlphaBlock;
- Inc(DestBits, SizeOf(AlphaBlock));
- end;
- end;
- procedure EncodeATI2N(SrcBits: Pointer; DestBits: PByte; Width, Height: Integer);
- var
- X, Y: Integer;
- AlphaBlock: TDXTAlphaBlockInt;
- Pixels: TPixelBlock;
- begin
- for Y := 0 to Height div 4 - 1 do
- for X := 0 to Width div 4 - 1 do
- begin
- // Encode Red/X channel
- GetOneChannelBlock(Pixels, SrcBits, X, Y, Width, Height, 4, ChannelRed);
- GetAlphaEndPoints(Pixels, AlphaBlock.Alphas[1], AlphaBlock.Alphas[0]);
- GetAlphaMask(AlphaBlock.Alphas[0], AlphaBlock.Alphas[1], Pixels,
- PByteArray(@AlphaBlock.Alphas[2]));
- PDXTAlphaBlockInt(DestBits)^ := AlphaBlock;
- Inc(DestBits, SizeOf(AlphaBlock));
- // Encode Green/Y channel
- GetOneChannelBlock(Pixels, SrcBits, X, Y, Width, Height, 4, ChannelGreen);
- GetAlphaEndPoints(Pixels, AlphaBlock.Alphas[1], AlphaBlock.Alphas[0]);
- GetAlphaMask(AlphaBlock.Alphas[0], AlphaBlock.Alphas[1], Pixels,
- PByteArray(@AlphaBlock.Alphas[2]));
- PDXTAlphaBlockInt(DestBits)^ := AlphaBlock;
- Inc(DestBits, SizeOf(AlphaBlock));
- end;
- end;
- procedure EncodeBinary(SrcBits: Pointer; DestBits: PByte; Width, Height: Integer);
- var
- Src: PByte absolute SrcBits;
- Bitmap: PByteArray absolute DestBits;
- X, Y, WidthBytes: Integer;
- PixelThresholded, Threshold: Byte;
- begin
- Threshold := ClampToByte(GetOption(ImagingBinaryThreshold));
- WidthBytes := (Width + 7) div 8;
- for Y := 0 to Height - 1 do
- for X := 0 to Width - 1 do
- begin
- if Src^ > Threshold then
- PixelThresholded := 255
- else
- PixelThresholded := 0;
- Bitmap[Y * WidthBytes + X div 8] := Bitmap[Y * WidthBytes + X div 8] or // OR current value of byte with following:
- (PixelThresholded and 1) // To make 1 from 255, 0 remains 0
- shl (7 - (X mod 8)); // Put current bit to proper place in byte
- Inc(Src);
- end;
- end;
- procedure DecodeBTC(SrcBits, DestBits: PByte; Width, Height: Integer);
- var
- X, Y, I, J, K: Integer;
- Block: TBTCBlock;
- Dest: PByte;
- begin
- for Y := 0 to Height div 4 - 1 do
- for X := 0 to Width div 4 - 1 do
- begin
- Block := PBTCBlock(SrcBits)^;
- Inc(SrcBits, SizeOf(Block));
- K := 0;
- // Just write MUpper when there is '1' in bit field and MLower
- // when there is '0'
- for I := 0 to 3 do
- for J := 0 to 3 do
- begin
- Dest := @PByteArray(DestBits)[(Y shl 2 + I) * Width + X shl 2 + J];
- if Block.BitField and (1 shl K) <> 0 then
- Dest^ := Block.MUpper
- else
- Dest^ := Block.MLower;
- Inc(K);
- end;
- end;
- end;
- procedure DecodeATI1N(SrcBits, DestBits: PByte; Width, Height: Integer);
- var
- X, Y, I, J: Integer;
- AlphaBlock: TDXTAlphaBlockInt;
- AMask: array[0..1] of UInt32;
- begin
- for Y := 0 to Height div 4 - 1 do
- for X := 0 to Width div 4 - 1 do
- begin
- AlphaBlock := PDXTAlphaBlockInt(SrcBits)^;
- Inc(SrcBits, SizeOf(AlphaBlock));
- // 6 bit alpha mask is copied into two long words for
- // easier usage
- AMask[0] := PUInt32(@AlphaBlock.Alphas[2])^ and $00FFFFFF;
- AMask[1] := PUInt32(@AlphaBlock.Alphas[5])^ and $00FFFFFF;
- // alpha interpolation between two endpoint alphas
- GetInterpolatedAlphas(AlphaBlock);
- // we distribute the dxt block alphas
- // across the 4x4 block of the destination image
- for J := 0 to 3 do
- for I := 0 to 3 do
- begin
- PByteArray(DestBits)[(Y shl 2 + J) * Width + (X shl 2 + I)] :=
- AlphaBlock.Alphas[AMask[J shr 1] and 7];
- AMask[J shr 1] := AMask[J shr 1] shr 3;
- end;
- end;
- end;
- procedure DecodeATI2N(SrcBits, DestBits: PByte; Width, Height: Integer);
- var
- X, Y, I, J: Integer;
- Color: TColor32Rec;
- AlphaBlock1, AlphaBlock2: TDXTAlphaBlockInt;
- AMask1: array[0..1] of UInt32;
- AMask2: array[0..1] of UInt32;
- begin
- for Y := 0 to Height div 4 - 1 do
- for X := 0 to Width div 4 - 1 do
- begin
- // Read the first alpha block and get masks
- AlphaBlock1 := PDXTAlphaBlockInt(SrcBits)^;
- Inc(SrcBits, SizeOf(AlphaBlock1));
- AMask1[0] := PUInt32(@AlphaBlock1.Alphas[2])^ and $00FFFFFF;
- AMask1[1] := PUInt32(@AlphaBlock1.Alphas[5])^ and $00FFFFFF;
- // Read the secind alpha block and get masks
- AlphaBlock2 := PDXTAlphaBlockInt(SrcBits)^;
- Inc(SrcBits, SizeOf(AlphaBlock2));
- AMask2[0] := PUInt32(@AlphaBlock2.Alphas[2])^ and $00FFFFFF;
- AMask2[1] := PUInt32(@AlphaBlock2.Alphas[5])^ and $00FFFFFF;
- // alpha interpolation between two endpoint alphas
- GetInterpolatedAlphas(AlphaBlock1);
- GetInterpolatedAlphas(AlphaBlock2);
- Color.A := $FF;
- Color.B := 0;
- // Distribute alpha block values across 4x4 pixel block,
- // first alpha block represents Red channel, second is Green.
- for J := 0 to 3 do
- for I := 0 to 3 do
- begin
- Color.R := AlphaBlock1.Alphas[AMask1[J shr 1] and 7];
- Color.G := AlphaBlock2.Alphas[AMask2[J shr 1] and 7];
- PColor32RecArray(DestBits)[(Y shl 2 + J) * Width + (X shl 2 + I)] := Color;
- AMask1[J shr 1] := AMask1[J shr 1] shr 3;
- AMask2[J shr 1] := AMask2[J shr 1] shr 3;
- end;
- end;
- end;
- procedure DecodeBinary(SrcBits, DestBits: PByte; Width, Height: Integer); {$IFDEF USE_INLINE}inline;{$ENDIF}
- begin
- Convert1To8(SrcBits, DestBits, Width, Height, (Width + 7) div 8, True);
- end;
- procedure SpecialToUnSpecial(const SrcImage: TImageData; DestBits: Pointer;
- SpecialFormat: TImageFormat);
- begin
- case SpecialFormat of
- ifDXT1: DecodeDXT1(SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height);
- ifDXT3: DecodeDXT3(SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height);
- ifDXT5: DecodeDXT5(SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height);
- ifBTC: DecodeBTC (SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height);
- ifATI1N: DecodeATI1N(SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height);
- ifATI2N: DecodeATI2N(SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height);
- ifBinary: DecodeBinary(SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height);
- end;
- end;
- procedure UnSpecialToSpecial(SrcBits: Pointer; const DestImage: TImageData;
- SpecialFormat: TImageFormat);
- begin
- case SpecialFormat of
- ifDXT1: EncodeDXT1(SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height);
- ifDXT3: EncodeDXT3(SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height);
- ifDXT5: EncodeDXT5(SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height);
- ifBTC: EncodeBTC (SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height);
- ifATI1N: EncodeATI1N(SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height);
- ifATI2N: EncodeATI2N(SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height);
- ifBinary: EncodeBinary(SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height);
- end;
- end;
- procedure ConvertSpecial(var Image: TImageData;
- SrcInfo, DstInfo: PImageFormatInfo);
- var
- WorkImage: TImageData;
- procedure CheckSize(var Img: TImageData; Info: PImageFormatInfo);
- var
- Width, Height: LongInt;
- begin
- Width := Img.Width;
- Height := Img.Height;
- DstInfo.CheckDimensions(Info.Format, Width, Height);
- ResizeImage(Img, Width, Height, rfNearest);
- end;
- begin
- if SrcInfo.IsSpecial and DstInfo.IsSpecial then
- begin
- // Convert source to nearest 'normal' format
- InitImage(WorkImage);
- NewImage(Image.Width, Image.Height, SrcInfo.SpecialNearestFormat, WorkImage);
- SpecialToUnSpecial(Image, WorkImage.Bits, SrcInfo.Format);
- FreeImage(Image);
- // Make sure output of SpecialToUnSpecial is the same as input of
- // UnSpecialToSpecial
- if SrcInfo.SpecialNearestFormat <> DstInfo.SpecialNearestFormat then
- ConvertImage(WorkImage, DstInfo.SpecialNearestFormat);
- // Convert work image to dest special format
- CheckSize(WorkImage, DstInfo);
- NewImage(WorkImage.Width, WorkImage.Height, DstInfo.Format, Image);
- UnSpecialToSpecial(WorkImage.Bits, Image, DstInfo.Format);
- FreeImage(WorkImage);
- end
- else if SrcInfo.IsSpecial and not DstInfo.IsSpecial then
- begin
- // Convert source to nearest 'normal' format
- InitImage(WorkImage);
- NewImage(Image.Width, Image.Height, SrcInfo.SpecialNearestFormat, WorkImage);
- SpecialToUnSpecial(Image, WorkImage.Bits, SrcInfo.Format);
- FreeImage(Image);
- // Now convert to dest format
- ConvertImage(WorkImage, DstInfo.Format);
- Image := WorkImage;
- end
- else if not SrcInfo.IsSpecial and DstInfo.IsSpecial then
- begin
- // Convert source to nearest format
- WorkImage := Image;
- ConvertImage(WorkImage, DstInfo.SpecialNearestFormat);
- // Now convert from nearest to dest
- CheckSize(WorkImage, DstInfo);
- InitImage(Image);
- NewImage(WorkImage.Width, WorkImage.Height, DstInfo.Format, Image);
- UnSpecialToSpecial(WorkImage.Bits, Image, DstInfo.Format);
- FreeImage(WorkImage);
- end;
- end;
- function GetStdPixelsSize(Format: TImageFormat; Width, Height: Integer): Int64;
- begin
- if FInfos[Format] <> nil then
- Result := Int64(Width) * Height * FInfos[Format].BytesPerPixel
- else
- Result := 0;
- end;
- procedure CheckStdDimensions(Format: TImageFormat; var Width, Height: Integer);
- begin
- end;
- function GetDXTPixelsSize(Format: TImageFormat; Width, Height: Integer): Int64;
- begin
- // DXT can be used only for images with dimensions that are
- // multiples of four
- CheckDXTDimensions(Format, Width, Height);
- Result := Int64(Width) * Height;
- if Format in [ifDXT1, ifATI1N] then
- Result := Result div 2;
- end;
- procedure CheckDXTDimensions(Format: TImageFormat; var Width, Height: Integer);
- begin
- // DXT image dimensions must be multiples of four
- Width := (Width + 3) and not 3; // div 4 * 4;
- Height := (Height + 3) and not 3; // div 4 * 4;
- end;
- function GetBTCPixelsSize(Format: TImageFormat; Width, Height: Integer): Int64;
- begin
- // BTC can be used only for images with dimensions that are
- // multiples of four
- CheckDXTDimensions(Format, Width, Height);
- Result := Int64(Width) * Height div 4; // 2bits/pixel
- end;
- function GetBCPixelsSize(Format: TImageFormat; Width, Height: Integer): Int64;
- begin
- raise ENotImplemented.Create();
- end;
- procedure CheckBCDimensions(Format: TImageFormat; var Width, Height: Integer);
- begin
- raise ENotImplemented.Create();
- end;
- function GetBinaryPixelsSize(Format: TImageFormat; Width, Height: Integer): Int64;
- begin
- // Binary images are aligned on BYTE boundary
- Result := ((Width + 7) div 8) * Int64(Height); // 1bit/pixel
- end;
- { Optimized pixel readers/writers for 32bit and FP colors to be stored in TImageFormatInfo }
- function GetPixel32ifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColor32Rec;
- begin
- Result.Color := PUInt32(Bits)^;
- end;
- procedure SetPixel32ifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColor32Rec);
- begin
- PUInt32(Bits)^ := Color.Color;
- end;
- function GetPixelFPifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec;
- begin
- Result.A := PColor32Rec(Bits).A * OneDiv8Bit;
- Result.R := PColor32Rec(Bits).R * OneDiv8Bit;
- Result.G := PColor32Rec(Bits).G * OneDiv8Bit;
- Result.B := PColor32Rec(Bits).B * OneDiv8Bit;
- end;
- procedure SetPixelFPifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec);
- begin
- PColor32Rec(Bits).A := ClampToByte(Round(Color.A * 255.0));
- PColor32Rec(Bits).R := ClampToByte(Round(Color.R * 255.0));
- PColor32Rec(Bits).G := ClampToByte(Round(Color.G * 255.0));
- PColor32Rec(Bits).B := ClampToByte(Round(Color.B * 255.0));
- end;
- function GetPixel32Channel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColor32Rec;
- begin
- case Info.Format of
- ifR8G8B8, ifX8R8G8B8:
- begin
- Result.A := $FF;
- PColor24Rec(@Result)^ := PColor24Rec(Bits)^;
- end;
- ifGray8, ifA8Gray8:
- begin
- if Info.HasAlphaChannel then
- Result.A := PWordRec(Bits).High
- else
- Result.A := $FF;
- Result.R := PWordRec(Bits).Low;
- Result.G := PWordRec(Bits).Low;
- Result.B := PWordRec(Bits).Low;
- end;
- end;
- end;
- procedure SetPixel32Channel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColor32Rec);
- begin
- case Info.Format of
- ifR8G8B8, ifX8R8G8B8:
- begin
- PColor24Rec(Bits)^ := PColor24Rec(@Color)^;
- end;
- ifGray8, ifA8Gray8:
- begin
- if Info.HasAlphaChannel then
- PWordRec(Bits).High := Color.A;
- PWordRec(Bits).Low := Round(GrayConv.R * Color.R + GrayConv.G * Color.G +
- GrayConv.B * Color.B);
- end;
- end;
- end;
- function GetPixelFPChannel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec;
- begin
- case Info.Format of
- ifR8G8B8, ifX8R8G8B8:
- begin
- Result.A := 1.0;
- Result.R := PColor24Rec(Bits).R * OneDiv8Bit;
- Result.G := PColor24Rec(Bits).G * OneDiv8Bit;
- Result.B := PColor24Rec(Bits).B * OneDiv8Bit;
- end;
- ifGray8, ifA8Gray8:
- begin
- if Info.HasAlphaChannel then
- Result.A := PWordRec(Bits).High * OneDiv8Bit
- else
- Result.A := 1.0;
- Result.R := PWordRec(Bits).Low * OneDiv8Bit;
- Result.G := PWordRec(Bits).Low * OneDiv8Bit;
- Result.B := PWordRec(Bits).Low * OneDiv8Bit;
- end;
- end;
- end;
- procedure SetPixelFPChannel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec);
- begin
- case Info.Format of
- ifR8G8B8, ifX8R8G8B8:
- begin
- PColor24Rec(Bits).R := ClampToByte(Round(Color.R * 255.0));
- PColor24Rec(Bits).G := ClampToByte(Round(Color.G * 255.0));
- PColor24Rec(Bits).B := ClampToByte(Round(Color.B * 255.0));
- end;
- ifGray8, ifA8Gray8:
- begin
- if Info.HasAlphaChannel then
- PWordRec(Bits).High := ClampToByte(Round(Color.A * 255.0));
- PWordRec(Bits).Low := ClampToByte(Round((GrayConv.R * Color.R + GrayConv.G * Color.G +
- GrayConv.B * Color.B) * 255.0));
- end;
- end;
- end;
- function GetPixelFPFloat32(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec;
- begin
- case Info.Format of
- ifA32R32G32B32F, ifA32B32G32R32F:
- begin
- Result := PColorFPRec(Bits)^;
- end;
- ifR32G32B32F, ifB32G32R32F:
- begin
- Result.A := 1.0;
- Result.Color96Rec := PColor96FPRec(Bits)^;
- end;
- ifR32F:
- begin
- Result.A := 1.0;
- Result.R := PSingle(Bits)^;
- Result.G := 0.0;
- Result.B := 0.0;
- end;
- end;
- if Info.IsRBSwapped then
- SwapValues(Result.R, Result.B);
- end;
- procedure SetPixelFPFloat32(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec);
- begin
- case Info.Format of
- ifA32R32G32B32F, ifA32B32G32R32F:
- begin
- PColorFPRec(Bits)^ := Color;
- end;
- ifR32G32B32F, ifB32G32R32F:
- begin
- PColor96FPRec(Bits)^ := Color.Color96Rec;
- end;
- ifR32F:
- begin
- PSingle(Bits)^ := Color.R;
- end;
- end;
- if Info.IsRBSwapped then
- SwapValues(PColor96FPRec(Bits).R, PColor96FPRec(Bits).B);
- end;
- initialization
- // Initialize default sampling filter function pointers and radii
- SamplingFilterFunctions[sfNearest] := FilterNearest;
- SamplingFilterFunctions[sfLinear] := FilterLinear;
- SamplingFilterFunctions[sfCosine] := FilterCosine;
- SamplingFilterFunctions[sfHermite] := FilterHermite;
- SamplingFilterFunctions[sfQuadratic] := FilterQuadratic;
- SamplingFilterFunctions[sfGaussian] := FilterGaussian;
- SamplingFilterFunctions[sfSpline] := FilterSpline;
- SamplingFilterFunctions[sfLanczos] := FilterLanczos;
- SamplingFilterFunctions[sfMitchell] := FilterMitchell;
- SamplingFilterFunctions[sfCatmullRom] := FilterCatmullRom;
- SamplingFilterRadii[sfNearest] := 1.0;
- SamplingFilterRadii[sfLinear] := 1.0;
- SamplingFilterRadii[sfCosine] := 1.0;
- SamplingFilterRadii[sfHermite] := 1.0;
- SamplingFilterRadii[sfQuadratic] := 1.5;
- SamplingFilterRadii[sfGaussian] := 1.25;
- SamplingFilterRadii[sfSpline] := 2.0;
- SamplingFilterRadii[sfLanczos] := 3.0;
- SamplingFilterRadii[sfMitchell] := 2.0;
- SamplingFilterRadii[sfCatmullRom] := 2.0;
- {
- File Notes:
- -- TODOS ----------------------------------------------------
- - add lookup tables to pixel formats for fast conversions
- -- 0.80 -------------------------------------------------------
- - Added PaletteIsGrayScale and Color32ToGray functions.
- -- 0.77 Changes/Bug Fixes -------------------------------------
- - NOT YET: Added support for Passthrough image data formats.
- - Added ConvertToPixel32 helper function.
- -- 0.26.5 Changes/Bug Fixes -----------------------------------
- - Removed optimized codepath for few data formats from StretchResample
- function. It was quite buggy and not so much faster anyway.
- - Added PaletteHasAlpha function.
- - Added support functions for ifBinary data format.
- - Added optional pixel scaling to Convert1To8, Convert2To8,
- abd Convert4To8 functions.
- -- 0.26.3 Changes/Bug Fixes -----------------------------------
- - Filtered resampling ~10% faster now.
- - Fixed DXT3 alpha encoding.
- - ifIndex8 format now has HasAlphaChannel=True.
- -- 0.25.0 Changes/Bug Fixes -----------------------------------
- - Made some resampling stuff public so that it can be used in canvas class.
- - Added some color constructors.
- - Added VisualizePalette helper function.
- - Fixed ConvertSpecial, not very readable before and error when
- converting special->special.
- -- 0.24.3 Changes/Bug Fixes -----------------------------------
- - Some refactorings a changes to DXT based formats.
- - Added ifATI1N and ifATI2N image data formats support structures and functions.
- -- 0.23 Changes/Bug Fixes -----------------------------------
- - Added ifBTC image format support structures and functions.
- -- 0.21 Changes/Bug Fixes -----------------------------------
- - FillMipMapLevel now works well with indexed and special formats too.
- - Moved Convert1To8 and Convert4To8 functions from ImagingBitmaps here
- and created new Convert2To8 function. They are now used by more than one
- file format loader.
- -- 0.19 Changes/Bug Fixes -----------------------------------
- - StretchResample now uses pixel get/set functions stored in
- TImageFormatInfo so it is much faster for formats that override
- them with optimized ones
- - added pixel set/get functions optimized for various image formats
- (to be stored in TImageFormatInfo)
- - bug in ConvertSpecial caused problems when converting DXTC images
- to bitmaps in ImagingComponents
- - bug in StretchRect caused that it didn't work with ifR32F and
- ifR16F formats
- - removed leftover code in FillMipMapLevel which disabled
- filtered resizing of images witch ChannelSize <> 8bits
- - added half float converting functions and support for half based
- image formats where needed
- - added TranslatePixel and IsImageFormatValid functions
- - fixed possible range overflows when converting from FP to integer images
- - added pixel set/get functions: GetPixel32Generic, GetPixelFPGeneric,
- SetPixel32Generic, SetPixelFPGeneric
- - fixed occasional range overflows in StretchResample
- -- 0.17 Changes/Bug Fixes -----------------------------------
- - added StretchNearest, StretchResample and some sampling functions
- - added ChannelCount values to TImageFormatInfo constants
- - added resolution validity check to GetDXTPixelsSize
- -- 0.15 Changes/Bug Fixes -----------------------------------
- - added RBSwapFormat values to some TImageFormatInfo definitions
- - fixed bug in ConvertSpecial (causing DXT images to convert only to 32bit)
- - added CopyPixel, ComparePixels helper functions
- -- 0.13 Changes/Bug Fixes -----------------------------------
- - replaced pixel format conversions for colors not to be
- darkened when converting from low bit counts
- - ReduceColorsMedianCut was updated to support creating one
- optimal palette for more images and it is somewhat faster
- now too
- - there was ugly bug in DXTC dimensions checking
- }
- end.
|