1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990 |
- unit GR32_Resamplers;
- (* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1 or LGPL 2.1 with linking exception
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * Alternatively, the contents of this file may be used under the terms of the
- * Free Pascal modified version of the GNU Lesser General Public License
- * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
- * of this license are applicable instead of those above.
- * Please see the file LICENSE.txt for additional information concerning this
- * license.
- *
- * The Original Code is Graphics32
- *
- * The Initial Developers of the Original Code is
- * Mattias Andersson <[email protected]>
- * (parts of this unit were taken from GR32_Transforms.pas by Alex A. Denisov)
- *
- * Many of the filters here were adapted from:
- * - "Interpolated Bitmap Resampling using filters"
- * Anders Melander, 1997
- * which in turn was based on:
- * - "General Filtered Image Rescaling"
- * Dale Schumacher
- * Graphics Gems III, Academic Press, Inc.
- * 1 July 1992
- *
- * Portions created by the Initial Developer are Copyright (C) 2000-2009
- * the Initial Developer. All Rights Reserved.
- *
- * ***** END LICENSE BLOCK ***** *)
- interface
- {$include GR32.inc}
- // Define PREMULTIPLY to have TKernelResampler handle alpha correctly.
- // The downside of the alpha handling is that the performance and
- // precision of the resampler suffers slightly.
- {$define PREMULTIPLY}
- uses
- Classes,
- SysUtils, // Exception
- GR32,
- GR32_Transforms,
- GR32_Containers,
- GR32_OrdinalMaps,
- GR32_Blend;
- //------------------------------------------------------------------------------
- //
- // BlockTransfer
- //
- //------------------------------------------------------------------------------
- // Unscaled block transfer
- //------------------------------------------------------------------------------
- procedure BlockTransfer(
- Dst: TCustomBitmap32; DstX: Integer; DstY: Integer; DstClip: TRect;
- Src: TCustomBitmap32; SrcRect: TRect;
- CombineOp: TDrawMode; CombineCallBack: TPixelCombineEvent = nil);
- procedure BlockTransferX(
- Dst: TCustomBitmap32; DstX, DstY: TFixed;
- Src: TCustomBitmap32; SrcRect: TRect;
- CombineOp: TDrawMode; CombineCallBack: TPixelCombineEvent = nil);
- //------------------------------------------------------------------------------
- //
- // StretchTransfer
- //
- //------------------------------------------------------------------------------
- // Scaled block transfer using resampler
- //------------------------------------------------------------------------------
- procedure StretchTransfer(
- Dst: TCustomBitmap32; DstRect: TRect; DstClip: TRect;
- Src: TCustomBitmap32; SrcRect: TRect;
- Resampler: TCustomResampler;
- CombineOp: TDrawMode; CombineCallBack: TPixelCombineEvent = nil);
- //------------------------------------------------------------------------------
- //
- // BlendTransfer
- //
- //------------------------------------------------------------------------------
- // Unscaled block blend
- //------------------------------------------------------------------------------
- procedure BlendTransfer(
- Dst: TCustomBitmap32; DstX, DstY: Integer; DstClip: TRect;
- SrcF: TCustomBitmap32; SrcRectF: TRect;
- SrcB: TCustomBitmap32; SrcRectB: TRect;
- BlendCallback: TBlendReg); overload;
- procedure BlendTransfer(
- Dst: TCustomBitmap32; DstX, DstY: Integer; DstClip: TRect;
- SrcF: TCustomBitmap32; SrcRectF: TRect;
- SrcB: TCustomBitmap32; SrcRectB: TRect;
- BlendCallback: TBlendRegEx; MasterAlpha: Integer); overload;
- //------------------------------------------------------------------------------
- //
- // Resampling
- //
- //------------------------------------------------------------------------------
- const
- MAX_KERNEL_WIDTH = 16;
- type
- PKernelEntry = ^TKernelEntry;
- TKernelEntry = array [-MAX_KERNEL_WIDTH..MAX_KERNEL_WIDTH] of Integer;
- TArrayOfKernelEntry = array of TArrayOfInteger;
- PKernelEntryArray = ^TKernelEntryArray;
- TKernelEntryArray = array [0..0] of TArrayOfInteger;
- TFilterMethod = function(Value: TFloat): TFloat of object;
- EBitmapException = class(Exception);
- ESrcInvalidException = class(Exception);
- ENestedException = class(Exception);
- ETransformerException = class(Exception);
- TGetSampleInt = function(X, Y: Integer): TColor32 of object;
- TGetSampleFloat = function(X, Y: TFloat): TColor32 of object;
- TGetSampleFixed = function(X, Y: TFixed): TColor32 of object;
- //------------------------------------------------------------------------------
- //
- // TCustomKernel
- //
- //------------------------------------------------------------------------------
- // Abstract base class for resampler kernels.
- //------------------------------------------------------------------------------
- type
- TCustomKernel = class(TPersistent)
- protected
- FObserver: TNotifiablePersistent;
- protected
- procedure AssignTo(Dst: TPersistent); override;
- function RangeCheck: Boolean; virtual;
- public
- constructor Create; virtual;
- procedure Changed;
- function Filter(Value: TFloat): TFloat; virtual; abstract;
- function GetWidth: TFloat; virtual; abstract;
- property Observer: TNotifiablePersistent read FObserver;
- end;
- TCustomKernelClass = class of TCustomKernel;
- //------------------------------------------------------------------------------
- //
- // TBoxKernel
- //
- //------------------------------------------------------------------------------
- // Nearest neighbor interpolation filter.
- // Also known as box filter, top-hat function or a Fourier window.
- //------------------------------------------------------------------------------
- type
- TBoxKernel = class(TCustomKernel)
- public
- function Filter(Value: TFloat): TFloat; override;
- function GetWidth: TFloat; override;
- end;
- //------------------------------------------------------------------------------
- //
- // TLinearKernel
- //
- //------------------------------------------------------------------------------
- // Linear reconstruction filter.
- // Also known as triangle filter, tent filter, roof function, Chateau function
- // or a Bartlett window.
- //------------------------------------------------------------------------------
- type
- TLinearKernel = class(TCustomKernel)
- public
- function Filter(Value: TFloat): TFloat; override;
- function GetWidth: TFloat; override;
- end;
- //------------------------------------------------------------------------------
- //
- // TCosineKernel
- //
- //------------------------------------------------------------------------------
- // Cosine reconstruction filter.
- //------------------------------------------------------------------------------
- type
- TCosineKernel = class(TCustomKernel)
- public
- function Filter(Value: TFloat): TFloat; override;
- function GetWidth: TFloat; override;
- end;
- //------------------------------------------------------------------------------
- //
- // TSplineKernel
- //
- //------------------------------------------------------------------------------
- // B-Spline interpolation filter.
- // Not the same as the Spline windowed Sinc kernel.
- //------------------------------------------------------------------------------
- type
- TSplineKernel = class(TCustomKernel)
- protected
- function RangeCheck: Boolean; override;
- public
- function Filter(Value: TFloat): TFloat; override;
- function GetWidth: TFloat; override;
- end;
- //------------------------------------------------------------------------------
- //
- // TMitchellKernel
- //
- //------------------------------------------------------------------------------
- // An implementation of a special case of the cubic filter described by Mitchell
- // and Netravali using the parameters (B: 1/3, C: 1/3).
- //
- // References:
- //
- // - Don P. Mitchell & Arun N. Netravali
- // AT&T Bell Laboratories
- // "Reconstruction Filters in Computer Graphics"
- // Computer Graphics, Volume 22, Number 4, August 1988.
- //
- // Also known as Mitchell-Netravali.
- // Many other variants of this filter, with various other values for B&C, exist.
- // Often people come up with some variation of B&C and then put their own name
- // on the filter. For example Robidoux (B:0.3782, C:0.3109), etc.
- //
- //------------------------------------------------------------------------------
- type
- TMitchellKernel = class(TCustomKernel)
- protected
- function RangeCheck: Boolean; override;
- public
- function Filter(Value: TFloat): TFloat; override;
- function GetWidth: TFloat; override;
- end;
- //------------------------------------------------------------------------------
- //
- // TCubicKernel
- //
- //------------------------------------------------------------------------------
- // A reconstruction filter described by a cubic polynomial.
- //
- // References:
- //
- // - Robert G. Keys
- // "Cubic convolution interpolation for digital image processing"
- // IEEE Transactions on Acoustics, Speech, and Signal Processing
- // Volume: 29, Issue: 6, December 1981
- //
- //------------------------------------------------------------------------------
- type
- TCubicKernel = class(TCustomKernel)
- private
- FCoeff: TFloat;
- procedure SetCoeff(const Value: TFloat);
- protected
- function RangeCheck: Boolean; override;
- public
- constructor Create; override;
- function Filter(Value: TFloat): TFloat; override;
- function GetWidth: TFloat; override;
- published
- property Coeff: TFloat read FCoeff write SetCoeff;
- end;
- //------------------------------------------------------------------------------
- //
- // THermiteKernel
- //
- //------------------------------------------------------------------------------
- // An implementation of the hermite kernel.
- //------------------------------------------------------------------------------
- type
- THermiteKernel = class(TCustomKernel)
- private
- FBias: TFloat;
- FTension: TFloat;
- procedure SetBias(const Value: TFloat);
- procedure SetTension(const Value: TFloat);
- protected
- function RangeCheck: Boolean; override;
- public
- constructor Create; override;
- function Filter(Value: TFloat): TFloat; override;
- function GetWidth: TFloat; override;
- published
- property Bias: TFloat read FBias write SetBias;
- property Tension: TFloat read FTension write SetTension;
- end;
- //------------------------------------------------------------------------------
- //
- // TSinshKernel
- //
- //------------------------------------------------------------------------------
- // A filter described by a hyperbolic sine, something, something.
- //------------------------------------------------------------------------------
- type
- TSinshKernel = class(TCustomKernel)
- private
- FWidth: TFloat;
- FCoeff: TFloat;
- procedure SetCoeff(const Value: TFloat);
- protected
- function RangeCheck: Boolean; override;
- public
- constructor Create; override;
- procedure SetWidth(Value: TFloat);
- function GetWidth: TFloat; override;
- function Filter(Value: TFloat): TFloat; override;
- published
- property Coeff: TFloat read FCoeff write SetCoeff;
- property Width: TFloat read GetWidth write SetWidth;
- end;
- //------------------------------------------------------------------------------
- //
- // TWindowedKernel
- //
- //------------------------------------------------------------------------------
- // Abstract base class for windowed kernels.
- // Returns the value of the filter function constrained by a window function.
- // Descendant classes must override the Window method in order to implement a
- // custom window function.
- //------------------------------------------------------------------------------
- type
- TWindowedKernel = class(TCustomKernel)
- strict protected
- FWidth : TFloat;
- FWidthReciprocal : TFloat;
- protected
- function RangeCheck: Boolean; override;
- function Window(Value: TFloat): TFloat; virtual; abstract;
- procedure DoSetWidth(Value: TFloat);
- public
- function Filter(Value: TFloat): TFloat; override;
- procedure SetWidth(Value: TFloat);
- function GetWidth: TFloat; override;
- property WidthReciprocal : TFloat read FWidthReciprocal;
- published
- property Width: TFloat read FWidth write SetWidth;
- end;
- //------------------------------------------------------------------------------
- //
- // TGaussianKernel
- //
- //------------------------------------------------------------------------------
- // A kernel constrained by a Gaussian window function.
- //------------------------------------------------------------------------------
- type
- TGaussianKernel = class(TWindowedKernel)
- private
- FSigma: TFloat;
- FSigmaReciprocal: TFloat;
- FNormalizationFactor: Single;
- procedure DoSetSigma(const Value: TFloat);
- procedure SetSigma(const Value: TFloat);
- protected
- function Window(Value: TFloat): TFloat; override;
- public
- constructor Create; override;
- published
- property Sigma: TFloat read FSigma write SetSigma;
- end;
- //------------------------------------------------------------------------------
- //
- // TWindowedSincKernel
- //
- //------------------------------------------------------------------------------
- // Abstract base class for windowed Sinc kernels.
- // Returns the value of the Sinc function constrained by a window function.
- // Descendant classes must override the Window method in order to implement a
- // custom window function.
- //------------------------------------------------------------------------------
- type
- TWindowedSincKernel = class(TWindowedKernel)
- protected
- class function Sinc(Value: TFloat): TFloat; static;
- public
- constructor Create; override;
- function Filter(Value: TFloat): TFloat; override;
- published
- property Width: TFloat read FWidth write SetWidth;
- end;
- //------------------------------------------------------------------------------
- //
- // TAlbrecht-Kernel
- //
- //------------------------------------------------------------------------------
- // A Sinc kernel constrained by Albrecht window functions.
- //
- // References:
- //
- // - Hans-Helge Albrecht
- // Physikalisch-Technische Bundesanstalt, Berlin, Germany
- // "A family of cosine-sum windows for high resolution measurements"
- // IEEE International Conference on Acoustics, Speech, and Signal Processing,
- // Salt Lake City, May 2001.
- //
- //------------------------------------------------------------------------------
- type
- TAlbrechtKernel = class(TWindowedSincKernel)
- private
- FTerms: Integer;
- FCoefPointer : Array [0..11] of Double;
- procedure SetTerms(Value : Integer);
- protected
- function Window(Value: TFloat): TFloat; override;
- public
- constructor Create; override;
- published
- property Terms: Integer read FTerms write SetTerms;
- end;
- //------------------------------------------------------------------------------
- //
- // TLanczosKernel
- //
- //------------------------------------------------------------------------------
- // A Sinc kernel constrained by a Lanczos window function.
- // It uses three lobes of the Sinc filter as a window.
- //
- // References:
- //
- // - Claude E. Duchon
- // School of Meteorology, University of Oklahoma, USA
- // "Lanczos Filtering in One and Two Dimensions"
- // Journal of Applied Meteorology and Climatology, volume 18, pp. 1016-1022
- // 1 Aug 1979
- //
- // Also known as Lanczo3.
- //------------------------------------------------------------------------------
- type
- TLanczosKernel = class(TWindowedSincKernel)
- protected
- function Window(Value: TFloat): TFloat; override;
- end;
- //------------------------------------------------------------------------------
- //
- // TBlackmanKernel
- //
- //------------------------------------------------------------------------------
- // A Sinc kernel constrained by a Blackman window function.
- //
- // References:
- //
- // - Ralph Beebe Blackman & John Wilder Tukey
- // "Particular Pairs of Windows"
- // The measurement of power spectra from the point of view of communications
- // engineering.
- // New York: Dover, pp. 98-99, 1959.
- //
- //------------------------------------------------------------------------------
- type
- TBlackmanKernel = class(TWindowedSincKernel)
- protected
- function Window(Value: TFloat): TFloat; override;
- end;
- //------------------------------------------------------------------------------
- //
- // THannKernel
- //
- //------------------------------------------------------------------------------
- // A Sinc kernel constrained by a Hann window function.
- // Also known as raised cosine.
- //
- // References:
- //
- // - Ralph Beebe Blackman & John Wilder Tukey
- // The measurement of power spectra from the point of view of communications
- // engineering — Part I.
- // The Bell System Technical Journal. 37 (1), pp. 273, 1958.
- //
- // Supposedly based on work done by Julius von Hann, 1839-1921
- //
- //------------------------------------------------------------------------------
- type
- THannKernel = class(TWindowedSincKernel)
- protected
- function Window(Value: TFloat): TFloat; override;
- end;
- //------------------------------------------------------------------------------
- //
- // THammingKernel
- //
- //------------------------------------------------------------------------------
- // A Sinc kernel constrained by a Hamming window function.
- //
- // References:
- //
- // - Richard W. Hamming
- // "Digital Filters"
- // Prentice-Hall, 1977 pp. 226; 2nd ed. 1983; 3rd ed. 1989
- //
- //------------------------------------------------------------------------------
- type
- THammingKernel = class(TWindowedSincKernel)
- protected
- function Window(Value: TFloat): TFloat; override;
- end;
- //------------------------------------------------------------------------------
- //
- // TNearestResampler
- //
- //------------------------------------------------------------------------------
- // A fast resampler based on the nearest-neighbor interpolation algorithm.
- //------------------------------------------------------------------------------
- type
- TNearestResampler = class(TCustomResampler)
- private
- FGetSampleInt: TGetSampleInt;
- protected
- function GetPixelTransparentEdge(X, Y: Integer): TColor32;
- function GetWidth: TFloat; override;
- procedure Resample(
- Dst: TCustomBitmap32; DstRect: TRect; DstClip: TRect;
- Src: TCustomBitmap32; SrcRect: TRect;
- CombineOp: TDrawMode; CombineCallBack: TPixelCombineEvent); override;
- public
- function GetSampleInt(X, Y: Integer): TColor32; override;
- function GetSampleFixed(X, Y: TFixed): TColor32; override;
- function GetSampleFloat(X, Y: TFloat): TColor32; override;
- procedure PrepareSampling; override;
- end;
- //------------------------------------------------------------------------------
- //
- // TLinearResampler
- //
- //------------------------------------------------------------------------------
- // Performance-optimized linear upsampler.
- // Falls back to using TLinearKernel for downsampling.
- //------------------------------------------------------------------------------
- type
- TLinearResampler = class(TCustomResampler)
- private
- FLinearKernel: TLinearKernel;
- FGetSampleFixed: TGetSampleFixed;
- protected
- function GetWidth: TFloat; override;
- function GetPixelTransparentEdge(X, Y: TFixed): TColor32;
- procedure Resample(
- Dst: TCustomBitmap32; DstRect: TRect; DstClip: TRect;
- Src: TCustomBitmap32; SrcRect: TRect;
- CombineOp: TDrawMode; CombineCallBack: TPixelCombineEvent); override;
- public
- constructor Create; override;
- destructor Destroy; override;
- function GetSampleFixed(X, Y: TFixed): TColor32; override;
- function GetSampleFloat(X, Y: TFloat): TColor32; override;
- procedure PrepareSampling; override;
- end;
- //------------------------------------------------------------------------------
- //
- // TDraftResampler
- //
- //------------------------------------------------------------------------------
- // Performance-optimized downsampler.
- // Falls back to using TLinearResampler for upsampling.
- //------------------------------------------------------------------------------
- type
- TDraftResampler = class(TLinearResampler)
- protected
- procedure Resample(
- Dst: TCustomBitmap32; DstRect: TRect; DstClip: TRect;
- Src: TCustomBitmap32; SrcRect: TRect;
- CombineOp: TDrawMode; CombineCallBack: TPixelCombineEvent); override;
- end;
- //------------------------------------------------------------------------------
- //
- // TKernelResampler
- //
- //------------------------------------------------------------------------------
- // This resampler class will perform resampling by using an arbitrary
- // reconstruction kernel.
- // By using the kmTableNearest and kmTableLinear kernel modes, kernel values are
- // precomputed in a look-up table. This allows GetSample to execute faster for
- // complex kernels.
- //------------------------------------------------------------------------------
- type
- TKernelMode = (kmDynamic, kmTableNearest, kmTableLinear);
- TKernelResampler = class(TCustomResampler)
- private
- FKernel: TCustomKernel;
- FKernelMode: TKernelMode;
- FWeightTable: TIntegerMap;
- FTableSize: Integer;
- FOuterColor: TColor32;
- procedure SetKernel(const Value: TCustomKernel);
- function GetKernelClassName: string;
- procedure SetKernelClassName(const Value: string);
- procedure SetKernelMode(const Value: TKernelMode);
- procedure SetTableSize(Value: Integer);
- protected
- function GetWidth: TFloat; override;
- public
- constructor Create; override;
- destructor Destroy; override;
- function GetSampleFloat(X, Y: TFloat): TColor32; override;
- procedure Resample(
- Dst: TCustomBitmap32; DstRect: TRect; DstClip: TRect;
- Src: TCustomBitmap32; SrcRect: TRect;
- CombineOp: TDrawMode; CombineCallBack: TPixelCombineEvent); override;
- procedure PrepareSampling; override;
- procedure FinalizeSampling; override;
- published
- property KernelClassName: string read GetKernelClassName write SetKernelClassName;
- property Kernel: TCustomKernel read FKernel write SetKernel;
- property KernelMode: TKernelMode read FKernelMode write SetKernelMode;
- property TableSize: Integer read FTableSize write SetTableSize;
- end;
- //------------------------------------------------------------------------------
- //
- // TNestedSampler
- //
- //------------------------------------------------------------------------------
- // TNestedSampler is a base class for chained or nested samplers.
- //------------------------------------------------------------------------------
- type
- TNestedSampler = class(TCustomSampler)
- private
- FSampler: TCustomSampler;
- FGetSampleInt: TGetSampleInt;
- FGetSampleFixed: TGetSampleFixed;
- FGetSampleFloat: TGetSampleFloat;
- procedure SetSampler(const Value: TCustomSampler);
- protected
- procedure AssignTo(Dst: TPersistent); override;
- public
- constructor Create(ASampler: TCustomSampler); reintroduce; virtual;
- procedure PrepareSampling; override;
- procedure FinalizeSampling; override;
- function HasBounds: Boolean; override;
- function GetSampleBounds: TFloatRect; override;
- published
- property Sampler: TCustomSampler read FSampler write SetSampler;
- end;
- //------------------------------------------------------------------------------
- //
- // TTransformer
- //
- //------------------------------------------------------------------------------
- // TTransformer is a nested sampler that will transform the sampling coordinates
- // using a transformation defined by a TTransformation descendant.
- //------------------------------------------------------------------------------
- type
- TTransformInt = procedure(DstX, DstY: Integer; out SrcX, SrcY: Integer) of object;
- TTransformFixed = procedure(DstX, DstY: TFixed; out SrcX, SrcY: TFixed) of object;
- TTransformFloat = procedure(DstX, DstY: TFloat; out SrcX, SrcY: TFloat) of object;
- TTransformer = class(TNestedSampler)
- private
- FTransformation: TTransformation;
- FTransformInt: TTransformInt; // Unused
- FTransformFixed: TTransformFixed;
- FTransformFloat: TTransformFloat;
- FReverse: boolean;
- public
- constructor Create(ASampler: TCustomSampler; ATransformation: TTransformation; AReverse: boolean = True); reintroduce;
- procedure PrepareSampling; override;
- function GetSampleInt(X, Y: Integer): TColor32; override;
- function GetSampleFixed(X, Y: TFixed): TColor32; override;
- function GetSampleFloat(X, Y: TFloat): TColor32; override;
- function HasBounds: Boolean; override;
- function GetSampleBounds: TFloatRect; override;
- published
- property Transformation: TTransformation read FTransformation write FTransformation;
- property ReverseTransform: boolean read FReverse write FReverse;
- end;
- //------------------------------------------------------------------------------
- //
- // TSuperSampler
- //
- //------------------------------------------------------------------------------
- // TSuperSampler is a nested sampler that adds a mechanism for performing super
- // sampling.
- //------------------------------------------------------------------------------
- type
- TSamplingRange = 1..MaxInt;
- TSuperSampler = class(TNestedSampler)
- private
- FSamplingY: TSamplingRange;
- FSamplingX: TSamplingRange;
- FDistanceX: TFixed;
- FDistanceY: TFixed;
- FOffsetX: TFixed;
- FOffsetY: TFixed;
- FScale: TFixed;
- procedure SetSamplingX(const Value: TSamplingRange);
- procedure SetSamplingY(const Value: TSamplingRange);
- public
- constructor Create(Sampler: TCustomSampler); override;
- function GetSampleFixed(X, Y: TFixed): TColor32; override;
- published
- property SamplingX: TSamplingRange read FSamplingX write SetSamplingX;
- property SamplingY: TSamplingRange read FSamplingY write SetSamplingY;
- end;
- //------------------------------------------------------------------------------
- //
- // TAdaptiveSuperSampler
- //
- //------------------------------------------------------------------------------
- // Adaptive supersampling is different from ordinary supersampling in the sense
- // that samples are choosen adaptively; It is a recursive method that collects
- // more samples at areas with rapid transitions.
- //------------------------------------------------------------------------------
- type
- TRecurseProc = function(X, Y, W: TFixed; const C1, C2: TColor32): TColor32 of object;
- TAdaptiveSuperSampler = class(TNestedSampler)
- private
- FMinOffset: TFixed;
- FLevel: Integer;
- FTolerance: Integer;
- procedure SetLevel(const Value: Integer);
- function DoRecurse(X, Y, Offset: TFixed; const A, B, C, D, E: TColor32): TColor32;
- function QuadrantColor(const C1, C2: TColor32; X, Y, Offset: TFixed;
- Proc: TRecurseProc): TColor32;
- function RecurseAC(X, Y, Offset: TFixed; const A, C: TColor32): TColor32;
- function RecurseBD(X, Y, Offset: TFixed; const B, D: TColor32): TColor32;
- protected
- function CompareColors(C1, C2: TColor32): Boolean; virtual;
- public
- constructor Create(Sampler: TCustomSampler); override;
- function GetSampleFixed(X, Y: TFixed): TColor32; override;
- published
- property Level: Integer read FLevel write SetLevel;
- property Tolerance: Integer read FTolerance write FTolerance;
- end;
- //------------------------------------------------------------------------------
- //
- // TPatternSampler
- //
- //------------------------------------------------------------------------------
- // TPatternSampler provides a mechanism for performing sampling according to a
- // supplied sample pattern.
- //------------------------------------------------------------------------------
- type
- TFloatSamplePattern = array of array of TArrayOfFloatPoint;
- TFixedSamplePattern = array of array of TArrayOfFixedPoint;
- TPatternSampler = class(TNestedSampler)
- private
- FPattern: TFixedSamplePattern;
- procedure SetPattern(const Value: TFixedSamplePattern);
- protected
- WrapProcVert: TWrapProc;
- public
- destructor Destroy; override;
- function GetSampleFixed(X, Y: TFixed): TColor32; override;
- property Pattern: TFixedSamplePattern read FPattern write SetPattern;
- end;
- { Auxiliary record used in accumulation routines }
- PBufferEntry = ^TBufferEntry;
- TBufferEntry = record
- B, G, R, A: Integer;
- end;
- //------------------------------------------------------------------------------
- //
- // TKernelSampler
- //
- //------------------------------------------------------------------------------
- // TKernelSampler is an abstract base class for samplers that compute an output
- // sample by collecting a number of samples in a local region of the actual
- // sample coordinate.
- //------------------------------------------------------------------------------
- type
- TKernelSampler = class(TNestedSampler)
- private
- FKernel: TIntegerMap;
- FStartEntry: TBufferEntry;
- FCenterX: Integer;
- FCenterY: Integer;
- protected
- procedure SetKernel(const Value: TIntegerMap);
- procedure UpdateBuffer(var Buffer: TBufferEntry; Color: TColor32;
- Weight: Integer); virtual; abstract;
- function ConvertBuffer(var Buffer: TBufferEntry): TColor32; virtual;
- public
- constructor Create(ASampler: TCustomSampler); override;
- destructor Destroy; override;
- function GetSampleInt(X, Y: Integer): TColor32; override;
- function GetSampleFixed(X, Y: TFixed): TColor32; override;
- published
- property Kernel: TIntegerMap read FKernel write SetKernel;
- property CenterX: Integer read FCenterX write FCenterX;
- property CenterY: Integer read FCenterY write FCenterY;
- end;
- //------------------------------------------------------------------------------
- //
- // TConvolver
- //
- //------------------------------------------------------------------------------
- // The TConvolver kernel sampler provides functionality for performing discrete
- // convolution within a chain of nested samplers.
- //------------------------------------------------------------------------------
- type
- TConvolver = class(TKernelSampler)
- protected
- procedure UpdateBuffer(var Buffer: TBufferEntry; Color: TColor32;
- Weight: Integer); override;
- end;
- //------------------------------------------------------------------------------
- //
- // TSelectiveConvolver
- //
- //------------------------------------------------------------------------------
- // TSelectiveConvolver works similarly to TConvolver, but it will exclude color
- // samples from the convolution depending on a the difference from a local
- // reference sample value.
- //------------------------------------------------------------------------------
- type
- TSelectiveConvolver = class(TConvolver)
- private
- FRefColor: TColor32;
- FDelta: Integer;
- FWeightSum: TBufferEntry;
- protected
- procedure UpdateBuffer(var Buffer: TBufferEntry; Color: TColor32;
- Weight: Integer); override;
- function ConvertBuffer(var Buffer: TBufferEntry): TColor32; override;
- public
- constructor Create(ASampler: TCustomSampler); override;
- function GetSampleInt(X, Y: Integer): TColor32; override;
- function GetSampleFixed(X, Y: TFixed): TColor32; override;
- published
- property Delta: Integer read FDelta write FDelta;
- end;
- //------------------------------------------------------------------------------
- //
- // TMorphologicalSampler
- //
- //------------------------------------------------------------------------------
- // Abstract base class for TDilater and TEroder.
- //------------------------------------------------------------------------------
- type
- TMorphologicalSampler = class(TKernelSampler)
- protected
- function ConvertBuffer(var Buffer: TBufferEntry): TColor32; override;
- end;
- //------------------------------------------------------------------------------
- //
- // TDilater
- //
- //------------------------------------------------------------------------------
- // TDilater is a nested sampler for performing morphological dilation.
- //------------------------------------------------------------------------------
- type
- TDilater = class(TMorphologicalSampler)
- protected
- procedure UpdateBuffer(var Buffer: TBufferEntry; Color: TColor32;
- Weight: Integer); override;
- end;
- //------------------------------------------------------------------------------
- //
- // TEroder
- //
- //------------------------------------------------------------------------------
- // TEroder is a nested sampler for performing morphological erosion
- //------------------------------------------------------------------------------
- type
- TEroder = class(TMorphologicalSampler)
- protected
- procedure UpdateBuffer(var Buffer: TBufferEntry; Color: TColor32;
- Weight: Integer); override;
- public
- constructor Create(ASampler: TCustomSampler); override;
- end;
- //------------------------------------------------------------------------------
- //
- // TExpander
- //
- //------------------------------------------------------------------------------
- // TExpander implements a neighborhood operation similar to morphological
- // dilation.
- //------------------------------------------------------------------------------
- type
- TExpander = class(TKernelSampler)
- protected
- procedure UpdateBuffer(var Buffer: TBufferEntry; Color: TColor32;
- Weight: Integer); override;
- end;
- //------------------------------------------------------------------------------
- //
- // TContracter
- //
- //------------------------------------------------------------------------------
- // Similar to TExpander, but contracts instead of exanding.
- //------------------------------------------------------------------------------
- type
- TContracter = class(TExpander)
- private
- FMaxWeight: TColor32;
- protected
- procedure UpdateBuffer(var Buffer: TBufferEntry; Color: TColor32;
- Weight: Integer); override;
- public
- procedure PrepareSampling; override;
- function GetSampleInt(X, Y: Integer): TColor32; override;
- function GetSampleFixed(X, Y: TFixed): TColor32; override;
- end;
- //------------------------------------------------------------------------------
- //
- // CreateJitteredPattern
- //
- //------------------------------------------------------------------------------
- // Create a random jitter pattern for use with TPatternSampler.
- //------------------------------------------------------------------------------
- function CreateJitteredPattern(TileWidth, TileHeight, SamplesX, SamplesY: Integer): TFixedSamplePattern;
- //------------------------------------------------------------------------------
- //
- // Convolution and morphological routines
- //
- //------------------------------------------------------------------------------
- // Kernel sampler wrapper functions.
- //------------------------------------------------------------------------------
- procedure Convolve(Src, Dst: TCustomBitmap32; Kernel: TIntegerMap; CenterX, CenterY: Integer);
- procedure Dilate(Src, Dst: TCustomBitmap32; Kernel: TIntegerMap; CenterX, CenterY: Integer);
- procedure Erode(Src, Dst: TCustomBitmap32; Kernel: TIntegerMap; CenterX, CenterY: Integer);
- procedure Expand(Src, Dst: TCustomBitmap32; Kernel: TIntegerMap; CenterX, CenterY: Integer);
- procedure Contract(Src, Dst: TCustomBitmap32; Kernel: TIntegerMap; CenterX, CenterY: Integer);
- //------------------------------------------------------------------------------
- //
- // Auxiliary routines for accumulating colors in a buffer
- //
- //------------------------------------------------------------------------------
- procedure IncBuffer(var Buffer: TBufferEntry; Color: TColor32); {$IFDEF USEINLINING} inline; {$ENDIF}
- procedure MultiplyBuffer(var Buffer: TBufferEntry; W: Integer); {$IFDEF USEINLINING} inline; {$ENDIF}
- function BufferToColor32(const Buffer: TBufferEntry; Shift: Integer): TColor32; {$IFDEF USEINLINING} inline; {$ENDIF}
- procedure ShrBuffer(var Buffer: TBufferEntry; Shift: Integer); {$IFDEF USEINLINING} inline; {$ENDIF}
- //------------------------------------------------------------------------------
- //
- // Downsample byte map
- //
- //------------------------------------------------------------------------------
- procedure DownsampleByteMap2x(Source, Dest: TByteMap);
- procedure DownsampleByteMap3x(Source, Dest: TByteMap);
- procedure DownsampleByteMap4x(Source, Dest: TByteMap);
- //------------------------------------------------------------------------------
- //
- // Registration routines
- //
- //------------------------------------------------------------------------------
- procedure RegisterResampler(ResamplerClass: TCustomResamplerClass);
- procedure RegisterKernel(KernelClass: TCustomKernelClass);
- var
- KernelList: TCustomClassList<TCustomKernelClass>;
- ResamplerList: TCustomClassList<TCustomResamplerClass>;
- const
- EMPTY_ENTRY: TBufferEntry = (B: 0; G: 0; R: 0; A: 0) deprecated 'Use Default(TBufferEntry)';
- //------------------------------------------------------------------------------
- //
- // Bindings
- //
- //------------------------------------------------------------------------------
- var
- BlockAverage: function(Dlx, Dly: Cardinal; RowSrc: PColor32; OffSrc: Cardinal): TColor32;
- Interpolator: function(WX_256, WY_256: Cardinal; C11, C21: PColor32): TColor32;
- //------------------------------------------------------------------------------
- resourcestring
- SDstNil = 'Destination bitmap is nil';
- SSrcNil = 'Source bitmap is nil';
- SSrcInvalid = 'Source rectangle is invalid';
- SSamplerNil = 'Nested sampler is nil';
- STransformationNil = 'Transformation is nil';
- //------------------------------------------------------------------------------
- //------------------------------------------------------------------------------
- //------------------------------------------------------------------------------
- implementation
- uses
- Math,
- Types,
- GR32_Bindings,
- GR32_LowLevel,
- GR32_Rasterizers,
- GR32_Math,
- GR32_Gamma;
- resourcestring
- RCStrInvalidSrcRect = 'Invalid SrcRect';
- const
- CAlbrecht2 : array [0..1] of Double = (5.383553946707251E-1,
- 4.616446053292749E-1);
- CAlbrecht3 : array [0..2] of Double = (3.46100822018625E-1,
- 4.97340635096738E-1, 1.56558542884637E-1);
- CAlbrecht4 : array [0..3] of Double = (2.26982412792069E-1,
- 4.57254070828427E-1, 2.73199027957384E-1, 4.25644884221201E-2);
- CAlbrecht5 : array [0..4] of Double = (1.48942606015830E-1,
- 3.86001173639176E-1, 3.40977403214053E-1, 1.139879604246E-1,
- 1.00908567063414E-2);
- CAlbrecht6 : array [0..5] of Double = (9.71676200107429E-2,
- 3.08845222524055E-1, 3.62623371437917E-1, 1.88953325525116E-1,
- 4.02095714148751E-2, 2.20088908729420E-3);
- CAlbrecht7 : array [0..6] of Double = (6.39644241143904E-2,
- 2.39938645993528E-1, 3.50159563238205E-1, 2.47741118970808E-1,
- 8.54382560558580E-2, 1.23202033692932E-2, 4.37788257917735E-4);
- CAlbrecht8 : array [0..7] of Double = (4.21072107042137E-2,
- 1.82076226633776E-1, 3.17713781059942E-1, 2.84438001373442E-1,
- 1.36762237777383E-1, 3.34038053504025E-2, 3.41677216705768E-3,
- 8.19649337831348E-5);
- CAlbrecht9 : array [0..8] of Double = (2.76143731612611E-2,
- 1.35382228758844E-1, 2.75287234472237E-1, 2.98843335317801E-1,
- 1.85319330279284E-1, 6.48884482549063E-2, 1.17641910285655E-2,
- 8.85987580106899E-4, 1.48711469943406E-5);
- CAlbrecht10: array [0..9] of Double = (1.79908225352538E-2,
- 9.87959586065210E-2, 2.29883817001211E-1, 2.94113019095183E-1,
- 2.24338977814325E-1, 1.03248806248099E-1, 2.75674109448523E-2,
- 3.83958622947123E-3, 2.18971708430106E-4, 2.62981665347889E-6);
- CAlbrecht11: array [0..10] of Double = (1.18717127796602E-2,
- 7.19533651951142E-2, 1.87887160922585E-1, 2.75808174097291E-1,
- 2.48904243244464E-1, 1.41729867200712E-1, 5.02002976228256E-2,
- 1.04589649084984E-2, 1.13615112741660E-3, 4.96285981703436E-5,
- 4.34303262685720E-7);
- type
- TTransformationAccess = class(TTransformation);
- TCustomBitmap32Access = class(TCustomBitmap32);
- TCustomResamplerAccess = class(TCustomResampler);
- TCustomKernelAccess = class(TCustomKernel);
- TPointRec = record
- Pos: Integer;
- Weight: Integer;
- end;
- TCluster = array of TPointRec;
- TMappingTable = array of TCluster;
- TKernelSamplerClass = class of TKernelSampler;
- { Auxiliary rasterization routine for kernel-based samplers }
- procedure RasterizeKernelSampler(Src, Dst: TCustomBitmap32; Kernel: TIntegerMap;
- CenterX, CenterY: Integer; SamplerClass: TKernelSamplerClass);
- var
- Sampler: TKernelSampler;
- Rasterizer: TRasterizer;
- begin
- Rasterizer := DefaultRasterizerClass.Create;
- try
- Dst.SetSizeFrom(Src);
- Sampler := SamplerClass.Create(Src.Resampler);
- try
- Sampler.Kernel := Kernel;
- Sampler.CenterX := CenterX;
- Sampler.CenterY := CenterY;
- Rasterizer.Sampler := Sampler;
- Rasterizer.Rasterize(Dst);
- finally
- Sampler.Free;
- end;
- finally
- Rasterizer.Free;
- end;
- end;
- procedure Convolve(Src, Dst: TCustomBitmap32; Kernel: TIntegerMap; CenterX, CenterY: Integer);
- begin
- RasterizeKernelSampler(Src, Dst, Kernel, CenterX, CenterY, TConvolver);
- end;
- procedure Dilate(Src, Dst: TCustomBitmap32; Kernel: TIntegerMap; CenterX, CenterY: Integer);
- begin
- RasterizeKernelSampler(Src, Dst, Kernel, CenterX, CenterY, TDilater);
- end;
- procedure Erode(Src, Dst: TCustomBitmap32; Kernel: TIntegerMap; CenterX, CenterY: Integer);
- begin
- RasterizeKernelSampler(Src, Dst, Kernel, CenterX, CenterY, TEroder);
- end;
- procedure Expand(Src, Dst: TCustomBitmap32; Kernel: TIntegerMap; CenterX, CenterY: Integer);
- begin
- RasterizeKernelSampler(Src, Dst, Kernel, CenterX, CenterY, TExpander);
- end;
- procedure Contract(Src, Dst: TCustomBitmap32; Kernel: TIntegerMap; CenterX, CenterY: Integer);
- begin
- RasterizeKernelSampler(Src, Dst, Kernel, CenterX, CenterY, TContracter);
- end;
- { Auxiliary routines }
- procedure IncBuffer(var Buffer: TBufferEntry; Color: TColor32);
- begin
- with TColor32Entry(Color) do
- begin
- Inc(Buffer.B, B);
- Inc(Buffer.G, G);
- Inc(Buffer.R, R);
- Inc(Buffer.A, A);
- end;
- end;
- procedure MultiplyBuffer(var Buffer: TBufferEntry; W: Integer);
- begin
- Buffer.B := Buffer.B * W;
- Buffer.G := Buffer.G * W;
- Buffer.R := Buffer.R * W;
- Buffer.A := Buffer.A * W;
- end;
- procedure ShrBuffer(var Buffer: TBufferEntry; Shift: Integer);
- begin
- Buffer.B := Buffer.B shr Shift;
- Buffer.G := Buffer.G shr Shift;
- Buffer.R := Buffer.R shr Shift;
- Buffer.A := Buffer.A shr Shift;
- end;
- function BufferToColor32(const Buffer: TBufferEntry; Shift: Integer): TColor32;
- begin
- with TColor32Entry(Result) do
- begin
- B := Buffer.B shr Shift;
- G := Buffer.G shr Shift;
- R := Buffer.R shr Shift;
- A := Buffer.A shr Shift;
- end;
- end;
- procedure CheckBitmaps(Dst, Src: TCustomBitmap32); {$IFDEF USEINLINING}inline;{$ENDIF}
- begin
- if not Assigned(Dst) then raise EBitmapException.Create(SDstNil);
- if not Assigned(Src) then raise EBitmapException.Create(SSrcNil);
- end;
- procedure BlendBlock(
- Dst: TCustomBitmap32; DstRect: TRect;
- Src: TCustomBitmap32; SrcX, SrcY: Integer;
- CombineOp: TDrawMode; CombineCallBack: TPixelCombineEvent);
- var
- SrcP, DstP: PColor32;
- SP, DP: PColor32;
- MC: TColor32;
- W, I, DstY: Integer;
- BlendLine: TBlendLine;
- BlendLineEx: TBlendLineEx;
- begin
- { Internal routine }
- W := DstRect.Right - DstRect.Left;
- SrcP := Src.PixelPtr[SrcX, SrcY];
- DstP := Dst.PixelPtr[DstRect.Left, DstRect.Top];
- case CombineOp of
- dmOpaque:
- begin
- for DstY := DstRect.Top to DstRect.Bottom - 1 do
- begin
- //Move(SrcP^, DstP^, W shl 2); // for FastCode
- MoveLongWord(SrcP^, DstP^, W);
- Inc(SrcP, Src.Width);
- Inc(DstP, Dst.Width);
- end;
- end;
- dmBlend:
- if Src.MasterAlpha >= 255 then
- begin
- BlendLine := BLEND_LINE[Src.CombineMode]^;
- for DstY := DstRect.Top to DstRect.Bottom - 1 do
- begin
- BlendLine(SrcP, DstP, W);
- Inc(SrcP, Src.Width);
- Inc(DstP, Dst.Width);
- end
- end
- else
- begin
- BlendLineEx := BLEND_LINE_EX[Src.CombineMode]^;
- for DstY := DstRect.Top to DstRect.Bottom - 1 do
- begin
- BlendLineEx(SrcP, DstP, W, Src.MasterAlpha);
- Inc(SrcP, Src.Width);
- Inc(DstP, Dst.Width);
- end
- end;
- dmTransparent:
- begin
- MC := Src.OuterColor;
- for DstY := DstRect.Top to DstRect.Bottom - 1 do
- begin
- SP := SrcP;
- DP := DstP;
- { TODO: Write an optimized routine for fast masked transfers. }
- for I := 0 to W - 1 do
- begin
- if MC <> SP^ then DP^ := SP^;
- Inc(SP); Inc(DP);
- end;
- Inc(SrcP, Src.Width);
- Inc(DstP, Dst.Width);
- end;
- end;
- else // dmCustom:
- begin
- for DstY := DstRect.Top to DstRect.Bottom - 1 do
- begin
- SP := SrcP;
- DP := DstP;
- for I := 0 to W - 1 do
- begin
- CombineCallBack(SP^, DP^, Src.MasterAlpha);
- Inc(SP); Inc(DP);
- end;
- Inc(SrcP, Src.Width);
- Inc(DstP, Dst.Width);
- end;
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- //
- // BlockTransfer
- //
- //------------------------------------------------------------------------------
- procedure BlockTransfer(
- Dst: TCustomBitmap32; DstX: Integer; DstY: Integer; DstClip: TRect;
- Src: TCustomBitmap32; SrcRect: TRect;
- CombineOp: TDrawMode; CombineCallBack: TPixelCombineEvent);
- var
- SrcX, SrcY: Integer;
- begin
- CheckBitmaps(Dst, Src);
- if Dst.Empty or Src.Empty or ((CombineOp = dmBlend) and (Src.MasterAlpha = 0)) then Exit;
- SrcX := SrcRect.Left;
- SrcY := SrcRect.Top;
- GR32.IntersectRect(DstClip, DstClip, Dst.BoundsRect);
- GR32.IntersectRect(SrcRect, SrcRect, Src.BoundsRect);
- GR32.OffsetRect(SrcRect, DstX - SrcX, DstY - SrcY);
- GR32.IntersectRect(SrcRect, DstClip, SrcRect);
- if GR32.IsRectEmpty(SrcRect) then
- exit;
- DstClip := SrcRect;
- GR32.OffsetRect(SrcRect, SrcX - DstX, SrcY - DstY);
- if not Dst.MeasuringMode then
- begin
- if (CombineOp = dmCustom) and not Assigned(CombineCallBack) then
- CombineOp := dmOpaque;
- BlendBlock(Dst, DstClip, Src, SrcRect.Left, SrcRect.Top, CombineOp, CombineCallBack);
- end;
- Dst.Changed(DstClip);
- end;
- //------------------------------------------------------------------------------
- {$WARNINGS OFF}
- procedure BlockTransferX(
- Dst: TCustomBitmap32; DstX, DstY: TFixed;
- Src: TCustomBitmap32; SrcRect: TRect;
- CombineOp: TDrawMode; CombineCallBack: TPixelCombineEvent = nil);
- type
- TColor32Array = array [0..1] of TColor32;
- PColor32Array = ^TColor32Array;
- var
- I, Index, SrcW, SrcRectW, SrcRectH, DstW, DstH: Integer;
- FracX, FracY: Integer;
- Buffer: array [0..1] of TArrayOfColor32;
- SrcP, Buf1, Buf2: PColor32Array;
- DstP: PColor32;
- C1, C2, C3, C4: TColor32;
- LW, RW, TW, BW, MA: Integer;
- DstBounds: TRect;
- BlendLineEx: TBlendLineEx;
- BlendMemEx: TBlendMemEx;
- begin
- CheckBitmaps(Dst, Src);
- if Dst.Empty or Src.Empty or ((CombineOp = dmBlend) and (Src.MasterAlpha = 0)) then Exit;
- SrcRectW := SrcRect.Right - SrcRect.Left - 1;
- SrcRectH := SrcRect.Bottom - SrcRect.Top - 1;
- FracX := (DstX and $FFFF) shr 8;
- FracY := (DstY and $FFFF) shr 8;
- DstX := DstX div $10000;
- DstY := DstY div $10000;
- DstW := Dst.Width;
- DstH := Dst.Height;
- MA := Src.MasterAlpha;
- if (DstX >= DstW) or (DstY >= DstH) or (MA = 0) then Exit;
- if (DstX + SrcRectW <= 0) or (Dsty + SrcRectH <= 0) then Exit;
- if DstX < 0 then LW := $FF else LW := FracX xor $FF;
- if DstY < 0 then TW := $FF else TW := FracY xor $FF;
- if DstX + SrcRectW >= DstW then RW := $FF else RW := FracX;
- if DstY + SrcRectH >= DstH then BW := $FF else BW := FracY;
- DstBounds := Dst.BoundsRect;
- Dec(DstBounds.Right);
- Dec(DstBounds.Bottom);
- GR32.OffsetRect(DstBounds, SrcRect.Left - DstX, SrcRect.Top - DstY);
- GR32.IntersectRect(SrcRect, SrcRect, DstBounds);
- if GR32.IsRectEmpty(SrcRect) then Exit;
- SrcW := Src.Width;
- SrcRectW := SrcRect.Right - SrcRect.Left;
- SrcRectH := SrcRect.Bottom - SrcRect.Top;
- if DstX < 0 then DstX := 0;
- if DstY < 0 then DstY := 0;
- if not Dst.MeasuringMode then
- begin
- SetLength(Buffer[0], SrcRectW + 1);
- SetLength(Buffer[1], SrcRectW + 1);
- BlendLineEx := BLEND_LINE_EX[Src.CombineMode]^;
- BlendMemEx := BLEND_MEM_EX[Src.CombineMode]^;
- try
- SrcP := PColor32Array(Src.PixelPtr[SrcRect.Left, SrcRect.Top - 1]);
- DstP := Dst.PixelPtr[DstX, DstY];
- Buf1 := @Buffer[0][0];
- Buf2 := @Buffer[1][0];
- if SrcRect.Top > 0 then
- begin
- MoveLongWord(SrcP[0], Buf1[0], SrcRectW);
- CombineLine(@Buf1[1], @Buf1[0], SrcRectW, FracX);
- if SrcRect.Left > 0 then
- C2 := CombineReg(PColor32(NativeUInt(SrcP) - 4)^, SrcP[0], FracX xor $FF)
- else
- C2 := SrcP[0];
- if SrcRect.Right < SrcW then
- C4 := CombineReg(SrcP[SrcRectW - 1], SrcP[SrcRectW], FracX)
- else
- C4 := SrcP[SrcRectW - 1];
- end;
- Inc(PColor32(SrcP), SrcW);
- MoveLongWord(SrcP^, Buf2^, SrcRectW);
- CombineLine(@Buf2[1], @Buf2[0], SrcRectW, FracX xor $FF);
- if SrcRect.Left > 0 then
- C1 := CombineReg(PColor32(NativeUInt(SrcP) - 4)^, SrcP[0], FracX)
- else
- C1 := SrcP[0];
- if SrcRect.Right < SrcW then
- C3 := CombineReg(SrcP[SrcRectW - 1], SrcP[SrcRectW], FracX)
- else
- C3 := SrcP[SrcRectW - 1];
- if SrcRect.Top > 0 then
- begin
- BlendMemEx(CombineReg(C1, C2, FracY), DstP^, LW * TW * MA shr 16);
- CombineLine(@Buf2[0], @Buf1[0], SrcRectW, FracY xor $FF);
- end
- else
- begin
- BlendMemEx(C1, DstP^, LW * TW * MA shr 16);
- MoveLongWord(Buf2^, Buf1^, SrcRectW);
- end;
- Inc(DstP, 1);
- BlendLineEx(@Buf1[0], DstP, SrcRectW - 1, TW * MA shr 8);
- Inc(DstP, SrcRectW - 1);
- if SrcRect.Top > 0 then
- BlendMemEx(CombineReg(C3, C4, FracY), DstP^, RW * TW * MA shr 16)
- else
- BlendMemEx(C3, DstP^, RW * TW * MA shr 16);
- Inc(DstP, DstW - SrcRectW);
- Index := 1;
- for I := SrcRect.Top to SrcRect.Bottom - 2 do
- begin
- Buf1 := @Buffer[Index][0];
- Buf2 := @Buffer[Index xor 1][0];
- Inc(PColor32(SrcP), SrcW);
- MoveLongWord(SrcP[0], Buf2^, SrcRectW);
- // Horizontal translation
- CombineLine(@Buf2[1], @Buf2[0], SrcRectW, FracX xor $FF);
- if SrcRect.Left > 0 then
- C2 := CombineReg(PColor32(NativeUInt(SrcP) - 4)^, SrcP[0], FracX xor $FF)
- else
- C2 := SrcP[0];
- BlendMemEx(CombineReg(C1, C2, FracY), DstP^, LW * MA shr 8);
- Inc(DstP);
- C1 := C2;
- // Vertical translation
- CombineLine(@Buf2[0], @Buf1[0], SrcRectW, FracY xor $FF);
- // Blend horizontal line to Dst
- BlendLineEx(@Buf1[0], DstP, SrcRectW - 1, MA);
- Inc(DstP, SrcRectW - 1);
- if SrcRect.Right < SrcW then
- C4 := CombineReg(SrcP[SrcRectW - 1], SrcP[SrcRectW], FracX)
- else
- C4 := SrcP[SrcRectW - 1];
- BlendMemEx(CombineReg(C3, C4, FracY), DstP^, RW * MA shr 8);
- Inc(DstP, DstW - SrcRectW);
- C3 := C4;
- Index := Index xor 1;
- end;
- Buf1 := @Buffer[Index][0];
- Buf2 := @Buffer[Index xor 1][0];
- Inc(PColor32(SrcP), SrcW);
- if SrcRect.Bottom < Src.Height then
- begin
- MoveLongWord(SrcP[0], Buf2^, SrcRectW);
- CombineLine(@Buf2[1], @Buf2[0], SrcRectW, FracY xor $FF);
- CombineLine(@Buf2[0], @Buf1[0], SrcRectW, FracY xor $FF);
- if SrcRect.Left > 0 then
- C2 := CombineReg(PColor32(NativeUInt(SrcP) - 4)^, SrcP[0], FracX xor $FF)
- else
- C2 := SrcP[0];
- BlendMemEx(CombineReg(C1, C2, FracY), DstP^, LW * BW * MA shr 16)
- end
- else
- BlendMemEx(C1, DstP^, LW * BW * MA shr 16);
- Inc(DstP);
- BlendLineEx(@Buf1[0], DstP, SrcRectW - 1, BW * MA shr 8);
- Inc(DstP, SrcRectW - 1);
- if SrcRect.Bottom < Src.Height then
- begin
- if SrcRect.Right < SrcW then
- C4 := CombineReg(SrcP[SrcRectW - 1], SrcP[SrcRectW], FracX)
- else
- C4 := SrcP[SrcRectW - 1];
- BlendMemEx(CombineReg(C3, C4, FracY), DstP^, RW * BW * MA shr 16);
- end
- else
- BlendMemEx(C3, DstP^, RW * BW * MA shr 16);
- finally
- Buffer[0] := nil;
- Buffer[1] := nil;
- end;
- end;
- Dst.Changed(MakeRect(DstX, DstY, DstX + SrcRectW + 1, DstY + SrcRectH + 1));
- end;
- {$WARNINGS ON}
- //------------------------------------------------------------------------------
- //
- // BlendTransfer
- //
- //------------------------------------------------------------------------------
- procedure BlendTransfer(
- Dst: TCustomBitmap32; DstX, DstY: Integer; DstClip: TRect;
- SrcF: TCustomBitmap32; SrcRectF: TRect;
- SrcB: TCustomBitmap32; SrcRectB: TRect;
- BlendCallback: TBlendReg);
- var
- I, J, SrcFX, SrcFY, SrcBX, SrcBY: Integer;
- PSrcF, PSrcB, PDst: PColor32Array;
- begin
- if not Assigned(Dst) then raise EBitmapException.Create(SDstNil);
- if not Assigned(SrcF) then raise EBitmapException.Create(SSrcNil);
- if not Assigned(SrcB) then raise EBitmapException.Create(SSrcNil);
- if Dst.Empty or SrcF.Empty or SrcB.Empty or not Assigned(BlendCallback) then Exit;
- if not Dst.MeasuringMode then
- begin
- SrcFX := SrcRectF.Left - DstX;
- SrcFY := SrcRectF.Top - DstY;
- SrcBX := SrcRectB.Left - DstX;
- SrcBY := SrcRectB.Top - DstY;
- GR32.IntersectRect(DstClip, DstClip, Dst.BoundsRect);
- GR32.IntersectRect(SrcRectF, SrcRectF, SrcF.BoundsRect);
- GR32.IntersectRect(SrcRectB, SrcRectB, SrcB.BoundsRect);
- GR32.OffsetRect(SrcRectF, -SrcFX, -SrcFY);
- GR32.OffsetRect(SrcRectB, -SrcBX, -SrcBY);
- GR32.IntersectRect(DstClip, DstClip, SrcRectF);
- GR32.IntersectRect(DstClip, DstClip, SrcRectB);
- if not GR32.IsRectEmpty(DstClip) then
- for I := DstClip.Top to DstClip.Bottom - 1 do
- begin
- PSrcF := PColor32Array(SrcF.PixelPtr[SrcFX, SrcFY + I]);
- PSrcB := PColor32Array(SrcB.PixelPtr[SrcBX, SrcBY + I]);
- PDst := Dst.ScanLine[I];
- for J := DstClip.Left to DstClip.Right - 1 do
- PDst[J] := BlendCallback(PSrcF[J], PSrcB[J]);
- end;
- end;
- Dst.Changed(DstClip);
- end;
- //------------------------------------------------------------------------------
- procedure BlendTransfer(
- Dst: TCustomBitmap32; DstX, DstY: Integer; DstClip: TRect;
- SrcF: TCustomBitmap32; SrcRectF: TRect;
- SrcB: TCustomBitmap32; SrcRectB: TRect;
- BlendCallback: TBlendRegEx; MasterAlpha: Integer);
- var
- I, J, SrcFX, SrcFY, SrcBX, SrcBY: Integer;
- PSrcF, PSrcB, PDst: PColor32Array;
- begin
- if not Assigned(Dst) then raise EBitmapException.Create(SDstNil);
- if not Assigned(SrcF) then raise EBitmapException.Create(SSrcNil);
- if not Assigned(SrcB) then raise EBitmapException.Create(SSrcNil);
- if Dst.Empty or SrcF.Empty or SrcB.Empty or not Assigned(BlendCallback) then Exit;
- if not Dst.MeasuringMode then
- begin
- SrcFX := SrcRectF.Left - DstX;
- SrcFY := SrcRectF.Top - DstY;
- SrcBX := SrcRectB.Left - DstX;
- SrcBY := SrcRectB.Top - DstY;
- GR32.IntersectRect(DstClip, DstClip, Dst.BoundsRect);
- GR32.IntersectRect(SrcRectF, SrcRectF, SrcF.BoundsRect);
- GR32.IntersectRect(SrcRectB, SrcRectB, SrcB.BoundsRect);
- GR32.OffsetRect(SrcRectF, -SrcFX, -SrcFY);
- GR32.OffsetRect(SrcRectB, -SrcBX, -SrcBY);
- GR32.IntersectRect(DstClip, DstClip, SrcRectF);
- GR32.IntersectRect(DstClip, DstClip, SrcRectB);
- if not GR32.IsRectEmpty(DstClip) then
- for I := DstClip.Top to DstClip.Bottom - 1 do
- begin
- PSrcF := PColor32Array(SrcF.PixelPtr[SrcFX, SrcFY + I]);
- PSrcB := PColor32Array(SrcB.PixelPtr[SrcBX, SrcBY + I]);
- PDst := Dst.ScanLine[I];
- for J := DstClip.Left to DstClip.Right - 1 do
- PDst[J] := BlendCallback(PSrcF[J], PSrcB[J], MasterAlpha);
- end;
- end;
- Dst.Changed(DstClip);
- end;
- //------------------------------------------------------------------------------
- //
- // StretchNearest
- //
- //------------------------------------------------------------------------------
- // Used by TNearestResampler.Resample
- //------------------------------------------------------------------------------
- procedure StretchNearest(
- Dst: TCustomBitmap32; DstRect, DstClip: TRect;
- Src: TCustomBitmap32; SrcRect: TRect;
- CombineOp: TDrawMode; CombineCallBack: TPixelCombineEvent);
- var
- R: TRect;
- SrcW, SrcH, DstW, DstH, DstClipW, DstClipH: Integer;
- SrcY, OldSrcY: Integer;
- I, J: Integer;
- MapHorz: PIntegerArray;
- SrcLine, DstLine: PColor32Array;
- Buffer: TArrayOfColor32;
- Scale: TFloat;
- BlendLine: TBlendLine;
- BlendLineEx: TBlendLineEx;
- DstLinePtr, MapPtr: PColor32;
- begin
- GR32.IntersectRect(DstClip, DstClip, MakeRect(0, 0, Dst.Width, Dst.Height));
- GR32.IntersectRect(DstClip, DstClip, DstRect);
- if GR32.IsRectEmpty(DstClip) then Exit;
- GR32.IntersectRect(R, DstClip, DstRect);
- if GR32.IsRectEmpty(R) then Exit;
- if (SrcRect.Left < 0) or (SrcRect.Top < 0) or (SrcRect.Right > Src.Width) or
- (SrcRect.Bottom > Src.Height) then
- raise Exception.Create(RCStrInvalidSrcRect);
- SrcW := SrcRect.Right - SrcRect.Left;
- SrcH := SrcRect.Bottom - SrcRect.Top;
- DstW := DstRect.Right - DstRect.Left;
- DstH := DstRect.Bottom - DstRect.Top;
- DstClipW := DstClip.Right - DstClip.Left;
- DstClipH := DstClip.Bottom - DstClip.Top;
- if (SrcW = DstW) and (SrcH = DstH) then
- begin
- { Copy without resampling }
- BlendBlock(Dst, DstClip, Src, SrcRect.Left + DstClip.Left - DstRect.Left,
- SrcRect.Top + DstClip.Top - DstRect.Top, CombineOp, CombineCallBack);
- end
- else
- begin
- GetMem(MapHorz, DstClipW * SizeOf(Integer));
- try
- if DstW > 1 then
- begin
- if FullEdge then
- begin
- Scale := SrcW / DstW;
- for I := 0 to DstClipW - 1 do
- MapHorz^[I] := Trunc(SrcRect.Left + (I + DstClip.Left - DstRect.Left) * Scale);
- end
- else
- begin
- Scale := (SrcW - 1) / (DstW - 1);
- for I := 0 to DstClipW - 1 do
- MapHorz^[I] := Round(SrcRect.Left + (I + DstClip.Left - DstRect.Left) * Scale);
- end;
-
- Assert(MapHorz^[0] >= SrcRect.Left);
- Assert(MapHorz^[DstClipW - 1] < SrcRect.Right);
- end
- else
- MapHorz^[0] := (SrcRect.Left + SrcRect.Right - 1) div 2;
- if DstH <= 1 then Scale := 0
- else if FullEdge then Scale := SrcH / DstH
- else Scale := (SrcH - 1) / (DstH - 1);
- if CombineOp = dmOpaque then
- begin
- DstLine := PColor32Array(Dst.PixelPtr[DstClip.Left, DstClip.Top]);
- OldSrcY := -1;
-
- for J := 0 to DstClipH - 1 do
- begin
- if DstH <= 1 then
- SrcY := (SrcRect.Top + SrcRect.Bottom - 1) div 2
- else if FullEdge then
- SrcY := Trunc(SrcRect.Top + (J + DstClip.Top - DstRect.Top) * Scale)
- else
- SrcY := Round(SrcRect.Top + (J + DstClip.Top - DstRect.Top) * Scale);
-
- if SrcY <> OldSrcY then
- begin
- SrcLine := Src.ScanLine[SrcY];
- DstLinePtr := @DstLine[0];
- MapPtr := @MapHorz^[0];
- for I := 0 to DstClipW - 1 do
- begin
- DstLinePtr^ := SrcLine[MapPtr^];
- Inc(DstLinePtr);
- Inc(MapPtr);
- end;
- OldSrcY := SrcY;
- end
- else
- MoveLongWord(DstLine[-Dst.Width], DstLine[0], DstClipW);
- Inc(DstLine, Dst.Width);
- end;
- end
- else
- begin
- SetLength(Buffer, DstClipW);
- DstLine := PColor32Array(Dst.PixelPtr[DstClip.Left, DstClip.Top]);
- OldSrcY := -1;
- if Src.MasterAlpha >= 255 then
- begin
- BlendLine := BLEND_LINE[Src.CombineMode]^;
- BlendLineEx := nil; // stop compiler warnings...
- end
- else
- begin
- BlendLineEx := BLEND_LINE_EX[Src.CombineMode]^;
- BlendLine := nil; // stop compiler warnings...
- end;
- for J := 0 to DstClipH - 1 do
- begin
- if DstH > 1 then
- begin
- if FullEdge then
- SrcY := Trunc(SrcRect.Top + (J + DstClip.Top - DstRect.Top) * Scale)
- else
- SrcY := Round(SrcRect.Top + (J + DstClip.Top - DstRect.Top) * Scale);
- end
- else
- SrcY := (SrcRect.Top + SrcRect.Bottom - 1) div 2;
-
- if SrcY <> OldSrcY then
- begin
- SrcLine := Src.ScanLine[SrcY];
- DstLinePtr := @Buffer[0];
- MapPtr := @MapHorz^[0];
- for I := 0 to DstClipW - 1 do
- begin
- DstLinePtr^ := SrcLine[MapPtr^];
- Inc(DstLinePtr);
- Inc(MapPtr);
- end;
- OldSrcY := SrcY;
- end;
- case CombineOp of
- dmBlend:
- if Src.MasterAlpha >= 255 then
- BlendLine(@Buffer[0], @DstLine[0], DstClipW)
- else
- BlendLineEx(@Buffer[0], @DstLine[0], DstClipW, Src.MasterAlpha);
- dmTransparent:
- for I := 0 to DstClipW - 1 do
- if Buffer[I] <> Src.OuterColor then DstLine[I] := Buffer[I];
- dmCustom:
- for I := 0 to DstClipW - 1 do
- CombineCallBack(Buffer[I], DstLine[I], Src.MasterAlpha);
- end;
- Inc(DstLine, Dst.Width);
- end;
- end;
- finally
- FreeMem(MapHorz);
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- //
- // StretchNearest
- //
- //------------------------------------------------------------------------------
- // Used by TDraftResampler.Resample (via DraftResample) and TLinearResampler.Resample
- //------------------------------------------------------------------------------
- procedure StretchHorzStretchVertLinear(
- Dst: TCustomBitmap32; DstRect, DstClip: TRect;
- Src: TCustomBitmap32; SrcRect: TRect;
- CombineOp: TDrawMode; CombineCallBack: TPixelCombineEvent);
- //Assure DstRect is >= SrcRect, otherwise quality loss will occur
- var
- SrcW, SrcH, DstW, DstH, DstClipW, DstClipH: Integer;
- MapHorz, MapVert: array of TPointRec;
- t2, Scale: TFloat;
- SrcLine, DstLine: PColor32Array;
- SrcIndex: Integer;
- SrcPtr1, SrcPtr2: PColor32;
- I, J: Integer;
- WY: Cardinal;
- C: TColor32;
- BlendMemEx: TBlendMemEx;
- begin
- SrcW := SrcRect.Right - SrcRect.Left;
- SrcH := SrcRect.Bottom - SrcRect.Top;
- DstW := DstRect.Right - DstRect.Left;
- DstH := DstRect.Bottom - DstRect.Top;
- DstClipW := DstClip.Right - DstClip.Left;
- DstClipH := DstClip.Bottom - DstClip.Top;
- SetLength(MapHorz, DstClipW);
- if FullEdge then Scale := SrcW / DstW
- else Scale := (SrcW - 1) / (DstW - 1);
- for I := 0 to DstClipW - 1 do
- begin
- if FullEdge then t2 := SrcRect.Left - 0.5 + (I + DstClip.Left - DstRect.Left + 0.5) * Scale
- else t2 := SrcRect.Left + (I + DstClip.Left - DstRect.Left) * Scale;
- if t2 < 0 then t2 := 0
- else if t2 > Src.Width - 1 then t2 := Src.Width - 1;
- MapHorz[I].Pos := Floor(t2);
- MapHorz[I].Weight := 256 - Round(Frac(t2) * 256);
- //Pre-pack weights to reduce MMX Reg. setups per pixel:
- //MapHorz[I].Weight:= MapHorz[I].Weight shl 16 + MapHorz[I].Weight;
- end;
- I := DstClipW - 1;
- while MapHorz[I].Pos = SrcRect.Right - 1 do
- begin
- Dec(MapHorz[I].Pos);
- MapHorz[I].Weight := 0;
- Dec(I);
- end;
- SetLength(MapVert, DstClipH);
- if FullEdge then Scale := SrcH / DstH
- else Scale := (SrcH - 1) / (DstH - 1);
- for I := 0 to DstClipH - 1 do
- begin
- if FullEdge then t2 := SrcRect.Top - 0.5 + (I + DstClip.Top - DstRect.Top + 0.5) * Scale
- else t2 := SrcRect.Top + (I + DstClip.Top - DstRect.Top) * Scale;
- if t2 < 0 then t2 := 0
- else if t2 > Src.Height - 1 then t2 := Src.Height - 1;
- MapVert[I].Pos := Floor(t2);
- MapVert[I].Weight := 256 - Round(Frac(t2) * 256);
- //Pre-pack weights to reduce MMX Reg. setups per pixel:
- //MapVert[I].Weight := MapVert[I].Weight shl 16 + MapVert[I].Weight;
- end;
- I := DstClipH - 1;
- while MapVert[I].Pos = SrcRect.Bottom - 1 do
- begin
- Dec(MapVert[I].Pos);
- MapVert[I].Weight := 0;
- Dec(I);
- end;
- DstLine := PColor32Array(Dst.PixelPtr[DstClip.Left, DstClip.Top]);
- SrcW := Src.Width;
- DstW := Dst.Width;
- case CombineOp of
- dmOpaque:
- for J := 0 to DstClipH - 1 do
- begin
- SrcLine := Src.ScanLine[MapVert[J].Pos];
- WY := MapVert[J].Weight;
- SrcIndex := MapHorz[0].Pos;
- SrcPtr1 := @SrcLine[SrcIndex];
- SrcPtr2 := @SrcLine[SrcIndex + SrcW];
- for I := 0 to DstClipW - 1 do
- begin
- if SrcIndex <> MapHorz[I].Pos then
- begin
- SrcIndex := MapHorz[I].Pos;
- SrcPtr1 := @SrcLine[SrcIndex];
- SrcPtr2 := @SrcLine[SrcIndex + SrcW];
- end;
- DstLine[I] := Interpolator(MapHorz[I].Weight, WY, SrcPtr1, SrcPtr2);
- end;
- Inc(DstLine, DstW);
- end;
- dmBlend:
- begin
- BlendMemEx := BLEND_MEM_EX[Src.CombineMode]^;
- for J := 0 to DstClipH - 1 do
- begin
- SrcLine := Src.ScanLine[MapVert[J].Pos];
- WY := MapVert[J].Weight;
- SrcIndex := MapHorz[0].Pos;
- SrcPtr1 := @SrcLine[SrcIndex];
- SrcPtr2 := @SrcLine[SrcIndex + SrcW];
- for I := 0 to DstClipW - 1 do
- begin
- if SrcIndex <> MapHorz[I].Pos then
- begin
- SrcIndex := MapHorz[I].Pos;
- SrcPtr1 := @SrcLine[SrcIndex];
- SrcPtr2 := @SrcLine[SrcIndex + SrcW];
- end;
- C := Interpolator(MapHorz[I].Weight, WY, SrcPtr1, SrcPtr2);
- BlendMemEx(C, DstLine[I], Src.MasterAlpha)
- end;
- Inc(DstLine, Dst.Width);
- end
- end;
- dmTransparent:
- begin
- for J := 0 to DstClipH - 1 do
- begin
- SrcLine := Src.ScanLine[MapVert[J].Pos];
- WY := MapVert[J].Weight;
- SrcIndex := MapHorz[0].Pos;
- SrcPtr1 := @SrcLine[SrcIndex];
- SrcPtr2 := @SrcLine[SrcIndex + SrcW];
- for I := 0 to DstClipW - 1 do
- begin
- if SrcIndex <> MapHorz[I].Pos then
- begin
- SrcIndex := MapHorz[I].Pos;
- SrcPtr1 := @SrcLine[SrcIndex];
- SrcPtr2 := @SrcLine[SrcIndex + SrcW];
- end;
- C := Interpolator(MapHorz[I].Weight, WY, SrcPtr1, SrcPtr2);
- if C <> Src.OuterColor then DstLine[I] := C;
- end;
- Inc(DstLine, Dst.Width);
- end
- end;
- else // cmCustom
- for J := 0 to DstClipH - 1 do
- begin
- SrcLine := Src.ScanLine[MapVert[J].Pos];
- WY := MapVert[J].Weight;
- SrcIndex := MapHorz[0].Pos;
- SrcPtr1 := @SrcLine[SrcIndex];
- SrcPtr2 := @SrcLine[SrcIndex + SrcW];
- for I := 0 to DstClipW - 1 do
- begin
- if SrcIndex <> MapHorz[I].Pos then
- begin
- SrcIndex := MapHorz[I].Pos;
- SrcPtr1 := @SrcLine[SrcIndex];
- SrcPtr2 := @SrcLine[SrcIndex + SrcW];
- end;
- C := Interpolator(MapHorz[I].Weight, WY, SrcPtr1, SrcPtr2);
- CombineCallBack(C, DstLine[I], Src.MasterAlpha);
- end;
- Inc(DstLine, Dst.Width);
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- //
- // Resample
- //
- //------------------------------------------------------------------------------
- // Primarily used by TKernelResampler.Resample
- //------------------------------------------------------------------------------
- // Precision of TMappingTable[][].Weight.
- // Totals Cb,Cg,Cr,Ca in Resample need to be unscaled by (1 shl MappingTablePrecicionShift2).
- const
- // Weight precision
- {$ifdef PREMULTIPLY}
- MappingTablePrecicionShift = 8; // Fixed precision [24:8]
- {$else PREMULTIPLY}
- MappingTablePrecicionShift = 11; // Fixed precision [21:11]
- {$endif PREMULTIPLY}
- MappingTablePrecicionShift2 = 2 * MappingTablePrecicionShift;
- MappingTablePrecicion = 1 shl MappingTablePrecicionShift;
- MappingTablePrecicion2 = 1 shl MappingTablePrecicionShift2;
- MappingTablePrecicionRound = (1 shl MappingTablePrecicionShift2) div 2 - 1;
- MappingTablePrecicionMax2 = 255 shl MappingTablePrecicionShift2;
- {$ifdef PREMULTIPLY}
- const
- // Premultiplication
- // Max error across all value[0..255]/alpha[1..255] combinations:
- // Shift=1: +/-1
- // Shift=2: +/-3
- // Shift=3: +/-7 in other words: error = +/- 2^(shift-1)
- // Shift=4: +/-15
- // Shift=5: +/-31
- MappingTablePremultPrecicionShift = 2; // [0..7]
- MappingTablePremultPrecicion = 1 shl MappingTablePremultPrecicionShift;
- {$endif PREMULTIPLY}
- //------------------------------------------------------------------------------
- // BuildMappingTable
- //------------------------------------------------------------------------------
- function BuildMappingTable(DstLo, DstHi: Integer; ClipLo, ClipHi: Integer;
- SrcLo, SrcHi: Integer; Kernel: TCustomKernel): TMappingTable;
- var
- SrcWidth, DstWidth, ClipWidth: Integer;
- Filter: TFilterMethod;
- FilterWidth: TFloat;
- Scale, InvScale: TFloat;
- Center: TFloat;
- Count: Integer;
- Left, Right: Integer;
- I, J, K: Integer;
- Weight: Integer;
- x0, x1, x2, x3: TFloat;
- begin
- SrcWidth := SrcHi - SrcLo;
- DstWidth := DstHi - DstLo;
- ClipWidth := ClipHi - ClipLo;
- if SrcWidth = 0 then
- begin
- Result := nil;
- Exit;
- end;
- if SrcWidth = 1 then
- begin
- SetLength(Result, ClipWidth);
- for I := 0 to ClipWidth - 1 do
- begin
- SetLength(Result[I], 1);
- Result[I][0].Pos := SrcLo;
- Result[I][0].Weight := MappingTablePrecicion; // Weight=1
- end;
- Exit;
- end;
- SetLength(Result, ClipWidth);
- if ClipWidth = 0 then
- Exit;
- if FullEdge then
- Scale := DstWidth / SrcWidth
- else
- Scale := (DstWidth - 1) / (SrcWidth - 1);
- Filter := Kernel.Filter;
- FilterWidth := Kernel.GetWidth;
- K := 0;
- if Scale = 0 then
- begin
- Assert(Length(Result) = 1);
- SetLength(Result[0], 1);
- Result[0][0].Pos := (SrcLo + SrcHi) div 2;
- Result[0][0].Weight := MappingTablePrecicion; // Weight=1
- end else
- if Scale < 1 then
- begin
- InvScale := Scale;
- Scale := 1 / Scale;
- FilterWidth := FilterWidth * Scale;
- for I := 0 to ClipWidth - 1 do
- begin
- if FullEdge then
- Center := SrcLo - 0.5 + (I - DstLo + ClipLo + 0.5) * Scale
- else
- Center := SrcLo + (I - DstLo + ClipLo) * Scale;
- Left := Floor(Center - FilterWidth);
- Right := Ceil(Center + FilterWidth);
- Count := -MappingTablePrecicion;
- for J := Left to Right do
- begin
- //
- // Compute the intergral for the convolution with the filter using the midpoint-rule:
- //
- // Assume that f(x) is continuous on [a, b], n is a positive integer and
- //
- // b - a
- // ∆x = -------
- // n
- //
- // If [a,b] is divided into n subintervals, each of length ∆x, and m{i} is the midpoint
- // of the i'th subinterval, set
- //
- // M{n} = ∑ f(m{i}) ∆x
- //
- // then
- //
- // M{n} ≈ ∫ f(x)dx
- //
- // In other words, the integral from x1 to x2 of f(x) dx is approximately:
- //
- // f((x1+x2)/2)*(x2-x1).
- //
- x0 := J - Center;
- x1 := Max(x0 - 0.5, -FilterWidth);
- x2 := Min(x0 + 0.5, FilterWidth);
- x3 := (x2 + x1) * 0.5; // Center of [x1, x2]
- Weight := Round(MappingTablePrecicion * Filter(x3 * InvScale) * (x2 - x1) * InvScale);
- if Weight <> 0 then
- begin
- Inc(Count, Weight);
- K := Length(Result[I]);
- SetLength(Result[I], K + 1);
- Result[I][K].Pos := Constrain(J, SrcLo, SrcHi - 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 := MappingTablePrecicion;
- end else
- if Count <> 0 then
- Dec(Result[I][K div 2].Weight, Count);
- end;
- end
- else // scale > 1
- begin
- Scale := 1 / Scale;
- for I := 0 to ClipWidth - 1 do
- begin
- if FullEdge then
- Center := SrcLo - 0.5 + (I - DstLo + ClipLo + 0.5) * Scale
- else
- Center := SrcLo + (I - DstLo + ClipLo) * Scale;
- Left := Floor(Center - FilterWidth);
- Right := Ceil(Center + FilterWidth);
- Count := -MappingTablePrecicion;
- for J := Left to Right do
- begin
- x0 := J - Center;
- x1 := Max(x0 - 0.5, -FilterWidth);
- x2 := Min(x0 + 0.5, FilterWidth);
- x3 := (x1 + x2) * 0.5;
- Weight := Round(MappingTablePrecicion * Filter(x3) * (x2 - x1));
- if Weight <> 0 then
- begin
- Inc(Count, Weight);
- K := Length(Result[I]);
- SetLength(Result[I], K + 1);
- Result[I][K].Pos := Constrain(J, SrcLo, SrcHi - 1);
- Result[I][K].Weight := Weight;
- end;
- end;
- if Count <> 0 then
- Dec(Result[I][K div 2].Weight, Count);
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- // Premultiply
- //------------------------------------------------------------------------------
- {$ifdef PREMULTIPLY}
- function Premultiply(Value, Alpha: integer): integer; {$IFDEF USEINLINING} inline; {$ENDIF}
- begin
- // Instead of performing a full traditional premultiplication:
- //
- // RGBp = RGB * Alpha / 255
- //
- // we try to lessen the rounding error, which is normally
- // introduced when this is done in integer precision, by
- // using a smaller divisor. Additionally we use a power of 2
- // divisor so the division can be done with a simple shift:
- //
- // RGBp = RGB * Alpha >> X
- //
- // We need to use "div" for division instead of a direct "shr" as
- // "shr" performs a logical shift and not an arithmetic shift.
- // The compiler will optimize a "div" with a power of 2 constant
- // divisor to an arithmetic shift, so it's a very cheap operation.
- Result := (Value * Alpha) div MappingTablePremultPrecicion;
- end;
- //------------------------------------------------------------------------------
- // Unpremultiply
- //------------------------------------------------------------------------------
- function Unpremultiply(Value, Alpha: integer): integer; {$IFDEF USEINLINING} inline; {$ENDIF}
- begin
- // It would be best if we could do the multiplication before the division
- // but unfortunately that overflows the fixed precision.
- Result := (Value div Alpha) * MappingTablePremultPrecicion;
- end;
- {$endif PREMULTIPLY}
- //------------------------------------------------------------------------------
- // Resample
- //------------------------------------------------------------------------------
- procedure Resample(
- Dst: TCustomBitmap32; DstRect: TRect; DstClip: TRect;
- Src: TCustomBitmap32; SrcRect: TRect;
- Kernel: TCustomKernel;
- CombineOp: TDrawMode; CombineCallBack: TPixelCombineEvent);
- var
- DstClipW: Integer;
- MapX, MapY: TMappingTable;
- I, J, X, Y: Integer;
- MapXLoPos, MapXHiPos: Integer;
- HorzBuffer: array of TBufferEntry;
- ClusterX, ClusterY: TCluster;
- Cb, Cg, Cr, Ca: Integer;
- C: TColor32Entry;
- ClusterWeight: Integer;
- DstLine: PColor32Array;
- RangeCheck: Boolean;
- BlendMemEx: TBlendMemEx;
- SourceColor: PColor32Entry;
- BufferEntry: PBufferEntry;
- {$ifdef PREMULTIPLY}
- Alpha: integer;
- DoPremultiply: boolean;
- {$endif PREMULTIPLY}
- begin
- if (CombineOp = dmCustom) and not Assigned(CombineCallBack) then
- CombineOp := dmOpaque;
- { check source and destination }
- if (CombineOp = dmBlend) and (Src.MasterAlpha = 0) then
- Exit;
- BlendMemEx := BLEND_MEM_EX[Src.CombineMode]^; // store in local variable
- DstClipW := DstClip.Right - DstClip.Left;
- // Mapping tables
- MapX := BuildMappingTable(DstRect.Left, DstRect.Right, DstClip.Left, DstClip.Right, SrcRect.Left, SrcRect.Right, Kernel);
- MapY := BuildMappingTable(DstRect.Top, DstRect.Bottom, DstClip.Top, DstClip.Bottom, SrcRect.Top, SrcRect.Bottom, Kernel);
- if (MapX = nil) or (MapY = nil) then
- Exit;
- {$ifdef PREMULTIPLY}
- // Scan bitmap for alpha
- DoPremultiply := False;
- SourceColor := PColor32Entry(Src.Bits);
- I := Src.Height*Src.Width;
- while (I > 0) do
- begin
- if (SourceColor.A <> 255) and (SourceColor.A <> 0) then
- begin
- // We only need to do alpha-premultiplication if Alpha exist in range [1..254]
- DoPremultiply := True;
- break;
- end;
- Inc(SourceColor);
- Dec(I);
- end;
- {$endif PREMULTIPLY}
- ClusterX := nil;
- ClusterY := nil;
- {$ifdef PREMULTIPLY}
- // If we're doing premultiplication then we always need to clamp the unpremultiplied
- // values. Why? Well, premult/unpremult normally goes like this:
- //
- // RGBp = RGB * Alpha / 255
- // RGB = RGBp * 255 / Alpha
- //
- // or in this particular case:
- //
- // RGBp = RGB * Alpha / 255
- // RGB = ∑RGBp * 255 / ∑Alpha
- //
- // Now in case the rounding of the RGB or Alpha values leads to (∑RGBp > RGBp) or
- // (Alpha > ∑Alpha) then we will get RGB values out of bounds (i.e. > 255).
- RangeCheck := DoPremultiply or Kernel.RangeCheck;
- {$else PREMULTIPLY}
- RangeCheck := Kernel.RangeCheck;
- {$endif PREMULTIPLY}
- MapXLoPos := MapX[0][0].Pos;
- MapXHiPos := MapX[DstClipW - 1][High(MapX[DstClipW - 1])].Pos;
- SetLength(HorzBuffer, MapXHiPos - MapXLoPos + 1);
- { transfer pixels }
- for J := DstClip.Top to DstClip.Bottom - 1 do
- begin
- ClusterY := MapY[J - DstClip.Top];
- ClusterWeight := ClusterY[0].Weight;
- SourceColor := @Src.Bits[ClusterY[0].Pos * Src.Width + MapXLoPos];
- BufferEntry := @HorzBuffer[0];
- X := MapXHiPos - MapXLoPos;
- while (X >= 0) do // for X := MapXLoPos to MapXHiPos do
- begin
- {$ifdef PREMULTIPLY}
- // Alpha=0 should not contribute to sample.
- Alpha := SourceColor.A;
- if (Alpha <> 0) then
- begin
- Alpha := Alpha * ClusterWeight;
- if (DoPremultiply) then
- begin
- // Sample premultiplied values
- // RGB is multiplied with Alpha during premultiplication so instead of
- // BufferEntry.RGB := Premultiply(SourceColor.RGB * ClusterWeight, Alpha);
- // we're doing
- // Alpha := Alpha * ClusterWeight;
- // BufferEntry.RGB := Premultiply(SourceColor.RGB, Alpha);
- // and saving 3 multiplications.
- BufferEntry.B := Premultiply(SourceColor.B, Alpha);
- BufferEntry.G := Premultiply(SourceColor.G, Alpha);
- BufferEntry.R := Premultiply(SourceColor.R, Alpha);
- end else
- begin
- BufferEntry.B := SourceColor.B * ClusterWeight;
- BufferEntry.G := SourceColor.G * ClusterWeight;
- BufferEntry.R := SourceColor.R * ClusterWeight;
- end;
- BufferEntry.A := Alpha;
- end else
- BufferEntry^ := Default(TBufferEntry);
- {$else PREMULTIPLY}
- // Alpha=0 should not contribute to sample.
- if (SourceColor.A <> 0) then
- begin
- BufferEntry.B := SourceColor.B * ClusterWeight;
- BufferEntry.G := SourceColor.G * ClusterWeight;
- BufferEntry.R := SourceColor.R * ClusterWeight;
- BufferEntry.A := SourceColor.A * ClusterWeight;
- end else
- BufferEntry^ := Default(TBufferEntry);
- {$endif PREMULTIPLY}
- Inc(SourceColor);
- Inc(BufferEntry);
- Dec(X);
- end;
- Y := Length(ClusterY) - 1;
- while (Y > 0) do // for Y := 1 to Length(ClusterY) - 1 do
- begin
- ClusterWeight := ClusterY[Y].Weight;
- SourceColor := @Src.Bits[ClusterY[Y].Pos * Src.Width + MapXLoPos];
- BufferEntry := @HorzBuffer[0];
- X := MapXHiPos - MapXLoPos;
- while (X >= 0) do // for X := MapXLoPos to MapXHiPos do
- begin
- {$ifdef PREMULTIPLY}
- // Alpha=0 should not contribute to sample.
- Alpha := SourceColor.A;
- if (Alpha <> 0) then
- begin
- Alpha := Alpha * ClusterWeight;
- if (DoPremultiply) then
- begin
- // Sample premultiplied values
- Inc(BufferEntry.B, Premultiply(SourceColor.B, Alpha));
- Inc(BufferEntry.G, Premultiply(SourceColor.G, Alpha));
- Inc(BufferEntry.R, Premultiply(SourceColor.R, Alpha));
- end else
- begin
- Inc(BufferEntry.B, SourceColor.B * ClusterWeight);
- Inc(BufferEntry.G, SourceColor.G * ClusterWeight);
- Inc(BufferEntry.R, SourceColor.R * ClusterWeight);
- end;
- Inc(BufferEntry.A, Alpha);
- end;
- {$else PREMULTIPLY}
- // Alpha=0 should not contribute to sample.
- if (SourceColor.A <> 0) then
- begin
- Inc(BufferEntry.B, SourceColor.B * ClusterWeight);
- Inc(BufferEntry.G, SourceColor.G * ClusterWeight);
- Inc(BufferEntry.R, SourceColor.R * ClusterWeight);
- Inc(BufferEntry.A, SourceColor.A * ClusterWeight);
- end;
- {$endif PREMULTIPLY}
- Inc(SourceColor);
- Inc(BufferEntry);
- Dec(X);
- end;
- Dec(Y);
- end;
- DstLine := Dst.ScanLine[J];
- for I := DstClip.Left to DstClip.Right - 1 do
- begin
- Cb := 0; Cg := Cb; Cr := Cb; Ca := Cb;
- ClusterX := MapX[I - DstClip.Left];
- X := Length(ClusterX) - 1;
- while (X >= 0) do // for X := 0 to Length(ClusterX) - 1 do
- begin
- with HorzBuffer[ClusterX[X].Pos - MapXLoPos] do
- if (A <> 0) then // If Alpha=0 then RGB=0
- begin
- ClusterWeight := ClusterX[X].Weight;
- Inc(Cb, B * ClusterWeight); // Note: Fixed precision multiplication done here
- Inc(Cg, G * ClusterWeight);
- Inc(Cr, R * ClusterWeight);
- Inc(Ca, A * ClusterWeight);
- end;
- Dec(X);
- end;
- // Unpremultiply, unscale and round
- if RangeCheck then
- begin
- {$ifdef PREMULTIPLY}
- Alpha:= (Clamp(Ca, 0, MappingTablePrecicionMax2) + MappingTablePrecicionRound) shr MappingTablePrecicionShift2;
- if (Alpha <> 0) then
- begin
- if (DoPremultiply) then
- begin
- C.B := (Clamp(Unpremultiply(Cb, Alpha), 0, MappingTablePrecicionMax2) + MappingTablePrecicionRound) shr MappingTablePrecicionShift2;
- C.G := (Clamp(Unpremultiply(Cg, Alpha), 0, MappingTablePrecicionMax2) + MappingTablePrecicionRound) shr MappingTablePrecicionShift2;
- C.R := (Clamp(Unpremultiply(Cr, Alpha), 0, MappingTablePrecicionMax2) + MappingTablePrecicionRound) shr MappingTablePrecicionShift2;
- C.A := Alpha;
- end else
- begin
- C.B := (Clamp(Cb, 0, MappingTablePrecicionMax2) + MappingTablePrecicionRound) shr MappingTablePrecicionShift2;
- C.G := (Clamp(Cg, 0, MappingTablePrecicionMax2) + MappingTablePrecicionRound) shr MappingTablePrecicionShift2;
- C.R := (Clamp(Cr, 0, MappingTablePrecicionMax2) + MappingTablePrecicionRound) shr MappingTablePrecicionShift2;
- C.A := 255; // We know Alpha=255 because RangeCheck is True otherwise
- end;
- end else
- C.ARGB := 0;
- {$else PREMULTIPLY}
- if (Ca <> 0) then
- begin
- C.B := (Clamp(Cb, 0, MappingTablePrecicionMax2) + MappingTablePrecicionRound) shr MappingTablePrecicionShift2;
- C.G := (Clamp(Cg, 0, MappingTablePrecicionMax2) + MappingTablePrecicionRound) shr MappingTablePrecicionShift2;
- C.R := (Clamp(Cr, 0, MappingTablePrecicionMax2) + MappingTablePrecicionRound) shr MappingTablePrecicionShift2;
- C.A := (Clamp(Ca, 0, MappingTablePrecicionMax2) + MappingTablePrecicionRound) shr MappingTablePrecicionShift2;
- end else
- C.ARGB := 0;
- {$endif PREMULTIPLY}
- end else
- begin
- {$ifdef PREMULTIPLY}
- Alpha:= (Ca + MappingTablePrecicionRound) shr MappingTablePrecicionShift2;
- if (Alpha <> 0) then
- begin
- C.B := (Cb + MappingTablePrecicionRound) shr MappingTablePrecicionShift2;
- C.G := (Cg + MappingTablePrecicionRound) shr MappingTablePrecicionShift2;
- C.R := (Cr + MappingTablePrecicionRound) shr MappingTablePrecicionShift2;
- C.A := 255; // We know Alpha=255 because RangeCheck is True otherwise
- end else
- C.ARGB := 0;
- {$else PREMULTIPLY}
- if (Ca <> 0) then
- begin
- C.B := (Cb + MappingTablePrecicionRound) shr MappingTablePrecicionShift2;
- C.G := (Cg + MappingTablePrecicionRound) shr MappingTablePrecicionShift2;
- C.R := (Cr + MappingTablePrecicionRound) shr MappingTablePrecicionShift2;
- C.A := (Ca + MappingTablePrecicionRound) shr MappingTablePrecicionShift2;
- end else
- C.ARGB := 0;
- {$endif PREMULTIPLY}
- end;
- // Combine it with the background
- case CombineOp of
- dmOpaque:
- DstLine[I] := C.ARGB;
- dmBlend:
- BlendMemEx(C.ARGB, DstLine[I], Src.MasterAlpha);
- dmTransparent:
- if C.ARGB <> Src.OuterColor then
- DstLine[I] := C.ARGB;
- dmCustom:
- CombineCallBack(C.ARGB, DstLine[I], Src.MasterAlpha);
- end;
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- //
- // DraftResample
- //
- //------------------------------------------------------------------------------
- // Used by TDraftResampler.Resample
- //------------------------------------------------------------------------------
- //------------------------------------------------------------------------------
- // BlockAverage_Pas
- //------------------------------------------------------------------------------
- function BlockAverage_Pas(Dlx, Dly: Cardinal; RowSrc: PColor32; OffSrc: Cardinal): TColor32;
- var
- C: PColor32Entry;
- ix, iy, iA, iR, iG, iB, Area: Cardinal;
- begin
- iR := 0; iB := iR; iG := iR; iA := iR;
- for iy := 1 to Dly do
- begin
- C := PColor32Entry(RowSrc);
- for ix := 1 to Dlx do
- begin
- Inc(iB, C.B);
- Inc(iG, C.G);
- Inc(iR, C.R);
- Inc(iA, C.A);
- Inc(C);
- end;
- Inc(PByte(RowSrc), OffSrc);
- end;
- Area := Dlx * Dly;
- Area := $1000000 div Area;
- Result := iA * Area and $FF000000 or
- iR * Area shr 8 and $FF0000 or
- iG * Area shr 16 and $FF00 or
- iB * Area shr 24 and $FF;
- end;
- //------------------------------------------------------------------------------
- // BlockAverage_SSE2
- //------------------------------------------------------------------------------
- {$if (not defined(PUREPASCAL)) and (not defined(OMIT_SSE2))}
- function BlockAverage_SSE2(Dlx, Dly: Cardinal; RowSrc: PColor32; OffSrc: Cardinal): TColor32;
- asm
- {$IFDEF TARGET_X64}
- MOV EAX,ECX
- MOV R10D,EDX
- SHL EAX,$02
- SUB R9D,EAX
- PXOR XMM1,XMM1
- PXOR XMM2,XMM2
- PXOR XMM7,XMM7
- @@LoopY:
- MOV EAX,ECX
- PXOR XMM0,XMM0
- LEA R8,[R8+RAX*4]
- NEG RAX
- @@LoopX:
- MOVD XMM6,[R8+RAX*4]
- PUNPCKLBW XMM6,XMM7
- PADDW XMM0,XMM6
- INC RAX
- JNZ @@LoopX
- MOVQ XMM6,XMM0
- PUNPCKLWD XMM6,XMM7
- PADDD XMM1,XMM6
- ADD R8,R9
- DEC EDX
- JNZ @@LoopY
- MOV EAX, ECX
- MUL R10D
- MOV ECX,EAX
- MOV EAX,$01000000
- DIV ECX
- MOV ECX,EAX
- MOVD EAX,XMM1
- MUL ECX
- SHR EAX,$18
- MOV R10D,EAX
- SHUFPS XMM1,XMM1,$39
- MOVD EAX,XMM1
- MUL ECX
- SHR EAX,$10
- AND EAX,$0000FF00
- ADD R10D,EAX
- PSHUFD XMM1,XMM1,$39
- MOVD EAX,XMM1
- MUL ECX
- SHR EAX,$08
- AND EAX,$00FF0000
- ADD R10D,EAX
- PSHUFD XMM1,XMM1,$39
- MOVD EAX,XMM1
- MUL ECX
- AND EAX,$FF000000
- ADD EAX,R10D
- {$ELSE}
- PUSH EBX
- PUSH ESI
- PUSH EDI
- MOV EBX,OffSrc
- MOV ESI,EAX
- MOV EDI,EDX
- SHL ESI,$02
- SUB EBX,ESI
- PXOR XMM1,XMM1
- PXOR XMM2,XMM2
- PXOR XMM7,XMM7
- @@LoopY:
- MOV ESI,EAX
- PXOR XMM0,XMM0
- LEA ECX,[ECX+ESI*4]
- NEG ESI
- @@LoopX:
- MOVD XMM6,[ECX+ESI*4]
- PUNPCKLBW XMM6,XMM7
- PADDW XMM0,XMM6
- INC ESI
- JNZ @@LoopX
- MOVQ XMM6,XMM0
- PUNPCKLWD XMM6,XMM7
- PADDD XMM1,XMM6
- ADD ECX,EBX
- DEC EDX
- JNZ @@LoopY
- MUL EDI
- MOV ECX,EAX
- MOV EAX,$01000000
- DIV ECX
- MOV ECX,EAX
- MOVD EAX,XMM1
- MUL ECX
- SHR EAX,$18
- MOV EDI,EAX
- SHUFPS XMM1,XMM1,$39
- MOVD EAX,XMM1
- MUL ECX
- SHR EAX,$10
- AND EAX,$0000FF00
- ADD EDI,EAX
- PSHUFD XMM1,XMM1,$39
- MOVD EAX,XMM1
- MUL ECX
- SHR EAX,$08
- AND EAX,$00FF0000
- ADD EDI,EAX
- PSHUFD XMM1,XMM1,$39
- MOVD EAX,XMM1
- MUL ECX
- AND EAX,$FF000000
- ADD EAX,EDI
- POP EDI
- POP ESI
- POP EBX
- {$ENDIF}
- end;
- {$ifend}
- //------------------------------------------------------------------------------
- // DraftResample
- //------------------------------------------------------------------------------
- procedure DraftResample(Dst: TCustomBitmap32; DstRect: TRect; DstClip: TRect;
- Src: TCustomBitmap32; SrcRect: TRect; Kernel: TCustomKernel;
- CombineOp: TDrawMode; CombineCallBack: TPixelCombineEvent);
- var
- SrcW, SrcH,
- DstW, DstH,
- DstClipW, DstClipH: Cardinal;
- RowSrc: PColor32;
- xsrc: PColor32;
- OffSrc,
- dy, dx,
- c1, c2, r1, r2,
- xs: Cardinal;
- C: TColor32;
- DstLine: PColor32Array;
- ScaleFactor: TFloat;
- I,J, sc, sr, cx, cy: Integer;
- BlendMemEx: TBlendMemEx;
- begin
- { rangechecking and rect intersection done by caller }
- SrcW := SrcRect.Right - SrcRect.Left;
- SrcH := SrcRect.Bottom - SrcRect.Top;
- DstW := DstRect.Right - DstRect.Left;
- DstH := DstRect.Bottom - DstRect.Top;
- DstClipW := DstClip.Right - DstClip.Left;
- DstClipH := DstClip.Bottom - DstClip.Top;
- BlendMemEx := BLEND_MEM_EX[Src.CombineMode]^;
- if (DstW > SrcW)or(DstH > SrcH) then
- begin
- if (SrcW < 2) or (SrcH < 2) then
- Resample(Dst, DstRect, DstClip, Src, SrcRect, Kernel, CombineOp, CombineCallBack)
- else
- StretchHorzStretchVertLinear(Dst, DstRect, DstClip, Src, SrcRect, CombineOp, CombineCallBack);
- end else
- begin //Full Scaledown, ignores Fulledge - cannot be integrated into this resampling method
- OffSrc := Src.Width * 4;
- ScaleFactor:= SrcW / DstW;
- cx := Trunc( (DstClip.Left - DstRect.Left) * ScaleFactor);
- r2 := Trunc(ScaleFactor);
- sr := Trunc( $10000 * ScaleFactor );
- ScaleFactor:= SrcH / DstH;
- cy := Trunc( (DstClip.Top - DstRect.Top) * ScaleFactor);
- c2 := Trunc(ScaleFactor);
- sc := Trunc( $10000 * ScaleFactor );
- DstLine := PColor32Array(Dst.PixelPtr[0, DstClip.Top]);
- RowSrc := Src.PixelPtr[SrcRect.Left + cx, SrcRect.Top + cy ];
- xs := r2;
- c1 := 0;
- Dec(DstClip.Left, 2);
- Inc(DstClipW);
- Inc(DstClipH);
- for J := 2 to DstClipH do
- begin
- dy := c2 - c1;
- c1 := c2;
- c2 := FixedMul(J, sc);
- r1 := 0;
- r2 := xs;
- xsrc := RowSrc;
- case CombineOp of
- dmOpaque:
- for I := 2 to DstClipW do
- begin
- dx := r2 - r1; r1 := r2;
- r2 := FixedMul(I, sr);
- DstLine[DstClip.Left + I] := BlockAverage(dx, dy, xsrc, OffSrc);
- Inc(xsrc, dx);
- end;
- dmBlend:
- for I := 2 to DstClipW do
- begin
- dx := r2 - r1; r1 := r2;
- r2 := FixedMul(I, sr);
- BlendMemEx(BlockAverage(dx, dy, xsrc, OffSrc),
- DstLine[DstClip.Left + I], Src.MasterAlpha);
- Inc(xsrc, dx);
- end;
- dmTransparent:
- for I := 2 to DstClipW do
- begin
- dx := r2 - r1; r1 := r2;
- r2 := FixedMul(I, sr);
- C := BlockAverage(dx, dy, xsrc, OffSrc);
- if C <> Src.OuterColor then DstLine[DstClip.Left + I] := C;
- Inc(xsrc, dx);
- end;
- dmCustom:
- for I := 2 to DstClipW do
- begin
- dx := r2 - r1; r1 := r2;
- r2 := FixedMul(I, sr);
- CombineCallBack(BlockAverage(dx, dy, xsrc, OffSrc),
- DstLine[DstClip.Left + I], Src.MasterAlpha);
- Inc(xsrc, dx);
- end;
- end;
- Inc(DstLine, Dst.Width);
- Inc(PByte(RowSrc), OffSrc * dy);
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- //
- // Special interpolators (for sfLinear and sfDraft)
- //
- //------------------------------------------------------------------------------
- //------------------------------------------------------------------------------
- // Interpolator_Pas
- //------------------------------------------------------------------------------
- function Interpolator_Pas(WX_256, WY_256: Cardinal; C11, C21: PColor32): TColor32;
- var
- C1, C3: TColor32;
- begin
- if WX_256 > $FF then WX_256:= $FF;
- if WY_256 > $FF then WY_256:= $FF;
- C1 := C11^; Inc(C11);
- C3 := C21^; Inc(C21);
- Result := CombineReg(CombineReg(C1, C11^, WX_256),
- CombineReg(C3, C21^, WX_256), WY_256);
- end;
- //------------------------------------------------------------------------------
- // Interpolator_SSE2
- //------------------------------------------------------------------------------
- {$if (not defined(PUREPASCAL)) and (not defined(OMIT_SSE2))}
- function Interpolator_SSE2(WX_256, WY_256: Cardinal; C11, C21: PColor32): TColor32;
- asm
- {$IFDEF TARGET_X64}
- MOV RAX, RCX
- MOVQ XMM1,QWORD PTR [R8]
- MOVQ XMM2,XMM1
- MOVQ XMM3,QWORD PTR [R9]
- {$ELSE}
- MOVQ XMM1,[ECX]
- MOVQ XMM2,XMM1
- MOV ECX,C21
- MOVQ XMM3,[ECX]
- {$ENDIF}
- PSRLQ XMM1,32
- MOVQ XMM4,XMM3
- PSRLQ XMM3,32
- MOVD XMM5,EAX
- PSHUFLW XMM5,XMM5,0
- PXOR XMM0,XMM0
- PUNPCKLBW XMM1,XMM0
- PUNPCKLBW XMM2,XMM0
- PSUBW XMM2,XMM1
- PMULLW XMM2,XMM5
- PSLLW XMM1,8
- PADDW XMM2,XMM1
- PSRLW XMM2,8
- PUNPCKLBW XMM3,XMM0
- PUNPCKLBW XMM4,XMM0
- PSUBW XMM4,XMM3
- PSLLW XMM3,8
- PMULLW XMM4,XMM5
- PADDW XMM4,XMM3
- PSRLW XMM4,8
- MOVD XMM5,EDX
- PSHUFLW XMM5,XMM5,0
- PSUBW XMM2,XMM4
- PMULLW XMM2,XMM5
- PSLLW XMM4,8
- PADDW XMM2,XMM4
- PSRLW XMM2,8
- PACKUSWB XMM2,XMM0
- MOVD EAX,XMM2
- end;
- {$ifend}
- //------------------------------------------------------------------------------
- //
- // StretchTransfer
- //
- //------------------------------------------------------------------------------
- {$WARNINGS OFF}
- procedure StretchTransfer(
- Dst: TCustomBitmap32; DstRect: TRect; DstClip: TRect;
- Src: TCustomBitmap32; SrcRect: TRect;
- Resampler: TCustomResampler;
- CombineOp: TDrawMode; CombineCallBack: TPixelCombineEvent);
- var
- SrcW, SrcH: Integer;
- DstW, DstH: Integer;
- R: TRect;
- RatioX, RatioY: Single;
- begin
- CheckBitmaps(Dst, Src);
- // transform dest rect when the src rect is out of the src bitmap's bounds
- if (SrcRect.Left < 0) or (SrcRect.Right > Src.Width) or
- (SrcRect.Top < 0) or (SrcRect.Bottom > Src.Height) then
- begin
- RatioX := (DstRect.Right - DstRect.Left) / (SrcRect.Right - SrcRect.Left);
- RatioY := (DstRect.Bottom - DstRect.Top) / (SrcRect.Bottom - SrcRect.Top);
- if SrcRect.Left < 0 then
- begin
- DstRect.Left := DstRect.Left + Ceil(-SrcRect.Left * RatioX);
- SrcRect.Left := 0;
- end;
- if SrcRect.Top < 0 then
- begin
- DstRect.Top := DstRect.Top + Ceil(-SrcRect.Top * RatioY);
- SrcRect.Top := 0;
- end;
- if SrcRect.Right > Src.Width then
- begin
- DstRect.Right := DstRect.Right - Floor((SrcRect.Right - Src.Width) * RatioX);
- SrcRect.Right := Src.Width;
- end;
- if SrcRect.Bottom > Src.Height then
- begin
- DstRect.Bottom := DstRect.Bottom - Floor((SrcRect.Bottom - Src.Height) * RatioY);
- SrcRect.Bottom := Src.Height;
- end;
- end;
- if Src.Empty or Dst.Empty or
- ((CombineOp = dmBlend) and (Src.MasterAlpha = 0)) or
- GR32.IsRectEmpty(SrcRect) then
- Exit;
- if not Dst.MeasuringMode then
- begin
- GR32.IntersectRect(DstClip, DstClip, Dst.BoundsRect);
- GR32.IntersectRect(DstClip, DstClip, DstRect);
- if GR32.IsRectEmpty(DstClip) then Exit;
- GR32.IntersectRect(R, DstClip, DstRect);
- if GR32.IsRectEmpty(R) then Exit;
- if (CombineOp = dmCustom) and not Assigned(CombineCallBack) then
- CombineOp := dmOpaque;
- SrcW := SrcRect.Right - SrcRect.Left;
- SrcH := SrcRect.Bottom - SrcRect.Top;
- DstW := DstRect.Right - DstRect.Left;
- DstH := DstRect.Bottom - DstRect.Top;
- if (SrcW = DstW) and (SrcH = DstH) then
- BlendBlock(Dst, DstClip, Src, SrcRect.Left + DstClip.Left - DstRect.Left, SrcRect.Top + DstClip.Top - DstRect.Top, CombineOp, CombineCallBack)
- else
- TCustomResamplerAccess(Resampler).Resample(Dst, DstRect, DstClip, Src, SrcRect, CombineOp, CombineCallBack);
- end;
- Dst.Changed(DstRect);
- end;
- {$WARNINGS ON}
- //------------------------------------------------------------------------------
- //
- // TByteMap downsample functions
- //
- //------------------------------------------------------------------------------
- procedure DownsampleByteMap2x(Source, Dest: TByteMap);
- var
- X, Y: Integer;
- ScnLn: array [0 .. 2] of PByteArray;
- begin
- for Y := 0 to (Source.Height div 2) - 1 do
- begin
- ScnLn[0] := Dest.ScanLine[Y];
- ScnLn[1] := Source.ScanLine[Y * 2];
- ScnLn[2] := Source.ScanLine[Y * 2 + 1];
- for X := 0 to (Source.Width div 2) - 1 do
- ScnLn[0, X] := (
- ScnLn[1, 2 * X] + ScnLn[1, 2 * X + 1] +
- ScnLn[2, 2 * X] + ScnLn[2, 2 * X + 1]) div 4;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure DownsampleByteMap3x(Source, Dest: TByteMap);
- var
- X, Y: Integer;
- x3: Integer;
- ScnLn: array [0 .. 3] of PByteArray;
- begin
- for Y := 0 to (Source.Height div 3) - 1 do
- begin
- ScnLn[0] := Dest.ScanLine[Y];
- ScnLn[1] := Source.ScanLine[3 * Y];
- ScnLn[2] := Source.ScanLine[3 * Y + 1];
- ScnLn[3] := Source.ScanLine[3 * Y + 2];
- for X := 0 to (Source.Width div 3) - 1 do
- begin
- x3 := 3 * X;
- ScnLn[0, X] := (
- ScnLn[1, x3] + ScnLn[1, x3 + 1] + ScnLn[1, x3 + 2] +
- ScnLn[2, x3] + ScnLn[2, x3 + 1] + ScnLn[2, x3 + 2] +
- ScnLn[3, x3] + ScnLn[3, x3 + 1] + ScnLn[3, x3 + 2]) div 9;
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure DownsampleByteMap4x(Source, Dest: TByteMap);
- var
- X, Y: Integer;
- x4: Integer;
- ScnLn: array [0 .. 4] of PByteArray;
- begin
- for Y := 0 to (Source.Height div 4) - 1 do
- begin
- ScnLn[0] := Dest.ScanLine[Y];
- ScnLn[1] := Source.ScanLine[Y * 4];
- ScnLn[2] := Source.ScanLine[Y * 4 + 1];
- ScnLn[3] := Source.ScanLine[Y * 4 + 2];
- ScnLn[4] := Source.ScanLine[Y * 4 + 3];
- for X := 0 to (Source.Width div 4) - 1 do
- begin
- x4 := 4 * X;
- ScnLn[0, X] := (
- ScnLn[1, x4] + ScnLn[1, x4 + 1] + ScnLn[1, x4 + 2] + ScnLn[1, x4 + 3] +
- ScnLn[2, x4] + ScnLn[2, x4 + 1] + ScnLn[2, x4 + 2] + ScnLn[2, x4 + 3] +
- ScnLn[3, x4] + ScnLn[3, x4 + 1] + ScnLn[3, x4 + 2] + ScnLn[3, x4 + 3] +
- ScnLn[4, x4] + ScnLn[4, x4 + 1] + ScnLn[4, x4 + 2] + ScnLn[4, x4 + 3]) div 16;
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- //
- // TCustomKernel
- //
- //------------------------------------------------------------------------------
- procedure TCustomKernel.AssignTo(Dst: TPersistent);
- begin
- if Dst is TCustomKernel then
- SmartAssign(Self, Dst)
- else
- inherited;
- end;
- procedure TCustomKernel.Changed;
- begin
- if Assigned(FObserver) then FObserver.Changed;
- end;
- constructor TCustomKernel.Create;
- begin
- end;
- function TCustomKernel.RangeCheck: Boolean;
- begin
- Result := False;
- end;
- //------------------------------------------------------------------------------
- //
- // TBoxKernel
- //
- //------------------------------------------------------------------------------
- function TBoxKernel.Filter(Value: TFloat): TFloat;
- begin
- if (Value >= -0.5) and (Value <= 0.5) then
- Result := 1.0
- else
- Result := 0;
- end;
- function TBoxKernel.GetWidth: TFloat;
- begin
- Result := 1;
- end;
- //------------------------------------------------------------------------------
- //
- // TLinearKernel
- //
- //------------------------------------------------------------------------------
- function TLinearKernel.Filter(Value: TFloat): TFloat;
- begin
- if Value < -1 then
- Result := 0
- else
- if Value < 0 then
- Result := 1 + Value
- else
- if Value < 1 then
- Result := 1 - Value
- else
- Result := 0;
- end;
- function TLinearKernel.GetWidth: TFloat;
- begin
- Result := 1;
- end;
- //------------------------------------------------------------------------------
- //
- // TCosineKernel
- //
- //------------------------------------------------------------------------------
- function TCosineKernel.Filter(Value: TFloat): TFloat;
- begin
- Result := 0;
- if Abs(Value) < 1 then
- Result := (Cos(Value * Pi) + 1) * 0.5;
- end;
- function TCosineKernel.GetWidth: TFloat;
- begin
- Result := 1;
- end;
- //------------------------------------------------------------------------------
- //
- // TSplineKernel
- //
- //------------------------------------------------------------------------------
- function TSplineKernel.Filter(Value: TFloat): TFloat;
- var
- tt: TFloat;
- const
- TwoThirds = 2 / 3;
- OneSixth = 1 / 6;
- begin
- Value := Abs(Value);
- if Value < 1 then
- begin
- tt := Sqr(Value);
- Result := 0.5 * tt * Value - tt + TwoThirds;
- end
- else if Value < 2 then
- begin
- Value := 2 - Value;
- Result := OneSixth * Sqr(Value) * Value;
- end
- else Result := 0;
- end;
- function TSplineKernel.RangeCheck: Boolean;
- begin
- Result := True;
- end;
- function TSplineKernel.GetWidth: TFloat;
- begin
- Result := 2;
- end;
- //------------------------------------------------------------------------------
- //
- // TMitchellKernel
- //
- //------------------------------------------------------------------------------
- function TMitchellKernel.Filter(Value: TFloat): TFloat;
- var
- tt, ttt: TFloat;
- const
- OneEighteenth = 1 / 18;
- begin
- Value := Abs(Value);
- tt := Sqr(Value);
- ttt := tt * Value;
- // Given B = C = 1/3
- if Value < 1 then
- // ((((12 - 9 * B - 6 * C) * ttt) + ((-18 + 12 * B + 6 * C) * tt) + (6 - 2 * B))) / 6
- Result := (21 * ttt - 36 * tt + 16 ) * OneEighteenth
- else
- if Value < 2 then
- // ((((-1 * B - 6 * C) * ttt) + ((6 * B + 30 * C) * tt) + ((-12 * B - 48 * C) * Value) + (8 * B + 24 * C))) / 6
- Result := (- 7 * ttt + 36 * tt - 60 * Value + 32) * OneEighteenth
- else
- Result := 0;
- end;
- function TMitchellKernel.RangeCheck: Boolean;
- begin
- Result := True;
- end;
- function TMitchellKernel.GetWidth: TFloat;
- begin
- Result := 2;
- end;
- //------------------------------------------------------------------------------
- //
- // TCubicKernel
- //
- //------------------------------------------------------------------------------
- constructor TCubicKernel.Create;
- begin
- FCoeff := -0.5;
- end;
- function TCubicKernel.Filter(Value: TFloat): TFloat;
- var
- tt, ttt: TFloat;
- begin
- Value := Abs(Value);
- tt := Sqr(Value);
- ttt := tt * Value;
- if Value <= 1 then
- Result := (FCoeff + 2) * ttt - (FCoeff + 3) * tt + 1
- else
- if Value < 2 then
- Result := FCoeff * (ttt - 5 * tt + 8 * Value - 4)
- else
- Result := 0;
- end;
- function TCubicKernel.RangeCheck: Boolean;
- begin
- Result := True;
- end;
- function TCubicKernel.GetWidth: TFloat;
- begin
- Result := 2;
- end;
- procedure TCubicKernel.SetCoeff(const Value: TFloat);
- begin
- if Value <> FCoeff then
- begin
- FCoeff := Value;
- Changed;
- end
- end;
- //------------------------------------------------------------------------------
- //
- // THermiteKernel
- //
- //------------------------------------------------------------------------------
- constructor THermiteKernel.Create;
- begin
- FBias := 0;
- FTension := 0;
- end;
- function THermiteKernel.Filter(Value: TFloat): TFloat;
- var
- Z: Integer;
- t, t2, t3, m0, m1, a0, a1, a2, a3: TFloat;
- begin
- t := (1 - FTension) * 0.5;
- m0 := (1 + FBias) * t;
- m1 := (1 - FBias) * t;
- Z := Floor(Value);
- t := Abs(Z - Value);
- t2 := t * t;
- t3 := t2 * t;
- a1 := t3 - 2 * t2 + t;
- a2 := t3 - t2;
- a3 := -2 * t3 + 3 * t2;
- a0 := -a3 + 1;
- case Z of
- -2: Result := a2 * m1;
- -1: Result := a3 + a1 * m1 + a2 * (m0 - m1);
- 0: Result := a0 + a1 * (m0 - m1) - a2 * m0;
- 1: Result := -a1 * m0;
- else
- Result := 0;
- end;
- end;
- function THermiteKernel.GetWidth: TFloat;
- begin
- Result := 2;
- end;
- function THermiteKernel.RangeCheck: Boolean;
- begin
- Result := True;
- end;
- procedure THermiteKernel.SetBias(const Value: TFloat);
- begin
- if FBias <> Value then
- begin
- FBias := Value;
- Changed;
- end;
- end;
- procedure THermiteKernel.SetTension(const Value: TFloat);
- begin
- if FTension <> Value then
- begin
- FTension := Value;
- Changed;
- end;
- end;
- //------------------------------------------------------------------------------
- //
- // TSinshKernel
- //
- //------------------------------------------------------------------------------
- constructor TSinshKernel.Create;
- begin
- FWidth := 3;
- FCoeff := 0.5;
- end;
- function TSinshKernel.Filter(Value: TFloat): TFloat;
- begin
- if Value = 0 then
- Result := 1
- else
- Result := FCoeff * Sin(Pi * Value) / Sinh(Pi * FCoeff * Value);
- end;
- function TSinshKernel.RangeCheck: Boolean;
- begin
- Result := True;
- end;
- procedure TSinshKernel.SetWidth(Value: TFloat);
- begin
- if FWidth <> Value then
- begin
- FWidth := Value;
- Changed;
- end;
- end;
- function TSinshKernel.GetWidth: TFloat;
- begin
- Result := FWidth;
- end;
- procedure TSinshKernel.SetCoeff(const Value: TFloat);
- begin
- if (FCoeff <> Value) and (FCoeff <> 0) then
- begin
- FCoeff := Value;
- Changed;
- end;
- end;
- //------------------------------------------------------------------------------
- //
- // TWindowedKernel
- //
- //------------------------------------------------------------------------------
- procedure TWindowedKernel.DoSetWidth(Value: TFloat);
- begin
- FWidth := Value;
- FWidthReciprocal := 1 / FWidth;
- end;
- function TWindowedKernel.Filter(Value: TFloat): TFloat;
- begin
- Value := Abs(Value);
- if Value < FWidth then
- Result := Window(Value)
- else
- Result := 0;
- end;
- function TWindowedKernel.RangeCheck: Boolean;
- begin
- Result := True;
- end;
- procedure TWindowedKernel.SetWidth(Value: TFloat);
- begin
- Value := Min(MAX_KERNEL_WIDTH, Value);
- if Value <> FWidth then
- begin
- DoSetWidth(Value);
- Changed;
- end;
- end;
- function TWindowedKernel.GetWidth: TFloat;
- begin
- Result := FWidth;
- end;
- //------------------------------------------------------------------------------
- //
- // TGaussianKernel
- //
- //------------------------------------------------------------------------------
- const
- // Because the gaussian function has inifinite extent we need to limit the
- // width of the window to something reasonable.
- // Often the limit (width) is set to "Full Width at Half Maximum" (FWHM) by
- // calculing the ratio between Radius and Sigma as
- //
- // Ratio = 1 / FWHM
- // = 1 / (2 * Sqrt(2 * Ln(2)))
- // = 0.424660891294479
- //
- // however, for resampling we need the area of the curve covered by the window
- // to be as close to 1 as possible so instead we calculate the ratio so that
- //
- // Ceil(Sigma / Ratio)
- //
- // gives us the smallest size of a kernel containing values >= 1/255:
- //
- // Ratio = 1 / Sqrt(-2 * Ln(1/255))
- // = 0.300386630413846
- //
- GaussianRadiusToSigma = 0.300386630413846;
- GaussianSigmaToRadius = 1 / GaussianRadiusToSigma;
- GaussianMinSigma = 0.4; // Sigma smaller than this causes overflow; Window(0) > 1
- constructor TGaussianKernel.Create;
- begin
- inherited;
- DoSetSigma(1 / Sqrt(2 * Pi));
- end;
- procedure TGaussianKernel.DoSetSigma(const Value: TFloat);
- begin
- FSigma := Value;
- FSigmaReciprocal := -0.5 / Sqr(FSigma);
- FNormalizationFactor := 1 / (FSigma * Sqrt(2 * Pi));
- DoSetWidth(FSigma * GaussianSigmaToRadius);
- end;
- procedure TGaussianKernel.SetSigma(const Value: TFloat);
- begin
- if (FSigma <> Value) and (FSigma <> 0) then
- begin
- DoSetSigma(Value);
- Changed;
- end;
- end;
- function TGaussianKernel.Window(Value: TFloat): TFloat;
- begin
- (*
- ** Gauss(x, σ) = 1/(σ √ 2π) * e^( - x^2 / (2 * σ^2))
- **
- ** FNormalizationFactor = 1/(σ √ 2π)
- **
- ** FSigmaReciprocal = - 1 / (2 * σ^2)
- *)
- Result := FNormalizationFactor * Exp(Sqr(Value) * FSigmaReciprocal);
- end;
- //------------------------------------------------------------------------------
- //
- // TWindowedSincKernel
- //
- //------------------------------------------------------------------------------
- class function TWindowedSincKernel.Sinc(Value: TFloat): TFloat;
- begin
- if Value <> 0 then
- begin
- Value := Value * Pi;
- Result := Sin(Value) / Value;
- end
- else Result := 1;
- end;
- constructor TWindowedSincKernel.Create;
- begin
- inherited;
- FWidth := 3;
- FWidthReciprocal := 1 / FWidth;
- end;
- function TWindowedSincKernel.Filter(Value: TFloat): TFloat;
- begin
- Value := Abs(Value);
- if Value < FWidth then
- Result := Sinc(Value) * Window(Value)
- else
- Result := 0;
- end;
- //------------------------------------------------------------------------------
- //
- // TAlbrechtKernel
- //
- //------------------------------------------------------------------------------
- constructor TAlbrechtKernel.Create;
- begin
- inherited;
- Terms := 7;
- end;
- procedure TAlbrechtKernel.SetTerms(Value: Integer);
- begin
- Value := Constrain(Value, 2, 11);
- if FTerms <> Value then
- begin
- FTerms := Value;
- case Value of
- 2 : Move(CAlbrecht2 [0], FCoefPointer[0], Value * SizeOf(Double));
- 3 : Move(CAlbrecht3 [0], FCoefPointer[0], Value * SizeOf(Double));
- 4 : Move(CAlbrecht4 [0], FCoefPointer[0], Value * SizeOf(Double));
- 5 : Move(CAlbrecht5 [0], FCoefPointer[0], Value * SizeOf(Double));
- 6 : Move(CAlbrecht6 [0], FCoefPointer[0], Value * SizeOf(Double));
- 7 : Move(CAlbrecht7 [0], FCoefPointer[0], Value * SizeOf(Double));
- 8 : Move(CAlbrecht8 [0], FCoefPointer[0], Value * SizeOf(Double));
- 9 : Move(CAlbrecht9 [0], FCoefPointer[0], Value * SizeOf(Double));
- 10 : Move(CAlbrecht10[0], FCoefPointer[0], Value * SizeOf(Double));
- 11 : Move(CAlbrecht11[0], FCoefPointer[0], Value * SizeOf(Double));
- end;
- Changed;
- end;
- end;
- function TAlbrechtKernel.Window(Value: TFloat): TFloat;
- var
- cs : Double;
- i : Integer;
- begin
- cs := Cos(Pi * Value * FWidthReciprocal);
- i := FTerms - 1;
- Result := FCoefPointer[i];
- while i > 0 do
- begin
- Dec(i);
- Result := Result * cs + FCoefPointer[i];
- end;
- end;
- //------------------------------------------------------------------------------
- //
- // TLanczosKernel
- //
- //------------------------------------------------------------------------------
- function TLanczosKernel.Window(Value: TFloat): TFloat;
- begin
- Result := Sinc(Value * FWidthReciprocal); // Get rid of division
- end;
- //------------------------------------------------------------------------------
- //
- // TBlackmanKernel
- //
- //------------------------------------------------------------------------------
- function TBlackmanKernel.Window(Value: TFloat): TFloat;
- begin
- Value := Cos(Pi * Value * FWidthReciprocal); // get rid of division
- Result := 0.34 + 0.5 * Value + 0.16 * sqr(Value);
- end;
- //------------------------------------------------------------------------------
- //
- // THannKernel
- //
- //------------------------------------------------------------------------------
- function THannKernel.Window(Value: TFloat): TFloat;
- begin
- Result := 0.5 + 0.5 * Cos(Pi * Value * FWidthReciprocal); // get rid of division
- end;
- //------------------------------------------------------------------------------
- //
- // THammingKernel
- //
- //------------------------------------------------------------------------------
- function THammingKernel.Window(Value: TFloat): TFloat;
- begin
- Result := 0.54 + 0.46 * Cos(Pi * Value * FWidthReciprocal); // get rid of division
- end;
- //------------------------------------------------------------------------------
- //
- // TKernelResampler
- //
- //------------------------------------------------------------------------------
- constructor TKernelResampler.Create;
- begin
- inherited;
- Kernel := TBoxKernel.Create;
- FTableSize := 32;
- end;
- destructor TKernelResampler.Destroy;
- begin
- FKernel.Free;
- inherited;
- end;
- function TKernelResampler.GetKernelClassName: string;
- begin
- Result := FKernel.ClassName;
- end;
- procedure TKernelResampler.SetKernelClassName(const Value: string);
- var
- KernelClass: TCustomKernelClass;
- NewKernel: TCustomKernel;
- begin
- if (Value <> '') and (FKernel.ClassName <> Value) and (KernelList <> nil) then
- begin
- KernelClass := KernelList.Find(Value);
- if (KernelClass <> nil) then
- begin
- NewKernel := KernelClass.Create;
- try
- SetKernel(NewKernel);
- except
- if (FKernel <> NewKernel) then
- NewKernel.Free;
- raise;
- end;
- end;
- end;
- end;
- procedure TKernelResampler.SetKernel(const Value: TCustomKernel);
- begin
- if (Value <> nil) and (FKernel <> Value) then
- begin
- FreeAndNil(FKernel);
- FKernel := Value;
- TCustomKernelAccess(FKernel).FObserver := Self;
- Changed;
- end;
- end;
- procedure TKernelResampler.Resample(Dst: TCustomBitmap32; DstRect,
- DstClip: TRect; Src: TCustomBitmap32; SrcRect: TRect; CombineOp: TDrawMode;
- CombineCallBack: TPixelCombineEvent);
- begin
- GR32_Resamplers.Resample(Dst, DstRect, DstClip, Src, SrcRect, FKernel, CombineOp, CombineCallBack);
- end;
- {$WARNINGS OFF}
- function TKernelResampler.GetSampleFloat(X, Y: TFloat): TColor32;
- var
- clX, clY: Integer;
- fracX, fracY: Integer;
- fracXS: TFloat absolute fracX;
- fracYS: TFloat absolute fracY;
- Filter: TFilterMethod;
- WrapProcVert: TWrapProcEx absolute Filter;
- WrapProcHorz: TWrapProcEx;
- Colors: PColor32EntryArray;
- KWidth, W, Wv, I, J, Incr, Dev: Integer;
- SrcP: PColor32Entry;
- C: TColor32Entry absolute SrcP;
- LoX, HiX, LoY, HiY, MappingY: Integer;
- HorzKernel, VertKernel: TKernelEntry;
- PHorzKernel, PVertKernel, FloorKernel, CeilKernel: PKernelEntry;
- HorzEntry, VertEntry: TBufferEntry;
- MappingX: TKernelEntry;
- Edge: Boolean;
- Alpha: integer;
- OuterPremultColorR, OuterPremultColorG, OuterPremultColorB: Byte;
- begin
- KWidth := Ceil(FKernel.GetWidth);
- clX := Ceil(X);
- clY := Ceil(Y);
- case PixelAccessMode of
- pamUnsafe, pamWrap:
- begin
- LoX := -KWidth; HiX := KWidth;
- LoY := -KWidth; HiY := KWidth;
- end;
- pamSafe, pamTransparentEdge:
- begin
- with ClipRect do
- begin
- if not ((clX < Left) or (clX > Right) or (clY < Top) or (clY > Bottom)) then
- begin
- Edge := False;
- if clX - KWidth < Left then
- begin
- LoX := Left - clX;
- Edge := True;
- end
- else
- LoX := -KWidth;
- if clX + KWidth >= Right then
- begin
- HiX := Right - clX - 1;
- Edge := True;
- end
- else
- HiX := KWidth;
- if clY - KWidth < Top then
- begin
- LoY := Top - clY;
- Edge := True;
- end
- else
- LoY := -KWidth;
- if clY + KWidth >= Bottom then
- begin
- HiY := Bottom - clY - 1;
- Edge := True;
- end
- else
- HiY := KWidth;
- end
- else
- begin
- if PixelAccessMode = pamTransparentEdge then
- Result := 0
- else
- Result := FOuterColor;
- Exit;
- end;
- end;
- end;
- end;
- case FKernelMode of
- kmDynamic:
- begin
- Filter := FKernel.Filter;
- fracXS := clX - X;
- fracYS := clY - Y;
- PHorzKernel := @HorzKernel;
- PVertKernel := @VertKernel;
- Dev := -256;
- for I := -KWidth to KWidth do
- begin
- W := Round(Filter(I + fracXS) * 256);
- HorzKernel[I] := W;
- Inc(Dev, W);
- end;
- Dec(HorzKernel[0], Dev);
- Dev := -256;
- for I := -KWidth to KWidth do
- begin
- W := Round(Filter(I + fracYS) * 256);
- VertKernel[I] := W;
- Inc(Dev, W);
- end;
- Dec(VertKernel[0], Dev);
- end;
- kmTableNearest:
- begin
- W := FWeightTable.Height - 2;
- PHorzKernel := @FWeightTable.ValPtr[KWidth - MAX_KERNEL_WIDTH, Round((clX - X) * W)]^;
- PVertKernel := @FWeightTable.ValPtr[KWidth - MAX_KERNEL_WIDTH, Round((clY - Y) * W)]^;
- end;
- kmTableLinear:
- begin
- W := (FWeightTable.Height - 2) * $10000;
- J := FWeightTable.Width * 4;
- with TFixedRec(FracX) do
- begin
- Fixed := Round((clX - X) * W);
- PHorzKernel := @HorzKernel;
- FloorKernel := @FWeightTable.ValPtr[KWidth - MAX_KERNEL_WIDTH, Int]^;
- CeilKernel := PKernelEntry(NativeUInt(FloorKernel) + J);
- Dev := -256;
- for I := -KWidth to KWidth do
- begin
- Wv := FloorKernel[I] + ((CeilKernel[I] - FloorKernel[I]) * Frac + $7FFF) div FixedOne;
- HorzKernel[I] := Wv;
- Inc(Dev, Wv);
- end;
- Dec(HorzKernel[0], Dev);
- end;
- with TFixedRec(FracY) do
- begin
- Fixed := Round((clY - Y) * W);
- PVertKernel := @VertKernel;
- FloorKernel := @FWeightTable.ValPtr[KWidth - MAX_KERNEL_WIDTH, Int]^;
- CeilKernel := PKernelEntry(NativeUInt(FloorKernel) + J);
- Dev := -256;
- for I := -KWidth to KWidth do
- begin
- Wv := FloorKernel[I] + ((CeilKernel[I] - FloorKernel[I]) * Frac + $7FFF) div FixedOne;
- VertKernel[I] := Wv;
- Inc(Dev, Wv);
- end;
- Dec(VertKernel[0], Dev);
- end;
- end;
- end;
- VertEntry := Default(TBufferEntry);
- case PixelAccessMode of
- pamUnsafe, pamSafe, pamTransparentEdge:
- begin
- SrcP := PColor32Entry(Bitmap.PixelPtr[LoX + clX, LoY + clY]);
- Incr := Bitmap.Width - (HiX - LoX) - 1;
- for I := LoY to HiY do
- begin
- Wv := PVertKernel[I];
- if Wv <> 0 then
- begin
- HorzEntry := Default(TBufferEntry);
- for J := LoX to HiX do
- begin
- // Alpha=0 should not contribute to sample.
- Alpha := SrcP.A;
- if (Alpha <> 0) then
- begin
- W := PHorzKernel[J];
- Inc(HorzEntry.A, Alpha * W);
- // Sample premultiplied values
- if (Alpha = 255) then
- begin
- Inc(HorzEntry.R, SrcP.R * W);
- Inc(HorzEntry.G, SrcP.G * W);
- Inc(HorzEntry.B, SrcP.B * W);
- end else
- begin
- Inc(HorzEntry.R, Integer(Div255(Alpha * SrcP.R)) * W);
- Inc(HorzEntry.G, Integer(Div255(Alpha * SrcP.G)) * W);
- Inc(HorzEntry.B, Integer(Div255(Alpha * SrcP.B)) * W);
- end;
- end;
- Inc(SrcP);
- end;
- Inc(VertEntry.A, HorzEntry.A * Wv);
- Inc(VertEntry.R, HorzEntry.R * Wv);
- Inc(VertEntry.G, HorzEntry.G * Wv);
- Inc(VertEntry.B, HorzEntry.B * Wv);
- end else Inc(SrcP, HiX - LoX + 1);
- Inc(SrcP, Incr);
- end;
- if (PixelAccessMode = pamSafe) and Edge then
- begin
- Alpha := TColor32Entry(FOuterColor).A;
- // Alpha=0 should not contribute to sample.
- if (Alpha <> 0) then
- begin
- // Sample premultiplied values
- OuterPremultColorR := Integer(Div255(Alpha * TColor32Entry(FOuterColor).R));
- OuterPremultColorG := Integer(Div255(Alpha * TColor32Entry(FOuterColor).G));
- OuterPremultColorB := Integer(Div255(Alpha * TColor32Entry(FOuterColor).B));
- for I := -KWidth to KWidth do
- begin
- Wv := PVertKernel[I];
- if Wv <> 0 then
- begin
- HorzEntry := Default(TBufferEntry);
- for J := -KWidth to KWidth do
- if (J < LoX) or (J > HiX) or (I < LoY) or (I > HiY) then
- begin
- W := PHorzKernel[J];
- Inc(HorzEntry.A, Alpha * W);
- Inc(HorzEntry.R, OuterPremultColorR * W);
- Inc(HorzEntry.G, OuterPremultColorG * W);
- Inc(HorzEntry.B, OuterPremultColorB * W);
- end;
- Inc(VertEntry.A, HorzEntry.A * Wv);
- Inc(VertEntry.R, HorzEntry.R * Wv);
- Inc(VertEntry.G, HorzEntry.G * Wv);
- Inc(VertEntry.B, HorzEntry.B * Wv);
- end;
- end
- end;
- end;
- end;
- pamWrap:
- begin
- WrapProcHorz := GetWrapProcEx(Bitmap.WrapMode, ClipRect.Left, ClipRect.Right - 1);
- WrapProcVert := GetWrapProcEx(Bitmap.WrapMode, ClipRect.Top, ClipRect.Bottom - 1);
- for I := -KWidth to KWidth do
- MappingX[I] := WrapProcHorz(clX + I, ClipRect.Left, ClipRect.Right - 1);
- for I := -KWidth to KWidth do
- begin
- Wv := PVertKernel[I];
- if Wv <> 0 then
- begin
- MappingY := WrapProcVert(clY + I, ClipRect.Top, ClipRect.Bottom - 1);
- Colors := PColor32EntryArray(Bitmap.ScanLine[MappingY]);
- HorzEntry := Default(TBufferEntry);
- for J := -KWidth to KWidth do
- begin
- C := Colors[MappingX[J]];
- Alpha := C.A;
- // Alpha=0 should not contribute to sample.
- if (Alpha <> 0) then
- begin
- W := PHorzKernel[J];
- Inc(HorzEntry.A, Alpha * W);
- // Sample premultiplied values
- if (Alpha = 255) then
- begin
- Inc(HorzEntry.R, C.R * W);
- Inc(HorzEntry.G, C.G * W);
- Inc(HorzEntry.B, C.B * W);
- end else
- begin
- Inc(HorzEntry.R, Div255(Alpha * C.R) * W);
- Inc(HorzEntry.G, Div255(Alpha * C.G) * W);
- Inc(HorzEntry.B, Div255(Alpha * C.B) * W);
- end;
- end;
- end;
- Inc(VertEntry.A, HorzEntry.A * Wv);
- Inc(VertEntry.R, HorzEntry.R * Wv);
- Inc(VertEntry.G, HorzEntry.G * Wv);
- Inc(VertEntry.B, HorzEntry.B * Wv);
- end;
- end;
- end;
- end;
- // Round and unpremultiply result
- with TColor32Entry(Result) do
- begin
- if FKernel.RangeCheck then
- begin
- A := Clamp(TFixedRec(Integer(VertEntry.A + FixedHalf)).Int);
- if (A = 255) then
- begin
- R := Clamp(TFixedRec(Integer(VertEntry.R + FixedHalf)).Int);
- G := Clamp(TFixedRec(Integer(VertEntry.G + FixedHalf)).Int);
- B := Clamp(TFixedRec(Integer(VertEntry.B + FixedHalf)).Int);
- end else
- if (A <> 0) then
- begin
- R := Clamp(TFixedRec(Integer(VertEntry.R + FixedHalf)).Int * 255 div A);
- G := Clamp(TFixedRec(Integer(VertEntry.G + FixedHalf)).Int * 255 div A);
- B := Clamp(TFixedRec(Integer(VertEntry.B + FixedHalf)).Int * 255 div A);
- end else
- begin
- R := 0;
- G := 0;
- B := 0;
- end;
- end
- else
- begin
- A := TFixedRec(Integer(VertEntry.A + FixedHalf)).Int;
- if (A = 255) then
- begin
- R := TFixedRec(Integer(VertEntry.R + FixedHalf)).Int;
- G := TFixedRec(Integer(VertEntry.G + FixedHalf)).Int;
- B := TFixedRec(Integer(VertEntry.B + FixedHalf)).Int;
- end else
- if (A <> 0) then
- begin
- R := TFixedRec(Integer(VertEntry.R + FixedHalf)).Int * 255 div A;
- G := TFixedRec(Integer(VertEntry.G + FixedHalf)).Int * 255 div A;
- B := TFixedRec(Integer(VertEntry.B + FixedHalf)).Int * 255 div A;
- end else
- begin
- R := 0;
- G := 0;
- B := 0;
- end;
- end;
- end;
- end;
- {$WARNINGS ON}
- function TKernelResampler.GetWidth: TFloat;
- begin
- Result := Kernel.GetWidth;
- end;
- procedure TKernelResampler.SetKernelMode(const Value: TKernelMode);
- begin
- if FKernelMode <> Value then
- begin
- FKernelMode := Value;
- Changed;
- end;
- end;
- procedure TKernelResampler.SetTableSize(Value: Integer);
- begin
- if Value < 2 then Value := 2;
- if FTableSize <> Value then
- begin
- FTableSize := Value;
- Changed;
- end;
- end;
- procedure TKernelResampler.FinalizeSampling;
- begin
- if FKernelMode in [kmTableNearest, kmTableLinear] then
- FWeightTable.Free;
- inherited;
- end;
- procedure TKernelResampler.PrepareSampling;
- var
- I, J, W, Weight, Dev: Integer;
- Fraction: TFloat;
- KernelPtr: PKernelEntry;
- begin
- inherited;
- FOuterColor := Bitmap.OuterColor;
- W := Ceil(FKernel.GetWidth);
- if FKernelMode in [kmTableNearest, kmTableLinear] then
- begin
- FWeightTable := TIntegerMap.Create(W * 2 + 1, FTableSize + 1);
- for I := 0 to FTableSize do
- begin
- Fraction := I / (FTableSize - 1);
- KernelPtr := @FWeightTable.ValPtr[W - MAX_KERNEL_WIDTH, I]^;
- Dev := - 256;
- for J := -W to W do
- begin
- Weight := Round(FKernel.Filter(J + Fraction) * 256);
- KernelPtr[J] := Weight;
- Inc(Dev, Weight);
- end;
- Dec(KernelPtr[0], Dev);
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- //
- // TNearestResampler
- //
- //------------------------------------------------------------------------------
- function TNearestResampler.GetSampleInt(X, Y: Integer): TColor32;
- begin
- Result := FGetSampleInt(X, Y);
- end;
- function TNearestResampler.GetSampleFixed(X, Y: TFixed): TColor32;
- begin
- Result := FGetSampleInt(FixedRound(X), FixedRound(Y));
- end;
- function TNearestResampler.GetSampleFloat(X, Y: TFloat): TColor32;
- begin
- Result := FGetSampleInt(Round(X), Round(Y));
- end;
- function TNearestResampler.GetWidth: TFloat;
- begin
- Result := 1;
- end;
- function TNearestResampler.GetPixelTransparentEdge(X,Y: Integer): TColor32;
- var
- I, J: Integer;
- begin
- with Bitmap, Bitmap.ClipRect do
- begin
- I := Clamp(X, Left, Right - 1);
- J := Clamp(Y, Top, Bottom - 1);
- Result := Pixel[I, J];
- if (I <> X) or (J <> Y) then
- Result := Result and $00FFFFFF;
- end;
- end;
- procedure TNearestResampler.PrepareSampling;
- begin
- inherited;
- case PixelAccessMode of
- pamUnsafe: FGetSampleInt := TCustomBitmap32Access(Bitmap).GetPixel;
- pamSafe: FGetSampleInt := TCustomBitmap32Access(Bitmap).GetPixelS;
- pamWrap: FGetSampleInt := TCustomBitmap32Access(Bitmap).GetPixelW;
- pamTransparentEdge: FGetSampleInt := GetPixelTransparentEdge;
- end;
- end;
- procedure TNearestResampler.Resample(
- Dst: TCustomBitmap32; DstRect: TRect; DstClip: TRect;
- Src: TCustomBitmap32; SrcRect: TRect;
- CombineOp: TDrawMode; CombineCallBack: TPixelCombineEvent);
- begin
- StretchNearest(Dst, DstRect, DstClip, Src, SrcRect, CombineOp, CombineCallBack)
- end;
- //------------------------------------------------------------------------------
- //
- // TLinearResampler
- //
- //------------------------------------------------------------------------------
- constructor TLinearResampler.Create;
- begin
- inherited;
- FLinearKernel := TLinearKernel.Create;
- end;
- destructor TLinearResampler.Destroy;
- begin
- FLinearKernel.Free;
- inherited Destroy;
- end;
- function TLinearResampler.GetSampleFixed(X, Y: TFixed): TColor32;
- begin
- Result := FGetSampleFixed(X, Y);
- end;
- function TLinearResampler.GetSampleFloat(X, Y: TFloat): TColor32;
- begin
- Result := FGetSampleFixed(Round(X * FixedOne), Round(Y * FixedOne));
- end;
- function TLinearResampler.GetPixelTransparentEdge(X, Y: TFixed): TColor32;
- var
- PixelX, PixelY, X1, X2, Y1, Y2, WeightX, EdgeX, EdgeY: TFixed;
- C1, C2, C3, C4: TColor32;
- PSrc: PColor32Array;
- begin
- EdgeX := Bitmap.ClipRect.Right - 1;
- EdgeY := Bitmap.ClipRect.Bottom - 1;
- PixelX := TFixedRec(X).Int;
- PixelY := TFixedRec(Y).Int;
- if (PixelX >= Bitmap.ClipRect.Left) and (PixelY >= Bitmap.ClipRect.Top) and (PixelX < EdgeX) and (PixelY < EdgeY) then
- begin //Safe
- Result := TCustomBitmap32Access(Bitmap).GET_T256(X shr 8, Y shr 8);
- end
- else
- if (PixelX >= Bitmap.ClipRect.Left - 1) and (PixelY >= Bitmap.ClipRect.Top - 1) and (PixelX <= EdgeX) and (PixelY <= EdgeY) then
- begin //Near edge, on edge or outside
- X1 := Clamp(PixelX, EdgeX);
- X2 := Clamp(PixelX + Sign(X), EdgeX);
- Y1 := Clamp(PixelY, EdgeY) * Bitmap.Width;
- Y2 := Clamp(PixelY + Sign(Y), EdgeY) * Bitmap.Width;
- PSrc := @Bitmap.Bits[0];
- C1 := PSrc[X1 + Y1];
- C2 := PSrc[X2 + Y1];
- C3 := PSrc[X1 + Y2];
- C4 := PSrc[X2 + Y2];
- if X <= Fixed(Bitmap.ClipRect.Left) then
- begin
- C1 := C1 and $00FFFFFF;
- C3 := C3 and $00FFFFFF;
- end else
- if PixelX = EdgeX then
- begin
- C2 := C2 and $00FFFFFF;
- C4 := C4 and $00FFFFFF;
- end;
- if Y <= Fixed(Bitmap.ClipRect.Top) then
- begin
- C1 := C1 and $00FFFFFF;
- C2 := C2 and $00FFFFFF;
- end else
- if PixelY = EdgeY then
- begin
- C3 := C3 and $00FFFFFF;
- C4 := C4 and $00FFFFFF;
- end;
- WeightX := ((X shr 8) and $FF) xor $FF;
- Result := CombineReg(CombineReg(C1, C2, WeightX),
- CombineReg(C3, C4, WeightX),
- ((Y shr 8) and $FF) xor $FF);
- end
- else
- Result := 0; //Nothing really makes sense here, return zero
- end;
- procedure TLinearResampler.PrepareSampling;
- begin
- inherited;
- case PixelAccessMode of
- pamUnsafe: FGetSampleFixed := TCustomBitmap32Access(Bitmap).GetPixelX;
- pamSafe: FGetSampleFixed := TCustomBitmap32Access(Bitmap).GetPixelXS;
- pamWrap: FGetSampleFixed := TCustomBitmap32Access(Bitmap).GetPixelXW;
- pamTransparentEdge: FGetSampleFixed := GetPixelTransparentEdge;
- end;
- end;
- function TLinearResampler.GetWidth: TFloat;
- begin
- Result := 1;
- end;
- procedure TLinearResampler.Resample(
- Dst: TCustomBitmap32; DstRect: TRect; DstClip: TRect;
- Src: TCustomBitmap32; SrcRect: TRect;
- CombineOp: TDrawMode; CombineCallBack: TPixelCombineEvent);
- var
- SrcW, SrcH: TFloat;
- DstW, DstH: Integer;
- begin
- SrcW := SrcRect.Right - SrcRect.Left;
- SrcH := SrcRect.Bottom - SrcRect.Top;
- DstW := DstRect.Right - DstRect.Left;
- DstH := DstRect.Bottom - DstRect.Top;
- if (DstW > SrcW) and (DstH > SrcH) and (SrcW > 1) and (SrcH > 1) then
- StretchHorzStretchVertLinear(Dst, DstRect, DstClip, Src, SrcRect, CombineOp,
- CombineCallBack)
- else
- GR32_Resamplers.Resample(Dst, DstRect, DstClip, Src, SrcRect, FLinearKernel,
- CombineOp, CombineCallBack);
- end;
- procedure TDraftResampler.Resample(
- Dst: TCustomBitmap32; DstRect: TRect; DstClip: TRect;
- Src: TCustomBitmap32; SrcRect: TRect;
- CombineOp: TDrawMode; CombineCallBack: TPixelCombineEvent);
- begin
- DraftResample(Dst, DstRect, DstClip, Src, SrcRect, FLinearKernel, CombineOp,
- CombineCallBack)
- end;
- //------------------------------------------------------------------------------
- //
- // TTransformer
- //
- //------------------------------------------------------------------------------
- function TTransformer.GetSampleInt(X, Y: Integer): TColor32;
- var
- U, V: TFixed;
- begin
- FTransformFixed(X * FixedOne + FixedHalf, Y * FixedOne + FixedHalf, U, V);
- Result := FGetSampleFixed(U - FixedHalf, V - FixedHalf);
- end;
- function TTransformer.GetSampleFixed(X, Y: TFixed): TColor32;
- var
- U, V: TFixed;
- begin
- FTransformFixed(X + FixedHalf, Y + FixedHalf, U, V);
- Result := FGetSampleFixed(U - FixedHalf, V - FixedHalf);
- end;
- function TTransformer.GetSampleFloat(X, Y: TFloat): TColor32;
- var
- U, V: TFloat;
- begin
- FTransformFloat(X + 0.5, Y + 0.5, U, V);
- Result := FGetSampleFloat(U - 0.5, V - 0.5);
- end;
- constructor TTransformer.Create(ASampler: TCustomSampler; ATransformation: TTransformation; AReverse: boolean);
- begin
- inherited Create(ASampler);
- FTransformation := ATransformation;
- FReverse := AReverse;
- end;
- procedure TTransformer.PrepareSampling;
- begin
- inherited;
- if (FTransformation = nil) then
- raise ETransformerException.Create(STransformationNil);
- if (FReverse) then
- begin
- FTransformInt := TTransformationAccess(FTransformation).ReverseTransformInt;
- FTransformFixed := TTransformationAccess(FTransformation).ReverseTransformFixed;
- FTransformFloat := TTransformationAccess(FTransformation).ReverseTransformFloat;
- end else
- begin
- FTransformInt := TTransformationAccess(FTransformation).TransformInt;
- FTransformFixed := TTransformationAccess(FTransformation).TransformFixed;
- FTransformFloat := TTransformationAccess(FTransformation).TransformFloat;
- end;
- if not TTransformationAccess(FTransformation).TransformValid then
- TTransformationAccess(FTransformation).PrepareTransform;
- end;
- function TTransformer.GetSampleBounds: TFloatRect;
- begin
- GR32.IntersectRect(Result, inherited GetSampleBounds, FTransformation.SrcRect);
- Result := FTransformation.GetTransformedBounds(Result);
- end;
- function TTransformer.HasBounds: Boolean;
- begin
- Result := FTransformation.HasTransformedBounds and inherited HasBounds;
- end;
- //------------------------------------------------------------------------------
- //
- // TSuperSampler
- //
- //------------------------------------------------------------------------------
- constructor TSuperSampler.Create(Sampler: TCustomSampler);
- begin
- inherited Create(Sampler);
- FSamplingX := 4;
- FSamplingY := 4;
- SamplingX := 4;
- SamplingY := 4;
- end;
- function TSuperSampler.GetSampleFixed(X, Y: TFixed): TColor32;
- var
- I, J: Integer;
- dX, dY, tX: TFixed;
- Buffer: TBufferEntry;
- begin
- Buffer := Default(TBufferEntry);
- tX := X + FOffsetX;
- Inc(Y, FOffsetY);
- dX := FDistanceX;
- dY := FDistanceY;
- for J := 1 to FSamplingY do
- begin
- X := tX;
- for I := 1 to FSamplingX do
- begin
- IncBuffer(Buffer, FGetSampleFixed(X, Y));
- Inc(X, dX);
- end;
- Inc(Y, dY);
- end;
- MultiplyBuffer(Buffer, FScale);
- Result := BufferToColor32(Buffer, 16);
- end;
- procedure TSuperSampler.SetSamplingX(const Value: TSamplingRange);
- begin
- FSamplingX := Value;
- FDistanceX := Fixed(1 / Value);
- FOffsetX := Fixed(((1 / Value) - 1) * 0.5); // replaced "/2" by "*0.5"
- FScale := Fixed(1 / (FSamplingX * FSamplingY));
- end;
- procedure TSuperSampler.SetSamplingY(const Value: TSamplingRange);
- begin
- FSamplingY := Value;
- FDistanceY := Fixed(1 / Value);
- FOffsetY := Fixed(((1 / Value) - 1) * 0.5); // replaced "/2" by "*0.5"
- FScale := Fixed(1 / (FSamplingX * FSamplingY));
- end;
- //------------------------------------------------------------------------------
- //
- // TAdaptiveSuperSampler
- //
- //------------------------------------------------------------------------------
- function TAdaptiveSuperSampler.CompareColors(C1, C2: TColor32): Boolean;
- var
- Diff: TColor32Entry;
- begin
- Diff.ARGB := ColorDifference(C1, C2);
- Result := FTolerance < Diff.R + Diff.G + Diff.B;
- end;
- constructor TAdaptiveSuperSampler.Create(Sampler: TCustomSampler);
- begin
- inherited Create(Sampler);
- Level := 4;
- Tolerance := 256;
- end;
- function TAdaptiveSuperSampler.DoRecurse(X, Y, Offset: TFixed; const A, B,
- C, D, E: TColor32): TColor32;
- var
- C1, C2, C3, C4: TColor32;
- begin
- C1 := QuadrantColor(A, E, X - Offset, Y - Offset, Offset, RecurseAC);
- C2 := QuadrantColor(B, E, X + Offset, Y - Offset, Offset, RecurseBD);
- C3 := QuadrantColor(E, C, X + Offset, Y + Offset, Offset, RecurseAC);
- C4 := QuadrantColor(E, D, X - Offset, Y + Offset, Offset, RecurseBD);
- Result := ColorAverage(ColorAverage(C1, C2), ColorAverage(C3, C4));
- end;
- function TAdaptiveSuperSampler.GetSampleFixed(X, Y: TFixed): TColor32;
- var
- A, B, C, D, E: TColor32;
- const
- FIXED_HALF = 32768;
- begin
- A := FGetSampleFixed(X - FIXED_HALF, Y - FIXED_HALF);
- B := FGetSampleFixed(X + FIXED_HALF, Y - FIXED_HALF);
- C := FGetSampleFixed(X + FIXED_HALF, Y + FIXED_HALF);
- D := FGetSampleFixed(X - FIXED_HALF, Y + FIXED_HALF);
- E := FGetSampleFixed(X, Y);
- Result := Self.DoRecurse(X, Y, 16384, A, B, C, D, E);
- end;
- function TAdaptiveSuperSampler.QuadrantColor(const C1, C2: TColor32; X, Y,
- Offset: TFixed; Proc: TRecurseProc): TColor32;
- begin
- if CompareColors(C1, C2) and (Offset >= FMinOffset) then
- Result := Proc(X, Y, Offset, C1, C2)
- else
- Result := ColorAverage(C1, C2);
- end;
- function TAdaptiveSuperSampler.RecurseAC(X, Y, Offset: TFixed; const A,
- C: TColor32): TColor32;
- var
- B, D, E: TColor32;
- begin
- B := FGetSampleFixed(X + Offset, Y - Offset);
- D := FGetSampleFixed(X - Offset, Y + Offset);
- E := FGetSampleFixed(X, Y);
- Result := DoRecurse(X, Y, Offset shr 1, A, B, C, D, E);
- end;
- function TAdaptiveSuperSampler.RecurseBD(X, Y, Offset: TFixed; const B,
- D: TColor32): TColor32;
- var
- A, C, E: TColor32;
- begin
- A := FGetSampleFixed(X - Offset, Y - Offset);
- C := FGetSampleFixed(X + Offset, Y + Offset);
- E := FGetSampleFixed(X, Y);
- Result := DoRecurse(X, Y, Offset shr 1, A, B, C, D, E);
- end;
- procedure TAdaptiveSuperSampler.SetLevel(const Value: Integer);
- begin
- FLevel := Value;
- FMinOffset := Fixed(1 / (1 shl Value));
- end;
- //------------------------------------------------------------------------------
- //
- // TPatternSampler
- //
- //------------------------------------------------------------------------------
- destructor TPatternSampler.Destroy;
- begin
- FPattern := nil;
- inherited;
- end;
- function TPatternSampler.GetSampleFixed(X, Y: TFixed): TColor32;
- var
- Points: TArrayOfFixedPoint;
- P: PFixedPoint;
- I, PY: Integer;
- Buffer: TBufferEntry;
- GetSample: TGetSampleFixed;
- WrapProcHorz: TWrapProc;
- begin
- GetSample := FSampler.GetSampleFixed;
- PY := WrapProcVert(TFixedRec(Y).Int, High(FPattern));
- I := High(FPattern[PY]);
- WrapProcHorz := GetOptimalWrap(I);
- Points := FPattern[PY][WrapProcHorz(TFixedRec(X).Int, I)];
- Buffer := Default(TBufferEntry);
- P := @Points[0];
- for I := 0 to High(Points) do
- begin
- IncBuffer(Buffer, GetSample(P.X + X, P.Y + Y));
- Inc(P);
- end;
- MultiplyBuffer(Buffer, FixedOne div Length(Points));
- Result := BufferToColor32(Buffer, 16);
- end;
- procedure TPatternSampler.SetPattern(const Value: TFixedSamplePattern);
- begin
- if (Value <> nil) then
- begin
- FPattern := Value;
- WrapProcVert := GetOptimalWrap(High(FPattern));
- end;
- end;
- //------------------------------------------------------------------------------
- //
- // CreateJitteredPattern
- //
- //------------------------------------------------------------------------------
- function JitteredPattern(XRes, YRes: Integer): TArrayOfFixedPoint;
- var
- I, J: Integer;
- begin
- SetLength(Result, XRes * YRes);
- for I := 0 to XRes - 1 do
- for J := 0 to YRes - 1 do
- with Result[I + J * XRes] do
- begin
- X := (Random(65536) + I * 65536) div XRes - 32768;
- Y := (Random(65536) + J * 65536) div YRes - 32768;
- end;
- end;
- function CreateJitteredPattern(TileWidth, TileHeight, SamplesX, SamplesY: Integer): TFixedSamplePattern;
- var
- I, J: Integer;
- begin
- SetLength(Result, TileHeight, TileWidth);
- for I := 0 to TileWidth - 1 do
- for J := 0 to TileHeight - 1 do
- Result[J][I] := JitteredPattern(SamplesX, SamplesY);
- end;
- //------------------------------------------------------------------------------
- //
- // TNestedSampler
- //
- //------------------------------------------------------------------------------
- procedure TNestedSampler.AssignTo(Dst: TPersistent);
- begin
- if Dst is TNestedSampler then
- SmartAssign(Self, Dst)
- else
- inherited;
- end;
- constructor TNestedSampler.Create(ASampler: TCustomSampler);
- begin
- inherited Create;
- Sampler := ASampler;
- end;
- procedure TNestedSampler.FinalizeSampling;
- begin
- if (FSampler = nil) then
- raise ENestedException.Create(SSamplerNil);
- FSampler.FinalizeSampling;
- end;
- {$WARNINGS OFF}
- function TNestedSampler.GetSampleBounds: TFloatRect;
- begin
- if (FSampler = nil) then
- raise ENestedException.Create(SSamplerNil);
- Result := FSampler.GetSampleBounds;
- end;
- function TNestedSampler.HasBounds: Boolean;
- begin
- if (FSampler = nil) then
- raise ENestedException.Create(SSamplerNil);
- Result := FSampler.HasBounds;
- end;
- {$WARNINGS ON}
- procedure TNestedSampler.PrepareSampling;
- begin
- if (FSampler = nil) then
- raise ENestedException.Create(SSamplerNil);
- FSampler.PrepareSampling;
- end;
- procedure TNestedSampler.SetSampler(const Value: TCustomSampler);
- begin
- FSampler := Value;
- if (Value <> nil) then
- begin
- FGetSampleInt := FSampler.GetSampleInt;
- FGetSampleFixed := FSampler.GetSampleFixed;
- FGetSampleFloat := FSampler.GetSampleFloat;
- end;
- end;
- //------------------------------------------------------------------------------
- //
- // TKernelSampler
- //
- //------------------------------------------------------------------------------
- function TKernelSampler.ConvertBuffer(var Buffer: TBufferEntry): TColor32;
- begin
- Buffer.A := Constrain(Buffer.A, 0, $FFFF);
- Buffer.R := Constrain(Buffer.R, 0, $FFFF);
- Buffer.G := Constrain(Buffer.G, 0, $FFFF);
- Buffer.B := Constrain(Buffer.B, 0, $FFFF);
- Result := BufferToColor32(Buffer, 8);
- end;
- constructor TKernelSampler.Create(ASampler: TCustomSampler);
- begin
- inherited;
- FKernel := TIntegerMap.Create;
- FStartEntry := Default(TBufferEntry);
- end;
- destructor TKernelSampler.Destroy;
- begin
- FKernel.Free;
- inherited;
- end;
- function TKernelSampler.GetSampleFixed(X, Y: TFixed): TColor32;
- var
- I, J: Integer;
- Buffer: TBufferEntry;
- begin
- X := X + FCenterX shl 16;
- Y := Y + FCenterY shl 16;
- Buffer := FStartEntry;
- for I := 0 to FKernel.Width - 1 do
- for J := 0 to FKernel.Height - 1 do
- UpdateBuffer(Buffer, FGetSampleFixed(X - I shl 16, Y - J shl 16), FKernel[I, J]);
- Result := ConvertBuffer(Buffer);
- end;
- function TKernelSampler.GetSampleInt(X, Y: Integer): TColor32;
- var
- I, J: Integer;
- Buffer: TBufferEntry;
- begin
- X := X + FCenterX;
- Y := Y + FCenterY;
- Buffer := FStartEntry;
- for I := 0 to FKernel.Width - 1 do
- for J := 0 to FKernel.Height - 1 do
- UpdateBuffer(Buffer, FGetSampleInt(X - I, Y - J), FKernel[I, J]);
- Result := ConvertBuffer(Buffer);
- end;
- procedure TKernelSampler.SetKernel(const Value: TIntegerMap);
- begin
- FKernel.Assign(Value);
- end;
- //------------------------------------------------------------------------------
- //
- // TConvolver
- //
- //------------------------------------------------------------------------------
- procedure TConvolver.UpdateBuffer(var Buffer: TBufferEntry; Color: TColor32;
- Weight: Integer);
- begin
- with TColor32Entry(Color) do
- begin
- Inc(Buffer.A, A * Weight);
- Inc(Buffer.R, R * Weight);
- Inc(Buffer.G, G * Weight);
- Inc(Buffer.B, B * Weight);
- end;
- end;
- //------------------------------------------------------------------------------
- //
- // TDilater
- //
- //------------------------------------------------------------------------------
- procedure TDilater.UpdateBuffer(var Buffer: TBufferEntry; Color: TColor32;
- Weight: Integer);
- begin
- with TColor32Entry(Color) do
- begin
- Buffer.A := Max(Buffer.A, A + Weight);
- Buffer.R := Max(Buffer.R, R + Weight);
- Buffer.G := Max(Buffer.G, G + Weight);
- Buffer.B := Max(Buffer.B, B + Weight);
- end;
- end;
- //------------------------------------------------------------------------------
- //
- // TEroder
- //
- //------------------------------------------------------------------------------
- constructor TEroder.Create(ASampler: TCustomSampler);
- const
- START_ENTRY: TBufferEntry = (B: $FFFF; G: $FFFF; R: $FFFF; A: $FFFF);
- begin
- inherited;
- FStartEntry := START_ENTRY;
- end;
- procedure TEroder.UpdateBuffer(var Buffer: TBufferEntry; Color: TColor32;
- Weight: Integer);
- begin
- with TColor32Entry(Color) do
- begin
- Buffer.A := Min(Buffer.A, A - Weight);
- Buffer.R := Min(Buffer.R, R - Weight);
- Buffer.G := Min(Buffer.G, G - Weight);
- Buffer.B := Min(Buffer.B, B - Weight);
- end;
- end;
- //------------------------------------------------------------------------------
- //
- // TExpander
- //
- //------------------------------------------------------------------------------
- procedure TExpander.UpdateBuffer(var Buffer: TBufferEntry; Color: TColor32;
- Weight: Integer);
- begin
- with TColor32Entry(Color) do
- begin
- Buffer.A := Max(Buffer.A, A * Weight);
- Buffer.R := Max(Buffer.R, R * Weight);
- Buffer.G := Max(Buffer.G, G * Weight);
- Buffer.B := Max(Buffer.B, B * Weight);
- end;
- end;
- //------------------------------------------------------------------------------
- //
- // TContracter
- //
- //------------------------------------------------------------------------------
- function TContracter.GetSampleFixed(X, Y: TFixed): TColor32;
- begin
- Result := ColorSub(FMaxWeight, inherited GetSampleFixed(X, Y));
- end;
- function TContracter.GetSampleInt(X, Y: Integer): TColor32;
- begin
- Result := ColorSub(FMaxWeight, inherited GetSampleInt(X, Y));
- end;
- procedure TContracter.PrepareSampling;
- var
- I, J, W: Integer;
- begin
- W := Low(Integer);
- for I := 0 to FKernel.Width - 1 do
- for J := 0 to FKernel.Height - 1 do
- W := Max(W, FKernel[I, J]);
- if W > 255 then
- W := 255;
- FMaxWeight := Gray32(W, W);
- end;
- procedure TContracter.UpdateBuffer(var Buffer: TBufferEntry; Color: TColor32;
- Weight: Integer);
- begin
- inherited UpdateBuffer(Buffer, Color xor $FFFFFFFF, Weight);
- end;
- //------------------------------------------------------------------------------
- //
- // TMorphologicalSampler
- //
- //------------------------------------------------------------------------------
- function TMorphologicalSampler.ConvertBuffer(
- var Buffer: TBufferEntry): TColor32;
- begin
- Buffer.A := Constrain(Buffer.A, 0, $FF);
- Buffer.R := Constrain(Buffer.R, 0, $FF);
- Buffer.G := Constrain(Buffer.G, 0, $FF);
- Buffer.B := Constrain(Buffer.B, 0, $FF);
- with TColor32Entry(Result) do
- begin
- A := Buffer.A;
- R := Buffer.R;
- G := Buffer.G;
- B := Buffer.B;
- end;
- end;
- //------------------------------------------------------------------------------
- //
- // TSelectiveConvolver
- //
- //------------------------------------------------------------------------------
- function TSelectiveConvolver.ConvertBuffer(var Buffer: TBufferEntry): TColor32;
- begin
- with TColor32Entry(Result) do
- begin
- A := Buffer.A div FWeightSum.A;
- R := Buffer.R div FWeightSum.R;
- G := Buffer.G div FWeightSum.G;
- B := Buffer.B div FWeightSum.B;
- end;
- end;
- constructor TSelectiveConvolver.Create(ASampler: TCustomSampler);
- begin
- inherited;
- FDelta := 30;
- end;
- function TSelectiveConvolver.GetSampleFixed(X, Y: TFixed): TColor32;
- begin
- FRefColor := FGetSampleFixed(X, Y);
- FWeightSum := Default(TBufferEntry);
- Result := inherited GetSampleFixed(X, Y);
- end;
- function TSelectiveConvolver.GetSampleInt(X, Y: Integer): TColor32;
- begin
- FRefColor := FGetSampleInt(X, Y);
- FWeightSum := Default(TBufferEntry);
- Result := inherited GetSampleInt(X, Y);
- end;
- procedure TSelectiveConvolver.UpdateBuffer(var Buffer: TBufferEntry;
- Color: TColor32; Weight: Integer);
- begin
- with TColor32Entry(Color) do
- begin
- if Abs(TColor32Entry(FRefColor).A - A) <= FDelta then
- begin
- Inc(Buffer.A, A * Weight);
- Inc(FWeightSum.A, Weight);
- end;
- if Abs(TColor32Entry(FRefColor).R - R) <= FDelta then
- begin
- Inc(Buffer.R, R * Weight);
- Inc(FWeightSum.R, Weight);
- end;
- if Abs(TColor32Entry(FRefColor).G - G) <= FDelta then
- begin
- Inc(Buffer.G, G * Weight);
- Inc(FWeightSum.G, Weight);
- end;
- if Abs(TColor32Entry(FRefColor).B - B) <= FDelta then
- begin
- Inc(Buffer.B, B * Weight);
- Inc(FWeightSum.B, Weight);
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- //
- // Registration routines
- //
- //------------------------------------------------------------------------------
- procedure RegisterResampler(ResamplerClass: TCustomResamplerClass);
- begin
- if (ResamplerList = nil) then
- ResamplerList := TCustomClassList<TCustomResamplerClass>.Create;
- ResamplerList.Add(ResamplerClass);
- end;
- procedure RegisterKernel(KernelClass: TCustomKernelClass);
- begin
- if (KernelList = nil) then
- KernelList := TCustomClassList<TCustomKernelClass>.Create;
- KernelList.Add(KernelClass);
- end;
- //------------------------------------------------------------------------------
- //
- // Bindings
- //
- //------------------------------------------------------------------------------
- var
- ResamplersRegistry: TFunctionRegistry;
- procedure RegisterBindings;
- begin
- ResamplersRegistry := NewRegistry('GR32_Resamplers bindings');
- ResamplersRegistry.RegisterBinding(@@BlockAverage, 'BlockAverage');
- ResamplersRegistry.RegisterBinding(@@Interpolator, 'Interpolator');
- ResamplersRegistry[@@BlockAverage].ADD(@BlockAverage_Pas, [isPascal]).Name := 'BlockAverage_Pas';
- ResamplersRegistry[@@Interpolator].ADD(@Interpolator_Pas, [isPascal]).Name := 'Interpolator_Pas';
- {$if (not defined(PUREPASCAL)) and (not defined(OMIT_SSE2))}
- ResamplersRegistry[@@BlockAverage].ADD(@BlockAverage_SSE2, [isSSE2]).Name := 'BlockAverage_SSE2';
- ResamplersRegistry[@@Interpolator].ADD(@Interpolator_SSE2, [isSSE2]).Name := 'Interpolator_SSE2';
- {$ifend}
- ResamplersRegistry.RebindAll;
- end;
- //------------------------------------------------------------------------------
- initialization
- RegisterBindings;
- { Register resamplers }
- RegisterResampler(TNearestResampler);
- RegisterResampler(TLinearResampler);
- RegisterResampler(TDraftResampler);
- RegisterResampler(TKernelResampler);
- { Register kernels }
- RegisterKernel(TBoxKernel);
- RegisterKernel(TLinearKernel);
- RegisterKernel(TCosineKernel);
- RegisterKernel(TSplineKernel);
- RegisterKernel(TCubicKernel);
- RegisterKernel(TMitchellKernel);
- RegisterKernel(TAlbrechtKernel);
- RegisterKernel(TLanczosKernel);
- RegisterKernel(TGaussianKernel);
- RegisterKernel(TBlackmanKernel);
- RegisterKernel(THannKernel);
- RegisterKernel(THammingKernel);
- RegisterKernel(TSinshKernel);
- RegisterKernel(THermiteKernel);
- finalization
- ResamplerList.Free;
- KernelList.Free;
- end.
|