12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183 |
- unit GR32_ColorGradients;
- (* ***** 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 Color Gradients for Graphics32 *
- * *
- * The Initial Developer of the Original Code is *
- * Angus Johnson *
- * Christian Budde *
- * Anders Melander *
- * *
- * Portions created by the Initial Developer are Copyright (C) 2008-2024 *
- * the Initial Developer. All Rights Reserved. *
- * *
- * ***** END LICENSE BLOCK *****************************************************)
- interface
- {$include GR32.inc}
- uses
- Types, Classes, SysUtils, Math,
- GR32,
- GR32_Polygons,
- GR32_VectorUtils,
- {$if defined(UseInlining)}
- // Needed in interface for inlining
- GR32_Blend,
- {$ifend}
- GR32_Bindings;
- //------------------------------------------------------------------------------
- //
- // TColor32LookupTable
- //
- //------------------------------------------------------------------------------
- type
- TColor32LookupTable = class(TPersistent)
- private
- FGradientLUT: PColor32Array;
- FOrder: Byte;
- FMask: Cardinal;
- FSize: Cardinal;
- FOnOrderChanged: TNotifyEvent;
- procedure SetOrder(const Value: Byte);
- function GetColor32(Index: Integer): TColor32; {$IFDEF USEINLINING} inline; {$ENDIF}
- procedure SetColor32(Index: Integer; const Value: TColor32);
- protected
- procedure OrderChanged;
- procedure AssignTo(Dest: TPersistent); override;
- public
- constructor Create(Order: Byte = 9); virtual;
- destructor Destroy; override;
- property Order: Byte read FOrder write SetOrder;
- property Size: Cardinal read FSize;
- property Mask: Cardinal read FMask;
- property Color32[Index: Integer]: TColor32 read GetColor32 write SetColor32;
- property Color32Ptr: PColor32Array read FGradientLUT;
- property OnOrderChanged: TNotifyEvent read FOnOrderChanged write FOnOrderChanged;
- end;
- //------------------------------------------------------------------------------
- //
- // TColor32Gradient
- //
- //------------------------------------------------------------------------------
- type
- TColor32GradientStop = record
- Offset: TFloat; //expected range between 0.0 and 1.0
- Color32: TColor32;
- end;
- TArrayOfColor32GradientStop = array of TColor32GradientStop;
- TColor32FloatPoint = record
- Point: TFloatPoint;
- Color32: TColor32;
- end;
- TArrayOfColor32FloatPoint = array of TColor32FloatPoint;
- TColor32Gradient = class(TInterfacedPersistent, IStreamPersist)
- private
- FGradientColors: TArrayOfColor32GradientStop;
- FOnGradientColorsChanged: TNotifyEvent;
- function GetGradientEntry(Index: Integer): TColor32GradientStop;
- function GetGradientCount: Integer; {$IFDEF USEINLINING}inline;{$ENDIF}
- function GetStartColor: TColor32;
- function GetEndColor: TColor32;
- procedure SetEndColor(const Value: TColor32);
- procedure SetStartColor(const Value: TColor32);
- protected
- procedure GradientColorsChanged; virtual;
- procedure AssignTo(Dest: TPersistent); override;
- public
- constructor Create(Color: TColor32); overload;
- constructor Create(StartColor, EndColor: TColor32); overload;
- constructor Create(const GradientColors: TArrayOfColor32GradientStop); overload;
- procedure LoadFromStream(Stream: TStream);
- procedure SaveToStream(Stream: TStream);
- procedure ClearColorStops; overload;
- procedure ClearColorStops(Color: TColor32); overload;
- procedure AddColorStop(Offset: TFloat; Color: TColor32); overload; virtual;
- procedure AddColorStop(ColorStop: TColor32GradientStop); overload; virtual;
- procedure SetColors(const GradientColors: array of const); overload;
- procedure SetColors(const GradientColors: TArrayOfColor32GradientStop); overload;
- procedure SetColors(const GradientColors: TArrayOfColor32); overload;
- procedure SetColors(const Palette: TPalette32); overload;
- function GetColorAt(Offset: TFloat): TColor32;
- procedure FillColorLookUpTable(var ColorLUT: array of TColor32); overload;
- procedure FillColorLookUpTable(ColorLUT: PColor32Array; Count: Integer); overload;
- procedure FillColorLookUpTable(ColorLUT: TColor32LookupTable); overload;
- property GradientEntry[Index: Integer]: TColor32GradientStop read GetGradientEntry;
- property GradientCount: Integer read GetGradientCount;
- property StartColor: TColor32 read GetStartColor write SetStartColor;
- property EndColor: TColor32 read GetEndColor write SetEndColor;
- property OnGradientColorsChanged: TNotifyEvent read FOnGradientColorsChanged write FOnGradientColorsChanged;
- end;
- //------------------------------------------------------------------------------
- //
- // TCustomSparsePointGradientSampler
- //
- //------------------------------------------------------------------------------
- type
- TCustomSparsePointGradientSampler = class(TCustomSampler)
- protected
- function GetCount: Integer; virtual; abstract;
- function GetColor(Index: Integer): TColor32; virtual; abstract;
- function GetPoint(Index: Integer): TFloatPoint; virtual; abstract;
- function GetColorPoint(Index: Integer): TColor32FloatPoint; virtual; abstract;
- procedure SetColor(Index: Integer; const Value: TColor32); virtual; abstract;
- procedure SetColorPoint(Index: Integer; const Value: TColor32FloatPoint); virtual; abstract;
- procedure SetPoint(Index: Integer; const Value: TFloatPoint); virtual; abstract;
- public
- function GetSampleFixed(X, Y: TFixed): TColor32; override;
- function GetSampleInt(X, Y: Integer): TColor32; override;
- procedure SetPoints(Points: TArrayOfFloatPoint); virtual; abstract;
- procedure SetColorPoints(ColorPoints: TArrayOfColor32FloatPoint); overload; virtual; abstract;
- procedure SetColorPoints(Points: TArrayOfFloatPoint; Colors: TArrayOfColor32); overload; virtual; abstract;
- property Color[Index: Integer]: TColor32 read GetColor write SetColor;
- property Point[Index: Integer]: TFloatPoint read GetPoint write SetPoint;
- property ColorPoint[Index: Integer]: TColor32FloatPoint read GetColorPoint write SetColorPoint;
- property Count: Integer read GetCount;
- end;
- //------------------------------------------------------------------------------
- //
- // TBarycentricGradientSampler
- //
- //------------------------------------------------------------------------------
- type
- TBarycentricGradientSampler = class(TCustomSparsePointGradientSampler)
- protected
- FColorPoints: array [0 .. 2] of TColor32FloatPoint;
- FDists: array [0 .. 1] of TFloatPoint;
- function GetCount: Integer; override;
- function GetColor(Index: Integer): TColor32; override;
- function GetColorPoint(Index: Integer): TColor32FloatPoint; override;
- function GetPoint(Index: Integer): TFloatPoint; override;
- procedure SetColor(Index: Integer; const Value: TColor32); override;
- procedure SetColorPoint(Index: Integer; const Value: TColor32FloatPoint); override;
- procedure SetPoint(Index: Integer; const Value: TFloatPoint); override;
- procedure AssignTo(Dest: TPersistent); override;
- procedure CalculateBarycentricCoordinates(X, Y: TFloat; out U, V, W: TFloat); {$IFDEF USEINLINING} inline; {$ENDIF}
- public
- constructor Create(P1, P2, P3: TColor32FloatPoint); overload; virtual;
- function IsPointInTriangle(X, Y: TFloat): Boolean; overload;
- function IsPointInTriangle(const Point: TFloatPoint): Boolean; overload;
- procedure SetPoints(Points: TArrayOfFloatPoint); override;
- procedure SetColorPoints(ColorPoints: TArrayOfColor32FloatPoint); override;
- procedure SetColorPoints(Points: TArrayOfFloatPoint; Colors: TArrayOfColor32); override;
- procedure PrepareSampling; override;
- function GetSampleFloat(X, Y: TFloat): TColor32; override;
- function GetSampleFloatInTriangle(X, Y: TFloat): TColor32;
- end;
- //------------------------------------------------------------------------------
- //
- // TBilinearGradientSampler
- //
- //------------------------------------------------------------------------------
- type
- TBilinearGradientSampler = class(TCustomSparsePointGradientSampler)
- protected
- FColorPoints: array [0 .. 3] of TColor32FloatPoint;
- FDists: array [0 .. 2] of TFloatPoint;
- FDot: TFloat;
- FBiasK0: TFloat;
- FBiasU: TFloat;
- FK2Sign: Integer;
- FK2Value: TFloat;
- function GetCount: Integer; override;
- function GetColor(Index: Integer): TColor32; override;
- function GetColorPoint(Index: Integer): TColor32FloatPoint; override;
- function GetPoint(Index: Integer): TFloatPoint; override;
- procedure SetColor(Index: Integer; const Value: TColor32); override;
- procedure SetColorPoint(Index: Integer; const Value: TColor32FloatPoint); override;
- procedure SetPoint(Index: Integer; const Value: TFloatPoint); override;
- procedure AssignTo(Dest: TPersistent); override;
- public
- procedure SetPoints(Points: TArrayOfFloatPoint); override;
- procedure SetColorPoints(ColorPoints: TArrayOfColor32FloatPoint); override;
- procedure SetColorPoints(Points: TArrayOfFloatPoint; Colors: TArrayOfColor32); override;
- procedure PrepareSampling; override;
- function GetSampleFloat(X, Y: TFloat): TColor32; override;
- end;
- //------------------------------------------------------------------------------
- //
- // TCustomArbitrarySparsePointGradientSampler
- //
- //------------------------------------------------------------------------------
- type
- TCustomArbitrarySparsePointGradientSampler = class(TCustomSparsePointGradientSampler)
- private
- FColorPoints: TArrayOfColor32FloatPoint;
- protected
- procedure AssignTo(Dest: TPersistent); override;
- function GetCount: Integer; override;
- function GetColor(Index: Integer): TColor32; override;
- function GetColorPoint(Index: Integer): TColor32FloatPoint; override;
- function GetPoint(Index: Integer): TFloatPoint; override;
- procedure SetColor(Index: Integer; const Value: TColor32); override;
- procedure SetColorPoint(Index: Integer; const Value: TColor32FloatPoint); override;
- procedure SetPoint(Index: Integer; const Value: TFloatPoint); override;
- public
- procedure Add(Point: TFloatPoint; Color: TColor32); overload; virtual;
- procedure Add(const ColorPoint: TColor32FloatPoint); overload; virtual;
- procedure SetColorPoints(ColorPoints: TArrayOfColor32FloatPoint); override;
- procedure SetColorPoints(Points: TArrayOfFloatPoint; Colors: TArrayOfColor32); override;
- procedure SetPoints(Points: TArrayOfFloatPoint); override;
- procedure Clear; virtual;
- end;
- //------------------------------------------------------------------------------
- //
- // TInvertedDistanceWeightingSampler
- //
- //------------------------------------------------------------------------------
- type
- TInvertedDistanceWeightingSampler = class(TCustomArbitrarySparsePointGradientSampler)
- private
- FDists: TArrayOfFloat;
- FUsePower: Boolean;
- FPower: TFloat;
- FScaledPower: TFloat;
- public
- constructor Create; virtual;
- procedure PrepareSampling; override;
- procedure FinalizeSampling; override;
- function GetSampleFloat(X, Y: TFloat): TColor32; override;
- property Power: TFloat read FPower write FPower;
- end;
- //------------------------------------------------------------------------------
- //
- // TVoronoiSampler
- //
- //------------------------------------------------------------------------------
- type
- TVoronoiMetric = (vmEuclidean, vmManhattan, vmCustom);
- TVoronoiMetricFunc = function (X, Y: TFloat; Point: TFloatPoint): TFloat;
- TVoronoiSampler = class(TCustomArbitrarySparsePointGradientSampler)
- private
- FMetric: TVoronoiMetric;
- FMetricFunc: TVoronoiMetricFunc;
- procedure SetMetric(const Value: TVoronoiMetric);
- procedure MetricChanged;
- procedure SetMetricFunc(const Value: TVoronoiMetricFunc);
- public
- constructor Create(Metric: TVoronoiMetric = vmEuclidean); virtual;
- function GetSampleFloat(X, Y: TFloat): TColor32; override;
- property Metric: TVoronoiMetric read FMetric write SetMetric;
- property MetricFunc: TVoronoiMetricFunc read FMetricFunc write SetMetricFunc;
- end;
- //------------------------------------------------------------------------------
- //
- // TGourandShadedDelaunayTrianglesSampler
- //
- //------------------------------------------------------------------------------
- type
- TGourandShadedDelaunayTrianglesSampler = class(TCustomArbitrarySparsePointGradientSampler)
- private
- FTriangles: TArrayOfTriangleVertexIndices;
- FBarycentric: array of TBarycentricGradientSampler;
- public
- procedure PrepareSampling; override;
- procedure FinalizeSampling; override;
- function GetSampleFloat(X, Y: TFloat): TColor32; override;
- end;
- //------------------------------------------------------------------------------
- //
- // TCustomGradientSampler
- //
- //------------------------------------------------------------------------------
- type
- TCustomGradientSampler = class(TCustomSampler)
- private
- FGradient: TColor32Gradient;
- FWrapMode: TWrapMode;
- procedure SetGradient(const Value: TColor32Gradient);
- procedure SetWrapMode(const Value: TWrapMode);
- protected
- FInitialized: Boolean;
- procedure AssignTo(Dest: TPersistent); override;
- procedure GradientChangedHandler(Sender: TObject);
- procedure GradientSamplerChanged; //de-initializes sampler
- procedure WrapModeChanged; virtual;
- procedure UpdateInternals; virtual; abstract;
- property Initialized: Boolean read FInitialized;
- public
- constructor Create(WrapMode: TWrapMode = wmMirror); overload; virtual;
- constructor Create(ColorGradient: TColor32Gradient); overload; virtual;
- destructor Destroy; override;
- procedure PrepareSampling; override;
- function GetSampleInt(X, Y: Integer): TColor32; override;
- function GetSampleFixed(X, Y: TFixed): TColor32; override;
- property Gradient: TColor32Gradient read FGradient write SetGradient;
- property WrapMode: TWrapMode read FWrapMode write SetWrapMode;
- end;
- //------------------------------------------------------------------------------
- //
- // TCustomGradientLookUpTableSampler
- //
- //------------------------------------------------------------------------------
- type
- TCustomGradientLookUpTableSampler = class(TCustomGradientSampler)
- private
- FGradientLUT: TColor32LookupTable;
- FLutPtr: PColor32Array;
- FLutMask: Integer;
- FWrapProc: TWrapProc;
- protected
- procedure AssignTo(Dest: TPersistent); override;
- procedure WrapModeChanged; override;
- procedure UpdateInternals; override;
- property LutPtr: PColor32Array read FLutPtr;
- property LutMask: Integer read FLutMask;
- property WrapProc: TWrapProc read FWrapProc;
- public
- constructor Create(WrapMode: TWrapMode = wmMirror); override;
- destructor Destroy; override;
- end;
- //------------------------------------------------------------------------------
- //
- // TCustomCenterLutGradientSampler
- //
- //------------------------------------------------------------------------------
- type
- TCustomCenterLutGradientSampler = class(TCustomGradientLookUpTableSampler)
- private
- FCenter: TFloatPoint;
- protected
- procedure AssignTo(Dest: TPersistent); override;
- procedure Transform(var X, Y: TFloat); virtual;
- public
- constructor Create(WrapMode: TWrapMode = wmMirror); override;
- property Center: TFloatPoint read FCenter write FCenter;
- end;
- //------------------------------------------------------------------------------
- //
- // TConicGradientSampler
- //
- //------------------------------------------------------------------------------
- type
- TConicGradientSampler = class(TCustomCenterLutGradientSampler)
- private
- FScale: TFloat;
- FAngle: TFloat;
- protected
- procedure AssignTo(Dest: TPersistent); override;
- procedure UpdateInternals; override;
- public
- function GetSampleFloat(X, Y: TFloat): TColor32; override;
- property Angle: TFloat read FAngle write FAngle;
- end;
- //------------------------------------------------------------------------------
- //
- // TCustomCenterRadiusLutGradientSampler
- //
- //------------------------------------------------------------------------------
- type
- TCustomCenterRadiusLutGradientSampler = class(TCustomCenterLutGradientSampler)
- private
- FRadius: TFloat;
- procedure SetRadius(const Value: TFloat);
- protected
- procedure AssignTo(Dest: TPersistent); override;
- procedure RadiusChanged; virtual;
- public
- constructor Create(WrapMode: TWrapMode = wmMirror); override;
- property Radius: TFloat read FRadius write SetRadius;
- end;
- //------------------------------------------------------------------------------
- //
- // TRadialGradientSampler
- //
- //------------------------------------------------------------------------------
- type
- TRadialGradientSampler = class(TCustomCenterRadiusLutGradientSampler)
- private
- FScale: TFloat;
- protected
- procedure UpdateInternals; override;
- public
- function GetSampleFloat(X, Y: TFloat): TColor32; override;
- end;
- //------------------------------------------------------------------------------
- //
- // TRadialExGradientSampler
- //
- //------------------------------------------------------------------------------
- // HTML5 radial gradient.
- // Provides gradation of colors along a cylinder defined by two circles.
- // Also knows as "two point conical gradient"
- //------------------------------------------------------------------------------
- //
- // References:
- // - HTML Canvas Radial Gradients, The createRadialGradient() Method
- // https://www.w3schools.com/graphics/canvas_radial_gradients.asp
- //
- // - Microsoft Typography, COLR — Color Table, Graphic compositions, Gradients, Radial gradients
- // https://learn.microsoft.com/en-us/typography/opentype/spec/colr#radial-gradients
- //
- //------------------------------------------------------------------------------
- type
- TRadialExGradientSampler = class(TCustomGradientLookUpTableSampler)
- private
- FCenter1: TFloatPoint;
- FRadius1: TFloat;
- FCenter2: TFloatPoint;
- FRadius2: TFloat;
- FDeltaCenter: TFloatPoint;
- FDeltaRadius: TFloat;
- FPolynomA: TFloat;
- FPolynomAInv: TFLoat;
- FRadius12: TFloat;
- FRadius1Sqr: TFloat;
- procedure SetRadius1(const Value: TFloat);
- procedure SetRadius2(const Value: TFloat);
- procedure SetCenter1(const Value: TFloatPoint);
- procedure SetCenter2(const Value: TFloatPoint);
- protected
- procedure AssignTo(Dest: TPersistent); override;
- procedure UpdateInternals; override;
- procedure Transform(var X, Y: TFloat); virtual;
- public
- constructor Create(WrapMode: TWrapMode = wmMirror); override;
- function GetSampleFloat(X, Y: TFloat): TColor32; override;
- // Center and radius of start circle
- property Center1: TFloatPoint read FCenter1 write SetCenter1;
- property Radius1: TFloat read FRadius1 write SetRadius1;
- // Center and radius of end circle
- property Center2: TFloatPoint read FCenter2 write SetCenter2;
- property Radius2: TFloat read FRadius2 write SetRadius2;
- end;
- //------------------------------------------------------------------------------
- //
- // TCustomCenterRadiusAngleLutGradientSampler
- //
- //------------------------------------------------------------------------------
- type
- TCustomCenterRadiusAngleLutGradientSampler = class(TCustomCenterRadiusLutGradientSampler)
- private
- FAngle: TFloat;
- FSinCos: TFloatPoint;
- procedure SetAngle(const Value: TFloat);
- protected
- procedure AssignTo(Dest: TPersistent); override;
- procedure AngleChanged; virtual;
- procedure RadiusChanged; override;
- procedure Transform(var X, Y: TFloat); override;
- public
- constructor Create(WrapMode: TWrapMode = wmMirror); override;
- property Angle: TFloat read FAngle write SetAngle;
- end;
- //------------------------------------------------------------------------------
- //
- // TDiamondGradientSampler
- //
- //------------------------------------------------------------------------------
- type
- TDiamondGradientSampler = class(TCustomCenterRadiusAngleLutGradientSampler)
- private
- FScale: TFloat;
- protected
- procedure UpdateInternals; override;
- public
- function GetSampleFloat(X, Y: TFloat): TColor32; override;
- end;
- //------------------------------------------------------------------------------
- //
- // TXGradientSampler
- // TLinearGradientSampler
- //
- //------------------------------------------------------------------------------
- type
- TXGradientSampler = class(TCustomCenterRadiusAngleLutGradientSampler)
- private
- FScale: TFloat;
- function GetEndPoint: TFloatPoint;
- function GetStartPoint: TFloatPoint;
- procedure SetEndPoint(const Value: TFloatPoint);
- procedure SetStartPoint(const Value: TFloatPoint);
- protected
- procedure UpdateInternals; override;
- public
- procedure SimpleGradient(const StartPoint: TFloatPoint; StartColor: TColor32;
- const EndPoint: TFloatPoint; EndColor: TColor32); virtual;
- procedure SetPoints(const StartPoint, EndPoint: TFloatPoint); virtual;
- function GetSampleFloat(X, Y: TFloat): TColor32; override;
- public
- property StartPoint: TFloatPoint read GetStartPoint write SetStartPoint;
- property EndPoint: TFloatPoint read GetEndPoint write SetEndPoint;
- end;
- type
- TLinearGradientSampler = class(TXGradientSampler);
- //------------------------------------------------------------------------------
- //
- // TXYGradientSampler
- //
- //------------------------------------------------------------------------------
- type
- TXYGradientSampler = class(TCustomCenterRadiusAngleLutGradientSampler)
- private
- FScale: TFloat;
- protected
- procedure UpdateInternals; override;
- public
- function GetSampleFloat(X, Y: TFloat): TColor32; override;
- end;
- //------------------------------------------------------------------------------
- //
- // TXYSqrtGradientSampler
- //
- //------------------------------------------------------------------------------
- type
- TXYSqrtGradientSampler = class(TCustomCenterRadiusAngleLutGradientSampler)
- private
- FScale: TFloat;
- protected
- procedure UpdateInternals; override;
- public
- function GetSampleFloat(X, Y: TFloat): TColor32; override;
- end;
- //------------------------------------------------------------------------------
- //
- // TCustomSparsePointGradientPolygonFiller
- //
- //------------------------------------------------------------------------------
- type
- TCustomSparsePointGradientPolygonFiller = class(TCustomPolygonFiller)
- protected
- function GetCount: Integer; virtual; abstract;
- function GetColor(Index: Integer): TColor32; virtual; abstract;
- function GetPoint(Index: Integer): TFloatPoint; virtual; abstract;
- function GetColorPoint(Index: Integer): TColor32FloatPoint; virtual; abstract;
- procedure SetColor(Index: Integer; const Value: TColor32); virtual; abstract;
- procedure SetColorPoint(Index: Integer; const Value: TColor32FloatPoint); virtual; abstract;
- procedure SetPoint(Index: Integer; const Value: TFloatPoint); virtual; abstract;
- public
- procedure SetPoints(Points: TArrayOfFloatPoint); virtual; abstract;
- procedure SetColorPoints(ColorPoints: TArrayOfColor32FloatPoint); overload; virtual; abstract;
- procedure SetColorPoints(Points: TArrayOfFloatPoint; Colors: TArrayOfColor32); overload; virtual; abstract;
- property Color[Index: Integer]: TColor32 read GetColor write SetColor;
- property Point[Index: Integer]: TFloatPoint read GetPoint write SetPoint;
- property ColorPoint[Index: Integer]: TColor32FloatPoint read GetColorPoint write SetColorPoint;
- property Count: Integer read GetCount;
- end;
- //------------------------------------------------------------------------------
- //
- // TBarycentricGradientPolygonFiller
- //
- //------------------------------------------------------------------------------
- type
- TBarycentricGradientPolygonFiller = class(TCustomSparsePointGradientPolygonFiller)
- protected
- FColorPoints: array [0 .. 2] of TColor32FloatPoint;
- FDists: array [0 .. 1] of TFloatPoint;
- function GetCount: Integer; override;
- function GetColor(Index: Integer): TColor32; override;
- function GetPoint(Index: Integer): TFloatPoint; override;
- function GetColorPoint(Index: Integer): TColor32FloatPoint; override;
- procedure SetColor(Index: Integer; const Value: TColor32); override;
- procedure SetColorPoint(Index: Integer; const Value: TColor32FloatPoint); override;
- procedure SetPoint(Index: Integer; const Value: TFloatPoint); override;
- function GetFillLine: TFillLineEvent; override;
- procedure FillLine(Dst: PColor32; DstX, DstY, Length: Integer;
- AlphaValues: PColor32; CombineMode: TCombineMode);
- class function Linear3PointInterpolation(A, B, C: TColor32;
- WeightA, WeightB, WeightC: Single): TColor32;
- public
- procedure BeginRendering; override;
- procedure SetPoints(Points: TArrayOfFloatPoint); override;
- procedure SetColorPoints(ColorPoints: TArrayOfColor32FloatPoint); overload; override;
- procedure SetColorPoints(Points: TArrayOfFloatPoint; Colors: TArrayOfColor32); overload; override;
- end;
- //------------------------------------------------------------------------------
- //
- // TCustomArbitrarySparsePointGradientPolygonFiller
- //
- //------------------------------------------------------------------------------
- type
- TCustomArbitrarySparsePointGradientPolygonFiller = class(TCustomSparsePointGradientPolygonFiller)
- private
- FColorPoints: TArrayOfColor32FloatPoint;
- protected
- function GetCount: Integer; override;
- function GetColor(Index: Integer): TColor32; override;
- function GetColorPoint(Index: Integer): TColor32FloatPoint; override;
- function GetPoint(Index: Integer): TFloatPoint; override;
- procedure SetColor(Index: Integer; const Value: TColor32); override;
- procedure SetColorPoint(Index: Integer; const Value: TColor32FloatPoint); override;
- procedure SetPoint(Index: Integer; const Value: TFloatPoint); override;
- public
- procedure Add(const Point: TFloatPoint; Color: TColor32); overload; virtual;
- procedure Add(const ColorPoint: TColor32FloatPoint); overload; virtual;
- procedure SetColorPoints(ColorPoints: TArrayOfColor32FloatPoint); override;
- procedure SetColorPoints(Points: TArrayOfFloatPoint; Colors: TArrayOfColor32); override;
- procedure SetPoints(Points: TArrayOfFloatPoint); override;
- procedure Clear; virtual;
- end;
- //------------------------------------------------------------------------------
- //
- // TGourandShadedDelaunayTrianglesPolygonFiller
- //
- //------------------------------------------------------------------------------
- type
- TGourandShadedDelaunayTrianglesPolygonFiller = class(TCustomArbitrarySparsePointGradientPolygonFiller)
- private
- FTriangles: TArrayOfTriangleVertexIndices;
- FBarycentric: array of TBarycentricGradientSampler;
- protected
- function GetFillLine: TFillLineEvent; override;
- procedure FillLine3(Dst: PColor32; DstX, DstY, Count: Integer;
- AlphaValues: PColor32; CombineMode: TCombineMode);
- procedure FillLine(Dst: PColor32; DstX, DstY, Count: Integer;
- AlphaValues: PColor32; CombineMode: TCombineMode);
- public
- procedure BeginRendering; override;
- end;
- //------------------------------------------------------------------------------
- //
- // TCustomGradientPolygonFiller
- //
- //------------------------------------------------------------------------------
- type
- TCustomGradientPolygonFiller = class(TCustomPolygonFiller)
- private
- FGradient: TColor32Gradient;
- FOwnsGradient: Boolean;
- FWrapMode: TWrapMode;
- FWrapProc: TWrapProc;
- procedure SetWrapMode(const Value: TWrapMode);
- protected
- procedure GradientColorsChangedHandler(Sender: TObject);
- procedure FillLineNone(Dst: PColor32; DstX, DstY, Length: Integer;
- AlphaValues: PColor32; CombineMode: TCombineMode);
- procedure FillLineSolid(Dst: PColor32; DstX, DstY, Length: Integer;
- AlphaValues: PColor32; CombineMode: TCombineMode);
- procedure GradientFillerChanged; virtual;
- procedure WrapModeChanged; virtual;
- public
- constructor Create; overload;
- constructor Create(ColorGradient: TColor32Gradient); overload; virtual;
- destructor Destroy; override;
- property Gradient: TColor32Gradient read FGradient;
- property WrapMode: TWrapMode read FWrapMode write SetWrapMode;
- end;
- //------------------------------------------------------------------------------
- //
- // TCustomGradientLookupTablePolygonFiller
- //
- //------------------------------------------------------------------------------
- type
- TCustomGradientLookupTablePolygonFiller = class(TCustomGradientPolygonFiller)
- private
- FLUTNeedsUpdate: Boolean;
- FOwnsLUT: Boolean;
- FGradientLUT: TColor32LookupTable;
- FUseLookUpTable: Boolean;
- function GetLUTNeedsUpdate: Boolean;
- procedure SetUseLookUpTable(const Value: Boolean);
- procedure SetGradientLUT(const Value: TColor32LookupTable);
- protected
- procedure GradientFillerChanged; override;
- procedure UseLookUpTableChanged; virtual;
- procedure LookUpTableChangedHandler(Sender: TObject);
- property LookUpTableNeedsUpdate: Boolean read GetLUTNeedsUpdate;
- public
- constructor Create; reintroduce; overload;
- constructor Create(LookupTable: TColor32LookupTable); overload; virtual;
- destructor Destroy; override;
- property GradientLUT: TColor32LookupTable read FGradientLUT write SetGradientLUT;
- property UseLookUpTable: Boolean read FUseLookUpTable write SetUseLookUpTable;
- end;
- //------------------------------------------------------------------------------
- //
- // TCustomLinearGradientPolygonFiller
- //
- //------------------------------------------------------------------------------
- type
- TCustomLinearGradientPolygonFiller = class(TCustomGradientLookupTablePolygonFiller)
- private
- FIncline: TFloat;
- FStartPoint: TFloatPoint;
- FEndPoint: TFloatPoint;
- procedure SetStartPoint(const Value: TFloatPoint);
- procedure SetEndPoint(const Value: TFloatPoint);
- procedure UpdateIncline;
- protected
- procedure EndPointChanged;
- procedure StartPointChanged;
- public
- procedure SimpleGradient(const StartPoint: TFloatPoint; StartColor: TColor32;
- const EndPoint: TFloatPoint; EndColor: TColor32); virtual;
- procedure SimpleGradientX(const StartX: TFloat; StartColor: TColor32;
- const EndX: TFloat; EndColor: TColor32);
- procedure SimpleGradientY(const StartY: TFloat; StartColor: TColor32;
- const EndY: TFloat; EndColor: TColor32);
- procedure SetPoints(const StartPoint, EndPoint: TFloatPoint); virtual;
- property StartPoint: TFloatPoint read FStartPoint write SetStartPoint;
- property EndPoint: TFloatPoint read FEndPoint write SetEndPoint;
- end;
- //------------------------------------------------------------------------------
- //
- // TLinearGradientPolygonFiller
- //
- //------------------------------------------------------------------------------
- type
- TLinearGradientPolygonFiller = class(TCustomLinearGradientPolygonFiller)
- private
- function ColorStopToScanLine(Index: Integer; Y: Integer): TFloat;
- protected
- function GetFillLine: TFillLineEvent; override;
- procedure FillLineNegative(Dst: PColor32; DstX, DstY, Length: Integer;
- AlphaValues: PColor32;
- CombineMode: TCombineMode);
- procedure FillLinePositive(Dst: PColor32; DstX, DstY, Length: Integer;
- AlphaValues: PColor32;
- CombineMode: TCombineMode);
- procedure FillLineVertical(Dst: PColor32;
- DstX, DstY, Length: Integer; AlphaValues: PColor32;
- CombineMode: TCombineMode);
- procedure FillLineVerticalExtreme(Dst: PColor32; DstX, DstY,
- Length: Integer; AlphaValues: PColor32;
- CombineMode: TCombineMode);
- procedure FillLineVerticalPad(Dst: PColor32;
- DstX, DstY, Length: Integer; AlphaValues: PColor32;
- CombineMode: TCombineMode);
- procedure FillLineVerticalPadExtreme(Dst: PColor32; DstX, DstY,
- Length: Integer; AlphaValues: PColor32;
- CombineMode: TCombineMode);
- procedure FillLineVerticalWrap(Dst: PColor32;
- DstX, DstY, Length: Integer; AlphaValues: PColor32;
- CombineMode: TCombineMode);
- procedure FillLineHorizontalPadPos(Dst: PColor32;
- DstX, DstY, Length: Integer; AlphaValues: PColor32;
- CombineMode: TCombineMode);
- procedure FillLineHorizontalPadNeg(Dst: PColor32; DstX, DstY,
- Length: Integer; AlphaValues: PColor32;
- CombineMode: TCombineMode);
- procedure FillLineHorizontalWrapNeg(Dst: PColor32; DstX, DstY,
- Length: Integer; AlphaValues: PColor32;
- CombineMode: TCombineMode);
- procedure FillLineHorizontalWrapPos(Dst: PColor32; DstX, DstY,
- Length: Integer; AlphaValues: PColor32;
- CombineMode: TCombineMode);
- procedure UseLookUpTableChanged; override;
- procedure WrapModeChanged; override;
- public
- constructor Create(ColorGradient: TColor32Gradient); overload; override;
- constructor Create(ColorGradient: TColor32Gradient; UseLookupTable: Boolean); overload; virtual;
- procedure BeginRendering; override; //flags initialized
- end;
- //------------------------------------------------------------------------------
- //
- // TCustomRadialGradientPolygonFiller
- //
- //------------------------------------------------------------------------------
- type
- TCustomRadialGradientPolygonFiller = class(TCustomGradientLookupTablePolygonFiller)
- private
- FEllipseBounds: TFloatRect;
- procedure SetEllipseBounds(const Value: TFloatRect);
- protected
- procedure EllipseBoundsChanged; virtual; abstract;
- public
- property EllipseBounds: TFloatRect read FEllipseBounds write SetEllipseBounds;
- end;
- //------------------------------------------------------------------------------
- //
- // TRadialGradientPolygonFiller
- //
- //------------------------------------------------------------------------------
- type
- TRadialGradientPolygonFiller = class(TCustomRadialGradientPolygonFiller)
- private
- FCenter: TFloatPoint;
- FRadius: TFloatPoint;
- FRadScale: TFloat;
- FRadXInv: TFloat;
- procedure SetCenter(const Value: TFloatPoint);
- procedure SetRadius(const Value: TFloatPoint);
- procedure UpdateEllipseBounds;
- procedure UpdateRadiusScale;
- protected
- function GetFillLine: TFillLineEvent; override;
- procedure EllipseBoundsChanged; override;
- procedure FillLineClamp(Dst: PColor32; DstX, DstY, Length: Integer; AlphaValues: PColor32; CombineMode: TCombineMode);
- procedure FillLineRepeat(Dst: PColor32; DstX, DstY, Length: Integer; AlphaValues: PColor32; CombineMode: TCombineMode);
- procedure FillLineMirror(Dst: PColor32; DstX, DstY, Length: Integer; AlphaValues: PColor32; CombineMode: TCombineMode);
- procedure FillLineReflect(Dst: PColor32; DstX, DstY, Length: Integer; AlphaValues: PColor32; CombineMode: TCombineMode);
- public
- constructor Create(Radius: TFloatPoint); overload;
- constructor Create(BoundingBox: TFloatRect); overload;
- constructor Create(Radius, Center: TFloatPoint); overload;
- procedure BeginRendering; override;
- property Radius: TFloatPoint read FRadius write SetRadius;
- property Center: TFloatPoint read FCenter write SetCenter;
- end;
- //------------------------------------------------------------------------------
- //
- // TSVGRadialGradientPolygonFiller
- //
- //------------------------------------------------------------------------------
- type
- TSVGRadialGradientPolygonFiller = class(TCustomRadialGradientPolygonFiller)
- private
- FOffset: TFloatPoint;
- FRadius: TFloatPoint;
- FCenter: TFloatPoint;
- FFocalPt: TFloatPoint;
- FVertDist: TFloat;
- FFocalPointNative: TFloatPoint;
- procedure SetFocalPoint(const Value: TFloatPoint);
- procedure InitMembers;
- protected
- function GetFillLine: TFillLineEvent; override;
- procedure EllipseBoundsChanged; override;
- procedure FillLineEllipse(Dst: PColor32; DstX, DstY, Length: Integer;
- AlphaValues: PColor32; CombineMode: TCombineMode);
- public
- constructor Create(EllipseBounds: TFloatRect); overload;
- constructor Create(EllipseBounds: TFloatRect; FocalPoint: TFloatPoint); overload;
- procedure BeginRendering; override;
- procedure SetParameters(EllipseBounds: TFloatRect); overload;
- procedure SetParameters(EllipseBounds: TFloatRect; FocalPoint: TFloatPoint); overload;
- property FocalPoint: TFloatPoint read FFocalPointNative write SetFocalPoint;
- end;
- //------------------------------------------------------------------------------
- //
- // Utilities
- //
- //------------------------------------------------------------------------------
- function Color32FloatPoint(Color: TColor32; Point: TFloatPoint): TColor32FloatPoint; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
- function Color32FloatPoint(Color: TColor32; X, Y: TFloat): TColor32FloatPoint; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
- function Color32GradientStop(Offset: TFloat; Color: TColor32): TColor32GradientStop; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
- //------------------------------------------------------------------------------
- //
- // Bindings
- //
- //------------------------------------------------------------------------------
- const
- FID_LINEAR3 = 0;
- FID_LINEAR4 = 1;
- type
- TLinear3PointInterpolation = function (A, B, C: TColor32; WA, WB, WC: Single): TColor32;
- TLinear4PointInterpolation = function (A, B, C, D: TColor32; WA, WB, WC, WD: Single): TColor32;
- var
- // Linear interpolation of 3 colors
- Linear3PointInterpolation: TLinear3PointInterpolation;
- // Linear interpolation of 4 colors
- Linear4PointInterpolation: TLinear4PointInterpolation;
- var
- GradientRegistry: TFunctionRegistry;
- //------------------------------------------------------------------------------
- //------------------------------------------------------------------------------
- //------------------------------------------------------------------------------
- implementation
- uses
- {$if not defined(UseInlining)}
- GR32_Blend,
- {$ifend}
- GR32_LowLevel,
- GR32_Math,
- GR32_Geometry;
- resourcestring
- RCStrIndexOutOfBounds = 'Index out of bounds (%d)';
- RCStrWrongFormat = 'Wrong format';
- RCStrOnlyExactly3Point = 'Only exactly 3 points expected!';
- RCStrPointCountMismatch = 'Point count mismatch';
- RCStrNoTColor32LookupTable = 'No TColor32LookupTable object specified';
- RCStrNoTColor32Gradient = 'No TColor32Gradient specified';
- RCStrNoLookupTablePassed = 'No lookup table passed!';
- const
- CFloatTolerance = 0.001;
- procedure FillLineAlpha(var Dst, AlphaValues: PColor32; Count: Integer;
- Color: TColor32; CombineMode: TCombineMode); {$IFDEF USEINLINING}inline;{$ENDIF}
- var
- X: Integer;
- BlendMemEx: TBlendMemEx;
- begin
- BlendMemEx := BLEND_MEM_EX[CombineMode]^;
- for X := 0 to Count - 1 do
- begin
- BlendMemEx(Color, Dst^, AlphaValues^);
- Inc(Dst);
- Inc(AlphaValues);
- end;
- end;
- function Color32FloatPoint(Color: TColor32; Point: TFloatPoint): TColor32FloatPoint;
- begin
- Result.Point := Point;
- Result.Color32 := Color;
- end;
- function Color32FloatPoint(Color: TColor32; X, Y: TFloat): TColor32FloatPoint;
- begin
- Result.Point := FloatPoint(X, Y);
- Result.Color32 := Color;
- end;
- function Color32GradientStop(Offset: TFloat; Color: TColor32): TColor32GradientStop;
- begin
- Result.Offset := Offset;
- Result.Color32 := Color;
- end;
- function Linear3PointInterpolation_Pas(A, B, C: TColor32; WA, WB, WC: Single): TColor32;
- var
- Clr: TColor32Entry absolute Result;
- begin
- Clr.B := Clamp(Round(
- WA * TColor32Entry(A).B +
- WB * TColor32Entry(B).B +
- WC * TColor32Entry(C).B));
- Clr.G := Clamp(Round(
- WA * TColor32Entry(A).G +
- WB * TColor32Entry(B).G +
- WC * TColor32Entry(C).G));
- Clr.R := Clamp(Round(
- WA * TColor32Entry(A).R +
- WB * TColor32Entry(B).R +
- WC * TColor32Entry(C).R));
- Clr.A := Clamp(Round(
- WA * TColor32Entry(A).A +
- WB * TColor32Entry(B).A +
- WC * TColor32Entry(C).A));
- end;
- function Linear4PointInterpolation_Pas(A, B, C, D: TColor32; WA, WB, WC,
- WD: Single): TColor32;
- var
- Clr: TColor32Entry absolute Result;
- begin
- Clr.B := Clamp(Round(
- WA * TColor32Entry(A).B +
- WB * TColor32Entry(B).B +
- WC * TColor32Entry(C).B +
- WD * TColor32Entry(D).B));
- Clr.G := Clamp(Round(
- WA * TColor32Entry(A).G +
- WB * TColor32Entry(B).G +
- WC * TColor32Entry(C).G +
- WD * TColor32Entry(D).G));
- Clr.R := Clamp(Round(
- WA * TColor32Entry(A).R +
- WB * TColor32Entry(B).R +
- WC * TColor32Entry(C).R +
- WD * TColor32Entry(D).R));
- Clr.A := Clamp(Round(
- WA * TColor32Entry(A).A +
- WB * TColor32Entry(B).A +
- WC * TColor32Entry(C).A +
- WD * TColor32Entry(D).A));
- end;
- {$IFNDEF OMIT_SSE2}
- {$IFNDEF PUREPASCAL}
- function Linear3PointInterpolation_SSE2(A, B, C: TColor32; WA, WB, WC: Single): TColor32; {$IFDEF FPC}assembler; {$ENDIF}
- asm
- {$IFDEF TARGET_X86}
- PXOR XMM3,XMM3
- MOVD XMM0,EAX
- PUNPCKLBW XMM0,XMM3
- PUNPCKLWD XMM0,XMM3
- CVTDQ2PS XMM0,XMM0
- MOVD XMM1,EDX
- PUNPCKLBW XMM1,XMM3
- PUNPCKLWD XMM1,XMM3
- CVTDQ2PS XMM1,XMM1
- MOVD XMM2,ECX
- PUNPCKLBW XMM2,XMM3
- PUNPCKLWD XMM2,XMM3
- CVTDQ2PS XMM2,XMM2
- MOV EAX, WA
- MOV EDX, WB
- MOV ECX, WC
- MOVD XMM4,EAX
- SHUFPS XMM4,XMM4,0
- MOVD XMM5,EDX
- SHUFPS XMM5,XMM5,0
- MOVD XMM6,ECX
- SHUFPS XMM6,XMM6,0
- MULPS XMM0,XMM4
- MULPS XMM1,XMM5
- MULPS XMM2,XMM6
- ADDPS XMM0,XMM1
- ADDPS XMM0,XMM2
- CVTPS2DQ XMM0,XMM0
- PACKSSDW XMM0,XMM3
- PACKUSWB XMM0,XMM3
- MOVD EAX,XMM0
- {$ENDIF}
- {$IFDEF TARGET_X64}
- MOVQ XMM0,XMM3
- SHUFPS XMM0,XMM0,0
- {$IFDEF FPC}
- MOVD XMM1,[RBP + $30]
- {$ELSE}
- MOVD XMM1,WB
- {$ENDIF}
- SHUFPS XMM1,XMM1,0
- {$IFDEF FPC}
- MOVD XMM2,[RBP + $38]
- {$ELSE}
- MOVD XMM2,WC
- {$ENDIF}
- SHUFPS XMM2,XMM2,0
- PXOR XMM3,XMM3
- MOVD XMM4,ECX
- PUNPCKLBW XMM4,XMM3
- PUNPCKLWD XMM4,XMM3
- CVTDQ2PS XMM4,XMM4
- MOVD XMM5,EDX
- PUNPCKLBW XMM5,XMM3
- PUNPCKLWD XMM5,XMM3
- CVTDQ2PS XMM5,XMM5
- MOVD XMM6,R8D
- PUNPCKLBW XMM6,XMM3
- PUNPCKLWD XMM6,XMM3
- CVTDQ2PS XMM6,XMM6
- MULPS XMM0,XMM4
- MULPS XMM1,XMM5
- MULPS XMM2,XMM6
- ADDPS XMM0,XMM1
- ADDPS XMM0,XMM2
- CVTPS2DQ XMM0,XMM0
- PACKSSDW XMM0,XMM3
- PACKUSWB XMM0,XMM3
- MOVD EAX,XMM0
- {$ENDIF}
- end;
- function Linear4PointInterpolation_SSE2(A, B, C, D: TColor32; WA, WB, WC, WD: Single): TColor32; {$IFDEF FPC}assembler; {$ENDIF}
- asm
- {$IFDEF TARGET_X86}
- PXOR XMM7,XMM7
- MOVD XMM0,EAX
- PUNPCKLBW XMM0,XMM7
- PUNPCKLWD XMM0,XMM7
- CVTDQ2PS XMM0,XMM0
- MOVD XMM1,EDX
- PUNPCKLBW XMM1,XMM7
- PUNPCKLWD XMM1,XMM7
- CVTDQ2PS XMM1,XMM1
- MOV EAX, WA
- MOVD XMM4,EAX
- SHUFPS XMM4,XMM4,0
- MULPS XMM0,XMM4
- MOV EDX, WB
- MOVD XMM5,EDX
- SHUFPS XMM5,XMM5,0
- MULPS XMM1,XMM5
- ADDPS XMM0,XMM1
- MOVD XMM2,ECX
- PUNPCKLBW XMM2,XMM7
- PUNPCKLWD XMM2,XMM7
- CVTDQ2PS XMM2,XMM2
- MOVD XMM3,D
- PUNPCKLBW XMM3,XMM7
- PUNPCKLWD XMM3,XMM7
- CVTDQ2PS XMM3,XMM3
- MOV EAX, WC
- MOVD XMM4,EAX
- SHUFPS XMM4,XMM4,0
- MULPS XMM2,XMM4
- MOV EDX, WD
- MOVD XMM5,EDX
- SHUFPS XMM5,XMM5,0
- MULPS XMM3,XMM5
- ADDPS XMM2,XMM3
- ADDPS XMM0,XMM2
- CVTPS2DQ XMM0,XMM0
- PACKSSDW XMM0,XMM7
- PACKUSWB XMM0,XMM7
- MOVD EAX,XMM0
- {$ENDIF}
- {$IFDEF TARGET_X64}
- PXOR XMM7,XMM7
- MOVD XMM0,A
- PUNPCKLBW XMM0,XMM7
- PUNPCKLWD XMM0,XMM7
- CVTDQ2PS XMM0,XMM0
- MOVD XMM1,B
- PUNPCKLBW XMM1,XMM7
- PUNPCKLWD XMM1,XMM7
- CVTDQ2PS XMM1,XMM1
- {$IFDEF FPC}
- MOV EAX, [RBP + $30]
- {$ELSE}
- MOV EAX, WA
- {$ENDIF}
- MOVD XMM4,EAX
- SHUFPS XMM4,XMM4,0
- MULPS XMM0,XMM4
- {$IFDEF FPC}
- MOV EDX, [RBP + $38]
- {$ELSE}
- MOV EDX, WB
- {$ENDIF}
- MOVD XMM5,EDX
- SHUFPS XMM5,XMM5,0
- MULPS XMM1,XMM5
- ADDPS XMM0,XMM1
- MOVD XMM2,C
- PUNPCKLBW XMM2,XMM7
- PUNPCKLWD XMM2,XMM7
- CVTDQ2PS XMM2,XMM2
- MOVD XMM3,D
- PUNPCKLBW XMM3,XMM7
- PUNPCKLWD XMM3,XMM7
- CVTDQ2PS XMM3,XMM3
- {$IFDEF FPC}
- MOV EAX, [RBP + $40]
- {$ELSE}
- MOV EAX, WC
- {$ENDIF}
- MOVD XMM4,EAX
- SHUFPS XMM4,XMM4,0
- MULPS XMM2,XMM4
- {$IFDEF FPC}
- MOV EDX, [RBP + $48]
- {$ELSE}
- MOV EDX, WD
- {$ENDIF}
- MOVD XMM5,EDX
- SHUFPS XMM5,XMM5,0
- MULPS XMM3,XMM5
- ADDPS XMM2,XMM3
- ADDPS XMM0,XMM2
- CVTPS2DQ XMM0,XMM0
- PACKSSDW XMM0,XMM7
- PACKUSWB XMM0,XMM7
- MOVD EAX,XMM0
- {$ENDIF}
- end;
- {$ENDIF}
- {$ENDIF}
- { TColor32LookupTable }
- constructor TColor32LookupTable.Create(Order: Byte);
- begin
- inherited Create;
- FOrder := Order;
- OrderChanged;
- end;
- destructor TColor32LookupTable.Destroy;
- begin
- {$WARNINGS OFF}
- FreeMem(FGradientLUT);
- {$WARNINGS ON}
- inherited;
- end;
- procedure TColor32LookupTable.AssignTo(Dest: TPersistent);
- begin
- if Dest is TColor32LookupTable then
- with TColor32LookupTable(Dest) do
- begin
- FOrder := Self.FOrder;
- OrderChanged;
- Move(Self.FGradientLUT^, FGradientLUT^, FSize * SizeOf(TColor32));
- end
- else
- inherited;
- end;
- function TColor32LookupTable.GetColor32(Index: Integer): TColor32;
- begin
- Result := FGradientLUT^[Index and FMask];
- end;
- procedure TColor32LookupTable.OrderChanged;
- begin
- FSize := 1 shl FOrder;
- FMask := FSize - 1;
- {$WARNINGS OFF}
- ReallocMem(FGradientLUT, FSize * SizeOf(TColor32));
- {$WARNINGS ON}
- if Assigned(FOnOrderChanged) then
- FOnOrderChanged(Self);
- end;
- procedure TColor32LookupTable.SetColor32(Index: Integer; const Value: TColor32);
- begin
- if (Index < 0) or (Index > Integer(FMask)) then
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index])
- else
- FGradientLUT^[Index] := Value;
- end;
- procedure TColor32LookupTable.SetOrder(const Value: Byte);
- begin
- if FOrder <> Value then
- begin
- FOrder := Value;
- OrderChanged;
- end;
- end;
- { TColor32Gradient; }
- constructor TColor32Gradient.Create(Color: TColor32);
- begin
- Create(Color, Color);
- end;
- constructor TColor32Gradient.Create(StartColor, EndColor: TColor32);
- var
- Temp: TArrayOfColor32GradientStop;
- begin
- // simple gradient using 2 color stops
- SetLength(Temp, 2);
- Temp[0].Offset := 0;
- Temp[0].Color32 := StartColor;
- Temp[1].Offset := 1;
- Temp[1].Color32 := EndColor;
- Create(Temp);
- end;
- constructor TColor32Gradient.Create(const GradientColors: TArrayOfColor32GradientStop);
- begin
- inherited Create;
- SetColors(GradientColors);
- end;
- procedure TColor32Gradient.AssignTo(Dest: TPersistent);
- begin
- if Dest is TColor32Gradient then
- TColor32Gradient(Dest).SetColors(Self.FGradientColors)
- else
- inherited;
- end;
- procedure TColor32Gradient.AddColorStop(ColorStop: TColor32GradientStop);
- begin
- AddColorStop(ColorStop.Offset, ColorStop.Color32);
- end;
- procedure TColor32Gradient.AddColorStop(Offset: TFloat; Color: TColor32);
- var
- Index, OldCount: Integer;
- begin
- OldCount := Length(FGradientColors);
- Index := 0;
- // navigate to index where the color stop shall be inserted
- while (Index < OldCount) and (Offset >= FGradientColors[Index].Offset) do
- Inc(Index);
- SetLength(FGradientColors, OldCount + 1);
- // move existing color stops to make space for the new color stop
- if (Index < OldCount) then
- Move(FGradientColors[Index], FGradientColors[Index + 1],
- (OldCount - Index) * SizeOf(TColor32GradientStop));
- // finally insert new color stop
- FGradientColors[Index].Offset := Offset;
- FGradientColors[Index].Color32 := Color;
- GradientColorsChanged;
- end;
- procedure TColor32Gradient.ClearColorStops(Color: TColor32);
- begin
- SetLength(FGradientColors, 1);
- FGradientColors[0].Offset := 0;
- FGradientColors[0].Color32 := Color;
- GradientColorsChanged;
- end;
- procedure TColor32Gradient.ClearColorStops;
- begin
- SetLength(FGradientColors, 0);
- GradientColorsChanged;
- end;
- procedure TColor32Gradient.SetColors(const GradientColors: array of const);
- var
- Index: Integer;
- Scale: TFloat;
- begin
- if Length(GradientColors) = 0 then
- begin
- // no colors specified
- if Length(FGradientColors) > 0 then
- ClearColorStops;
- end else
- begin
- SetLength(FGradientColors, Length(GradientColors));
- if Length(GradientColors) > 1 then
- begin
- // several colors (at least 2)
- Scale := 1 / High(GradientColors);
- for Index := 0 to High(GradientColors) do
- begin
- Assert(GradientColors[Index].VType = vtInteger);
- FGradientColors[Index].Color32 := GradientColors[Index].VInteger;
- FGradientColors[Index].Offset := Index * Scale;
- end;
- end
- else
- begin
- // only 1 color
- Assert(GradientColors[0].VType = vtInteger);
- FGradientColors[0].Color32 := GradientColors[0].VInteger;
- FGradientColors[0].Offset := 0;
- end;
- GradientColorsChanged;
- end;
- end;
- procedure TColor32Gradient.SetColors(const GradientColors: TArrayOfColor32GradientStop);
- var
- Index: Integer;
- begin
- if Length(GradientColors) = 0 then
- begin
- if Length(FGradientColors) > 0 then
- ClearColorStops;
- end else
- begin
- SetLength(FGradientColors, Length(GradientColors));
- for Index := 0 to High(GradientColors) do
- FGradientColors[Index] := GradientColors[Index];
- GradientColorsChanged;
- end;
- end;
- procedure TColor32Gradient.SetColors(const GradientColors: TArrayOfColor32);
- var
- Index: Integer;
- Scale: TFloat;
- begin
- if Length(GradientColors) = 0 then
- begin
- // no colors specified
- if Length(FGradientColors) > 0 then
- ClearColorStops;
- end else
- begin
- SetLength(FGradientColors, Length(GradientColors));
- if Length(GradientColors) > 1 then
- begin
- // several colors (at least 2)
- Scale := 1 / High(GradientColors);
- for Index := 0 to High(GradientColors) do
- begin
- FGradientColors[Index].Color32 := GradientColors[Index];
- FGradientColors[Index].Offset := Index * Scale;
- end;
- end
- else
- begin
- // only 1 color
- FGradientColors[0].Color32 := GradientColors[0];
- FGradientColors[0].Offset := 0;
- end;
- GradientColorsChanged;
- end;
- end;
- procedure TColor32Gradient.SetColors(const Palette: TPalette32);
- var
- Index: Integer;
- Scale: TFloat;
- begin
- // TPalette32 contains 256 colors
- SetLength(FGradientColors, Length(Palette));
- Scale := 1 / High(Palette);
- for Index := 0 to High(Palette) do
- begin
- FGradientColors[Index].Color32 := Palette[Index];
- FGradientColors[Index].Offset := Index * Scale;
- end;
- GradientColorsChanged;
- end;
- procedure TColor32Gradient.SetStartColor(const Value: TColor32);
- var
- HasChanged: Boolean;
- begin
- HasChanged := False;
- if Length(FGradientColors) = 0 then
- begin
- SetLength(FGradientColors, 1);
- HasChanged := True;
- end;
- if FGradientColors[0].Offset <> 0 then
- begin
- FGradientColors[0].Offset := 0;
- HasChanged := True;
- end;
- if FGradientColors[0].Color32 <> Value then
- begin
- FGradientColors[0].Color32 := Value;
- HasChanged := True;
- end;
- if HasChanged then
- GradientColorsChanged;
- end;
- procedure TColor32Gradient.SetEndColor(const Value: TColor32);
- var
- HasChanged: Boolean;
- begin
- HasChanged := False;
- if Length(FGradientColors) = 1 then
- begin
- SetLength(FGradientColors, 2);
- HasChanged := True;
- end;
- if FGradientColors[High(FGradientColors)].Offset <> 1 then
- begin
- FGradientColors[High(FGradientColors)].Offset := 1;
- HasChanged := True;
- end;
- if FGradientColors[High(FGradientColors)].Color32 <> Value then
- begin
- FGradientColors[High(FGradientColors)].Color32 := Value;
- HasChanged := True;
- end;
- if HasChanged then
- GradientColorsChanged;
- end;
- function TColor32Gradient.GetGradientCount: Integer;
- begin
- Result := Length(FGradientColors);
- end;
- function TColor32Gradient.GetGradientEntry(Index: Integer): TColor32GradientStop;
- begin
- if Index > Length(FGradientColors) then
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- Result := FGradientColors[Index];
- end;
- function TColor32Gradient.GetStartColor: TColor32;
- begin
- if Length(FGradientColors) = 0 then
- Result := clNone32
- else
- Result := FGradientColors[0].Color32;
- end;
- function TColor32Gradient.GetEndColor: TColor32;
- var
- Count: Integer;
- begin
- Count := Length(FGradientColors);
- if Count = 0 then
- Result := clNone32
- else
- Result := FGradientColors[Count - 1].Color32;
- end;
- function TColor32Gradient.GetColorAt(Offset: TFloat): TColor32;
- var
- Index, Count: Integer;
- begin
- Count := GradientCount;
- if (Count = 0) or (Offset <= FGradientColors[0].Offset) then
- Result := StartColor
- else
- if (Offset >= FGradientColors[Count - 1].Offset) then
- Result := EndColor
- else
- begin
- Index := 1;
- // find color index for a given offset (between 0 and 1)
- while (Index < Count) and (Offset > FGradientColors[Index].Offset) do
- Inc(Index);
- // calculate new offset (between two colors before and at 'Index')
- Offset := (Offset - FGradientColors[Index - 1].Offset) / (FGradientColors[Index].Offset - FGradientColors[Index - 1].Offset);
- // check if offset is out of bounds
- if Offset <= 0 then
- Result := FGradientColors[Index - 1].Color32
- else
- if Offset >= 1 then
- Result := FGradientColors[Index].Color32
- else
- // interpolate color
- Result := CombineReg(FGradientColors[Index].Color32, FGradientColors[Index - 1].Color32, Round($FF * Offset));
- end;
- end;
- procedure TColor32Gradient.FillColorLookUpTable(ColorLUT: TColor32LookupTable);
- begin
- FillColorLookUpTable(ColorLUT.Color32Ptr, ColorLUT.Size);
- end;
- procedure TColor32Gradient.FillColorLookUpTable(var ColorLUT: array of TColor32);
- begin
- {$WARNINGS OFF}
- FillColorLookUpTable(@ColorLUT[0], Length(ColorLUT));
- {$WARNINGS ON}
- end;
- procedure TColor32Gradient.FillColorLookUpTable(ColorLUT: PColor32Array;
- Count: Integer);
- var
- LutIndex, StopIndex, GradCount: Integer;
- RecalculateScale: Boolean;
- Fraction, LocalFraction, Delta, Scale: TFloat;
- begin
- GradCount := GradientCount;
- //check trivial case
- if (GradCount < 2) or (Count < 2) then
- begin
- for LutIndex := 0 to Count - 1 do
- ColorLUT^[LutIndex] := StartColor;
- Exit;
- end;
- // set first (start) and last (end) color
- ColorLUT^[0] := StartColor;
- ColorLUT^[Count - 1] := EndColor;
- Delta := 1 / Count;
- Fraction := Delta;
- LutIndex := 1;
- while Fraction <= FGradientColors[0].Offset do
- begin
- ColorLUT^[LutIndex] := ColorLUT^[0];
- Fraction := Fraction + Delta;
- Inc(LutIndex);
- end;
- Scale := 1;
- StopIndex := 1;
- RecalculateScale := True;
- for LutIndex := LutIndex to Count - 2 do
- begin
- // eventually search next stop
- while (Fraction > FGradientColors[StopIndex].Offset) do
- begin
- Inc(StopIndex);
- if (StopIndex >= GradCount) then
- Break;
- RecalculateScale := True;
- end;
- // eventually fill remaining LUT
- if StopIndex = GradCount then
- begin
- for StopIndex := LutIndex to Count - 2 do
- ColorLUT^[StopIndex] := ColorLUT^[Count - 1];
- Break;
- end;
- // eventually recalculate scale
- if RecalculateScale then
- Scale := 1 / (FGradientColors[StopIndex].Offset - FGradientColors[StopIndex - 1].Offset);
- // calculate current color
- LocalFraction := (Fraction - FGradientColors[StopIndex - 1].Offset) * Scale;
- if LocalFraction <= 0 then
- ColorLUT^[LutIndex] := FGradientColors[StopIndex - 1].Color32
- else
- if LocalFraction >= 1 then
- ColorLUT^[LutIndex] := FGradientColors[StopIndex].Color32
- else
- ColorLUT^[LutIndex] := CombineReg(FGradientColors[StopIndex].Color32, FGradientColors[StopIndex - 1].Color32, Round($FF * LocalFraction));
- Fraction := Fraction + Delta;
- end;
- end;
- procedure TColor32Gradient.GradientColorsChanged;
- begin
- if Assigned(FOnGradientColorsChanged) then
- FOnGradientColorsChanged(Self);
- end;
- procedure TColor32Gradient.LoadFromStream(Stream: TStream);
- var
- Index: Integer;
- ChunkName: array [0..3] of AnsiChar;
- ValueInt: Integer;
- ValueFloat: Single;
- begin
- // read simple header
- Stream.Read(ChunkName, 4);
- if ChunkName <> 'Grad' then
- raise Exception.Create(RCStrWrongFormat);
- Stream.Read(ValueInt, 4);
- SetLength(FGradientColors, ValueInt);
- // read data
- for Index := 0 to Length(FGradientColors) - 1 do
- begin
- ValueFloat := FGradientColors[Index].Offset;
- Stream.Read(ValueFloat, 4);
- ValueInt := FGradientColors[Index].Color32;
- Stream.Read(ValueInt, 4);
- end;
- GradientColorsChanged;
- end;
- procedure TColor32Gradient.SaveToStream(Stream: TStream);
- var
- Index: Integer;
- ChunkName: array [0..3] of AnsiChar;
- ValueInt: Integer;
- ValueFloat: Single;
- begin
- // write simple header
- ChunkName := 'Grad';
- Stream.Write(ChunkName, 4);
- ValueInt := Length(FGradientColors);
- Stream.Write(ValueInt, 4);
- // write data
- for Index := 0 to Length(FGradientColors) - 1 do
- begin
- ValueFloat := FGradientColors[Index].Offset;
- Stream.Write(ValueFloat, 4);
- ValueInt := FGradientColors[Index].Color32;
- Stream.Write(ValueInt, 4);
- end;
- end;
- { TCustomSparsePointGradientSampler }
- function TCustomSparsePointGradientSampler.GetSampleFixed(X, Y: TFixed): TColor32;
- begin
- Result := GetSampleFloat(X * FixedToFloat, Y * FixedToFloat);
- end;
- function TCustomSparsePointGradientSampler.GetSampleInt(X, Y: Integer): TColor32;
- begin
- Result := GetSampleFloat(X, Y);
- end;
- { TBarycentricGradientSampler }
- constructor TBarycentricGradientSampler.Create(P1, P2, P3: TColor32FloatPoint);
- begin
- FColorPoints[0] := P1;
- FColorPoints[1] := P2;
- FColorPoints[2] := P3;
- inherited Create;
- end;
- procedure TBarycentricGradientSampler.AssignTo(Dest: TPersistent);
- begin
- if Dest is TBarycentricGradientSampler then
- with TBarycentricGradientSampler(Dest) do
- FColorPoints := Self.FColorPoints
- else
- inherited;
- end;
- function TBarycentricGradientSampler.GetColor(Index: Integer): TColor32;
- begin
- if Index in [0 .. 2] then
- Result := FColorPoints[Index].Color32
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- function TBarycentricGradientSampler.GetColorPoint(
- Index: Integer): TColor32FloatPoint;
- begin
- if Index in [0 .. 2] then
- Result := FColorPoints[Index]
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- function TBarycentricGradientSampler.GetCount: Integer;
- begin
- Result := 3;
- end;
- function TBarycentricGradientSampler.GetPoint(Index: Integer): TFloatPoint;
- begin
- if Index in [0 .. 2] then
- Result := FColorPoints[Index].Point
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- procedure TBarycentricGradientSampler.CalculateBarycentricCoordinates(
- X, Y: TFloat; out U, V, W: TFloat);
- var
- Temp: TFloatPoint;
- begin
- Temp.X := X - FColorPoints[2].Point.X;
- Temp.Y := Y - FColorPoints[2].Point.Y;
- U := FDists[0].Y * Temp.X + FDists[0].X * Temp.Y;
- V := FDists[1].Y * Temp.X + FDists[1].X * Temp.Y;
- W := 1.0 - U - V;
- end;
- function TBarycentricGradientSampler.GetSampleFloat(X, Y: TFloat): TColor32;
- var
- U, V, W: TFloat;
- begin
- CalculateBarycentricCoordinates(X, Y, U, V, W);
- Result := Linear3PointInterpolation(FColorPoints[0].Color32,
- FColorPoints[1].Color32, FColorPoints[2].Color32, U, V, W);
- end;
- function TBarycentricGradientSampler.GetSampleFloatInTriangle(X,
- Y: TFloat): TColor32;
- var
- U, V, W: TFloat;
- begin
- CalculateBarycentricCoordinates(X, Y, U, V, W);
- if U < 0 then
- begin
- U := (V + W);
- V := V / U;
- W := W / U;
- U := 0;
- end;
- if V < 0 then
- begin
- V := (U + W);
- U := U / V;
- W := W / V;
- V := 0;
- end;
- if V < 0 then
- begin
- W := (U + V);
- U := U / W;
- V := V / W;
- W := 0;
- end;
- Result := Linear3PointInterpolation(FColorPoints[0].Color32,
- FColorPoints[1].Color32, FColorPoints[2].Color32, U, V, W);
- end;
- function TBarycentricGradientSampler.IsPointInTriangle(
- const Point: TFloatPoint): Boolean;
- var
- U, V, W: TFloat;
- begin
- CalculateBarycentricCoordinates(Point.X, Point.Y, U, V, W);
- Result := (U >= 0) and (V >= 0) and (W >= 0);
- end;
- function TBarycentricGradientSampler.IsPointInTriangle(X, Y: TFloat): Boolean;
- var
- U, V, W: TFloat;
- begin
- CalculateBarycentricCoordinates(X, Y, U, V, W);
- Result := (U >= 0) and (V >= 0) and (W >= 0);
- end;
- procedure TBarycentricGradientSampler.PrepareSampling;
- var
- NormScale: TFloat;
- begin
- NormScale := 1 / ((FColorPoints[1].Point.Y - FColorPoints[2].Point.Y) *
- (FColorPoints[0].Point.X - FColorPoints[2].Point.X) +
- (FColorPoints[2].Point.X - FColorPoints[1].Point.X) *
- (FColorPoints[0].Point.Y - FColorPoints[2].Point.Y));
- FDists[0].X := NormScale * (FColorPoints[2].Point.X - FColorPoints[1].Point.X);
- FDists[0].Y := NormScale * (FColorPoints[1].Point.Y - FColorPoints[2].Point.Y);
- FDists[1].X := NormScale * (FColorPoints[0].Point.X - FColorPoints[2].Point.X);
- FDists[1].Y := NormScale * (FColorPoints[2].Point.Y - FColorPoints[0].Point.Y);
- end;
- procedure TBarycentricGradientSampler.SetColor(Index: Integer;
- const Value: TColor32);
- begin
- if Index in [0 .. 2] then
- FColorPoints[Index].Color32 := Value
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- procedure TBarycentricGradientSampler.SetColorPoint(Index: Integer;
- const Value: TColor32FloatPoint);
- begin
- if Index in [0 .. 2] then
- FColorPoints[Index] := Value
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- procedure TBarycentricGradientSampler.SetColorPoints(
- ColorPoints: TArrayOfColor32FloatPoint);
- begin
- if Length(ColorPoints) <> 3 then
- raise Exception.Create(RCStrOnlyExactly3Point);
- FColorPoints[0] := ColorPoints[0];
- FColorPoints[1] := ColorPoints[1];
- FColorPoints[2] := ColorPoints[2];
- end;
- procedure TBarycentricGradientSampler.SetColorPoints(Points: TArrayOfFloatPoint;
- Colors: TArrayOfColor32);
- begin
- if (Length(Points) <> 3) or (Length(Colors) <> 3) then
- raise Exception.Create(RCStrOnlyExactly3Point);
- FColorPoints[0] := Color32FloatPoint(Colors[0], Points[0]);
- FColorPoints[1] := Color32FloatPoint(Colors[1], Points[1]);
- FColorPoints[2] := Color32FloatPoint(Colors[2], Points[2]);
- end;
- procedure TBarycentricGradientSampler.SetPoint(Index: Integer;
- const Value: TFloatPoint);
- begin
- if Index in [0 .. 2] then
- FColorPoints[Index].Point := Value
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- procedure TBarycentricGradientSampler.SetPoints(Points: TArrayOfFloatPoint);
- begin
- if Length(Points) <> 3 then
- raise Exception.Create(RCStrOnlyExactly3Point);
- FColorPoints[0].Point := Points[0];
- FColorPoints[1].Point := Points[1];
- FColorPoints[2].Point := Points[2];
- end;
- { TBilinearGradientSampler }
- procedure TBilinearGradientSampler.AssignTo(Dest: TPersistent);
- begin
- if Dest is TBilinearGradientSampler then
- with TBilinearGradientSampler(Dest) do
- FColorPoints := Self.FColorPoints
- else
- inherited;
- end;
- function TBilinearGradientSampler.GetColor(Index: Integer): TColor32;
- begin
- if Index in [0 .. 3] then
- Result := FColorPoints[Index].Color32
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- function TBilinearGradientSampler.GetColorPoint(
- Index: Integer): TColor32FloatPoint;
- begin
- if Index in [0 .. 3] then
- Result := FColorPoints[Index]
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- function TBilinearGradientSampler.GetCount: Integer;
- begin
- Result := 4;
- end;
- function TBilinearGradientSampler.GetPoint(Index: Integer): TFloatPoint;
- begin
- if Index in [0 .. 3] then
- Result := FColorPoints[Index].Point
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- function TBilinearGradientSampler.GetSampleFloat(X, Y: TFloat): TColor32;
- var
- u, v, t, k0, k1: Double;
- begin
- k1 := FDot + X * FDists[2].Y - Y * FDists[2].X;
- k0 := FBiasK0 + X * FDists[0].Y - Y * FDists[0].X;
- t := Sqr(k1) - 2 * k0 * FK2Value;
- if FK2Value = 0 then
- v := -k0 / k1
- else
- v := (FK2Sign * Sqrt(Abs(t)) - k1) / FK2Value;
- u := (X - FBiasU - FDists[1].X * v) / (FDists[0].X + FDists[2].X * v);
- Result := Linear4PointInterpolation(FColorPoints[0].Color32,
- FColorPoints[1].Color32, FColorPoints[2].Color32, FColorPoints[3].Color32,
- (1.0 - u) * (1.0 - v), u * (1.0 - v), u * v, (1.0 - u) * v);
- end;
- procedure TBilinearGradientSampler.PrepareSampling;
- var
- v, i, j: Integer;
- Orientation: array [0 .. 3] of Boolean;
- Indices: array [0 .. 1] of Integer;
- TempPoint: TColor32FloatPoint;
- begin
- Orientation[0] := (FColorPoints[0].Point.X - FColorPoints[3].Point.X) *
- (FColorPoints[1].Point.Y - FColorPoints[0].Point.Y) -
- (FColorPoints[0].Point.Y - FColorPoints[3].Point.Y) *
- (FColorPoints[1].Point.X - FColorPoints[0].Point.X) < 0;
- Orientation[1] := (FColorPoints[1].Point.X - FColorPoints[0].Point.X) *
- (FColorPoints[2].Point.Y - FColorPoints[1].Point.Y) -
- (FColorPoints[1].Point.Y - FColorPoints[0].Point.Y) *
- (FColorPoints[2].Point.X - FColorPoints[1].Point.X) < 0;
- Orientation[2] := (FColorPoints[2].Point.X - FColorPoints[1].Point.X) *
- (FColorPoints[3].Point.Y - FColorPoints[2].Point.Y) -
- (FColorPoints[2].Point.Y - FColorPoints[1].Point.Y) *
- (FColorPoints[3].Point.X - FColorPoints[2].Point.X) < 0;
- Orientation[3] := (FColorPoints[3].Point.X - FColorPoints[2].Point.X) *
- (FColorPoints[0].Point.Y - FColorPoints[3].Point.Y) -
- (FColorPoints[3].Point.Y - FColorPoints[2].Point.Y) *
- (FColorPoints[0].Point.X - FColorPoints[3].Point.X) < 0;
- if Orientation[0] then v := -1 else v := 1;
- if Orientation[1] then Dec(v) else Inc(v);
- if Orientation[2] then Dec(v) else Inc(v);
- if Orientation[3] then Dec(v) else Inc(v);
- FK2Sign := Sign(v);
- if v = 0 then
- begin
- // correct complex case
- i := 0;
- j := 0;
- repeat
- if Orientation[j] then
- begin
- Indices[i] := j;
- Inc(i);
- end;
- Inc(j);
- until i = 2;
- // exchange color points
- TempPoint := FColorPoints[Indices[0]];
- FColorPoints[Indices[0]] := FColorPoints[Indices[1]];
- FColorPoints[Indices[1]] := TempPoint;
- FK2Sign := 1;
- end;
- FDists[0].X := FColorPoints[1].Point.X - FColorPoints[0].Point.X;
- FDists[0].Y := FColorPoints[1].Point.Y - FColorPoints[0].Point.Y;
- FDists[1].X := FColorPoints[3].Point.X - FColorPoints[0].Point.X;
- FDists[1].Y := FColorPoints[3].Point.Y - FColorPoints[0].Point.Y;
- FDists[2].X := FColorPoints[0].Point.X - FColorPoints[1].Point.X +
- FColorPoints[2].Point.X - FColorPoints[3].Point.X;
- FDists[2].Y := FColorPoints[0].Point.Y - FColorPoints[1].Point.Y +
- FColorPoints[2].Point.Y - FColorPoints[3].Point.Y;
- FK2Value := 2 * (FDists[2].X * FDists[1].Y - FDists[2].Y * FDists[1].X);
- FDot := FDists[0].X * FDists[1].Y - FDists[0].Y * FDists[1].X -
- FColorPoints[0].Point.X * FDists[2].Y + FColorPoints[0].Point.Y * FDists[2].X;
- FBiasK0 := FColorPoints[0].Point.Y * FDists[0].X -
- FColorPoints[0].Point.X * FDists[0].Y;
- FBiasU := FColorPoints[0].Point.X;
- end;
- procedure TBilinearGradientSampler.SetColor(Index: Integer;
- const Value: TColor32);
- begin
- if Index in [0 .. 3] then
- FColorPoints[Index].Color32 := Value
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- procedure TBilinearGradientSampler.SetColorPoint(Index: Integer;
- const Value: TColor32FloatPoint);
- begin
- if Index in [0 .. 3] then
- FColorPoints[Index] := Value
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- procedure TBilinearGradientSampler.SetColorPoints(
- ColorPoints: TArrayOfColor32FloatPoint);
- begin
- if Length(ColorPoints) <> 4 then
- raise Exception.Create(RCStrOnlyExactly3Point);
- FColorPoints[0] := ColorPoints[0];
- FColorPoints[1] := ColorPoints[1];
- FColorPoints[2] := ColorPoints[2];
- FColorPoints[3] := ColorPoints[3];
- end;
- procedure TBilinearGradientSampler.SetColorPoints(Points: TArrayOfFloatPoint;
- Colors: TArrayOfColor32);
- begin
- if (Length(Points) <> 3) or (Length(Colors) <> 3) then
- raise Exception.Create(RCStrOnlyExactly3Point);
- FColorPoints[0] := Color32FloatPoint(Colors[0], Points[0]);
- FColorPoints[1] := Color32FloatPoint(Colors[1], Points[1]);
- FColorPoints[2] := Color32FloatPoint(Colors[2], Points[2]);
- FColorPoints[3] := Color32FloatPoint(Colors[3], Points[3]);
- end;
- procedure TBilinearGradientSampler.SetPoint(Index: Integer;
- const Value: TFloatPoint);
- begin
- if Index in [0 .. 3] then
- FColorPoints[Index].Point := Value
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- procedure TBilinearGradientSampler.SetPoints(Points: TArrayOfFloatPoint);
- begin
- if Length(Points) <> 4 then
- raise Exception.Create(RCStrOnlyExactly3Point);
- FColorPoints[0].Point := Points[0];
- FColorPoints[1].Point := Points[1];
- FColorPoints[2].Point := Points[2];
- FColorPoints[3].Point := Points[3];
- end;
- { TCustomArbitrarySparsePointGradientSampler }
- procedure TCustomArbitrarySparsePointGradientSampler.AssignTo(Dest: TPersistent);
- begin
- if Dest is TCustomArbitrarySparsePointGradientSampler then
- with TCustomArbitrarySparsePointGradientSampler(Dest) do
- begin
- FColorPoints := Self.FColorPoints;
- end
- else
- inherited;
- end;
- procedure TCustomArbitrarySparsePointGradientSampler.Add(Point: TFloatPoint;
- Color: TColor32);
- var
- Index: Integer;
- begin
- Index := Length(FColorPoints);
- SetLength(FColorPoints, Index + 1);
- FColorPoints[Index].Point := Point;
- FColorPoints[Index].Color32 := Color;
- end;
- procedure TCustomArbitrarySparsePointGradientSampler.Add(
- const ColorPoint: TColor32FloatPoint);
- var
- Index: Integer;
- begin
- Index := Length(FColorPoints);
- SetLength(FColorPoints, Index + 1);
- FColorPoints[Index].Point := ColorPoint.Point;
- FColorPoints[Index].Color32 := ColorPoint.Color32;
- end;
- procedure TCustomArbitrarySparsePointGradientSampler.Clear;
- begin
- SetLength(FColorPoints, 0);
- end;
- function TCustomArbitrarySparsePointGradientSampler.GetColor(
- Index: Integer): TColor32;
- begin
- if (Index >= 0) and (Index < Length(FColorPoints)) then
- Result := FColorPoints[Index].Color32
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- function TCustomArbitrarySparsePointGradientSampler.GetColorPoint(
- Index: Integer): TColor32FloatPoint;
- begin
- if (Index >= 0) and (Index < Length(FColorPoints)) then
- Result := FColorPoints[Index]
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- function TCustomArbitrarySparsePointGradientSampler.GetCount: Integer;
- begin
- Result := Length(FColorPoints);
- end;
- function TCustomArbitrarySparsePointGradientSampler.GetPoint(
- Index: Integer): TFloatPoint;
- begin
- if (Index >= 0) and (Index < Length(FColorPoints)) then
- Result := FColorPoints[Index].Point
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- procedure TCustomArbitrarySparsePointGradientSampler.SetColor(Index: Integer;
- const Value: TColor32);
- begin
- if (Index >= 0) and (Index < Length(FColorPoints)) then
- FColorPoints[Index].Color32 := Value
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- procedure TCustomArbitrarySparsePointGradientSampler.SetColorPoint(
- Index: Integer; const Value: TColor32FloatPoint);
- begin
- if (Index >= 0) and (Index < Length(FColorPoints)) then
- FColorPoints[Index] := Value
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- procedure TCustomArbitrarySparsePointGradientSampler.SetPoint(Index: Integer;
- const Value: TFloatPoint);
- begin
- if (Index >= 0) and (Index < Length(FColorPoints)) then
- FColorPoints[Index].Point := Value
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- procedure TCustomArbitrarySparsePointGradientSampler.SetColorPoints(
- ColorPoints: TArrayOfColor32FloatPoint);
- var
- Index: Integer;
- begin
- SetLength(FColorPoints, Length(ColorPoints));
- for Index := 0 to High(FColorPoints) do
- FColorPoints[Index] := ColorPoints[Index];
- end;
- procedure TCustomArbitrarySparsePointGradientSampler.SetColorPoints(
- Points: TArrayOfFloatPoint; Colors: TArrayOfColor32);
- var
- Index: Integer;
- begin
- if Length(Points) <> Length(Colors) then
- raise Exception.Create(RCStrPointCountMismatch);
- SetLength(FColorPoints, Length(Points));
- for Index := 0 to High(FColorPoints) do
- begin
- FColorPoints[Index].Point := Points[Index];
- FColorPoints[Index].Color32 := Colors[Index];
- end;
- end;
- procedure TCustomArbitrarySparsePointGradientSampler.SetPoints(
- Points: TArrayOfFloatPoint);
- var
- Index: Integer;
- begin
- if Length(FColorPoints) <> Length(Points) then
- raise Exception.Create(RCStrPointCountMismatch);
- for Index := 0 to High(Points) do
- FColorPoints[Index].Point := Points[Index];
- end;
- { TInvertedDistanceWeightingSampler }
- constructor TInvertedDistanceWeightingSampler.Create;
- begin
- inherited;
- FPower := 2;
- FScaledPower := 0.5 * FPower;
- end;
- procedure TInvertedDistanceWeightingSampler.FinalizeSampling;
- begin
- inherited;
- Finalize(FDists);
- end;
- function TInvertedDistanceWeightingSampler.GetSampleFloat(X, Y: TFloat): TColor32;
- var
- Index: Integer;
- Temp, DistSum, Scale: Double;
- R, G, B, A: TFloat;
- begin
- if Count = 1 then
- begin
- Result := FColorPoints[0].Color32;
- Exit;
- end;
- with FColorPoints[0] do
- Temp := Sqr(X - Point.X) + Sqr(Y - Point.Y);
- if FUsePower then
- Temp := Math.Power(Temp, FScaledPower);
- if Abs(Temp) > MaxSingle then
- FDists[0] := 0
- else
- FDists[0] := 1 / Max(1.0, Temp);
- DistSum := FDists[0];
- for Index := 1 to Count - 1 do
- with FColorPoints[Index] do
- begin
- Temp := Sqr(X - Point.X) + Sqr(Y - Point.Y);
- if FUsePower then
- Temp := Math.Power(Temp, FScaledPower);
- if Abs(Temp) > MaxSingle then
- FDists[Index] := 0
- else
- FDists[Index] := 1 / Max(1.0, Temp);
- DistSum := DistSum + FDists[Index];
- end;
- DistSum := 1.0 / (1E-30 + DistSum);
- Scale := FDists[0] * DistSum;
- case Count of
- 3:
- begin
- // optimization for 3-Point interpolation
- Result := Linear3PointInterpolation(FColorPoints[0].Color32,
- FColorPoints[1].Color32, FColorPoints[2].Color32, FDists[0] * DistSum,
- FDists[1] * DistSum, FDists[2] * DistSum);
- Exit;
- end;
- 4:
- begin
- // optimization for 4-Point interpolation
- Result := Linear4PointInterpolation(FColorPoints[0].Color32,
- FColorPoints[1].Color32, FColorPoints[2].Color32,
- FColorPoints[3].Color32, FDists[0] * DistSum, FDists[1] * DistSum,
- FDists[2] * DistSum, FDists[3] * DistSum);
- Exit;
- end;
- end;
- // general n-Point interpolation
- R := Scale * TColor32Entry(FColorPoints[0].Color32).R;
- G := Scale * TColor32Entry(FColorPoints[0].Color32).G;
- B := Scale * TColor32Entry(FColorPoints[0].Color32).B;
- A := Scale * TColor32Entry(FColorPoints[0].Color32).A;
- for Index := 1 to Count - 1 do
- begin
- Scale := FDists[Index] * DistSum;
- R := R + Scale * TColor32Entry(FColorPoints[Index].Color32).R;
- G := G + Scale * TColor32Entry(FColorPoints[Index].Color32).G;
- B := B + Scale * TColor32Entry(FColorPoints[Index].Color32).B;
- A := A + Scale * TColor32Entry(FColorPoints[Index].Color32).A;
- end;
- Result := Color32(Clamp(Round(R)), Clamp(Round(G)), Clamp(Round(B)),
- Clamp(Round(A)));
- end;
- procedure TInvertedDistanceWeightingSampler.PrepareSampling;
- begin
- SetLength(FDists, Count);
- FUsePower := FPower <> 2;
- FScaledPower := 0.5 * FPower;
- inherited;
- end;
- function EuclideanMetric(X, Y: TFloat; Point: TFloatPoint): TFloat;
- begin
- Result := Sqr(X - Point.X) + Sqr(Y - Point.Y);
- end;
- function ManhattanMetric(X, Y: TFloat; Point: TFloatPoint): TFloat;
- begin
- Result := Abs(X - Point.X) + Abs(Y - Point.Y);
- end;
- { TVoronoiSampler }
- constructor TVoronoiSampler.Create(Metric: TVoronoiMetric = vmEuclidean);
- begin
- FMetric := Metric;
- FMetricFunc := EuclideanMetric;
- case FMetric of
- vmEuclidean:
- FMetricFunc := @EuclideanMetric;
- vmManhattan:
- FMetricFunc := @ManhattanMetric;
- vmCustom:
- raise Exception.Create('Invalid metric');
- end;
- end;
- function TVoronoiSampler.GetSampleFloat(X, Y: TFloat): TColor32;
- var
- Index, NearestIndex: Integer;
- Distance: TFloat;
- NearestDistance: TFloat;
- begin
- NearestIndex := 0;
- NearestDistance := FMetricFunc(X, Y, FColorPoints[0].Point);
- for Index := 1 to High(FColorPoints) do
- begin
- Distance := FMetricFunc(X, Y, FColorPoints[Index].Point);
- if Distance < NearestDistance then
- begin
- NearestDistance := Distance;
- NearestIndex := Index;
- end;
- end;
- Result := FColorPoints[NearestIndex].Color32;
- end;
- procedure TVoronoiSampler.SetMetric(const Value: TVoronoiMetric);
- begin
- if FMetric <> Value then
- begin
- FMetric := Value;
- case FMetric of
- vmEuclidean:
- FMetricFunc := @EuclideanMetric;
- vmManhattan:
- FMetricFunc := @ManhattanMetric;
- end;
- MetricChanged;
- end;
- end;
- procedure TVoronoiSampler.SetMetricFunc(const Value: TVoronoiMetricFunc);
- begin
- FMetricFunc := Value;
- Metric := vmCustom;
- end;
- procedure TVoronoiSampler.MetricChanged;
- begin
- Changed;
- end;
- { TDelaunaySampler }
- procedure FastMergeSortX(const Values: TArrayOfColor32FloatPoint;
- out Indexes: TArrayOfInteger; out Bounds: TFloatRect);
- var
- Temp: TArrayOfInteger;
- procedure Merge(I1, I2, J1, J2: Integer);
- var
- I, J, K: Integer;
- begin
- if Values[Indexes[I2]].Point.X < Values[Indexes[J1]].Point.X then
- Exit;
- I := I1;
- J := J1;
- K := 0;
- repeat
- if Values[Indexes[I]].Point.X < Values[Indexes[J]].Point.X then
- begin
- Temp[K] := Indexes[I];
- Inc(I);
- end
- else
- begin
- Temp[K] := Indexes[J];
- Inc(J);
- end;
- Inc(K);
- until (I > I2) or (J > J2);
- while I <= I2 do
- begin
- Temp[K] := Indexes[I];
- Inc(I); Inc(K);
- end;
- while J <= J2 do
- begin
- Temp[K] := Indexes[J];
- Inc(J); Inc(K);
- end;
- for I := 0 to K - 1 do
- begin
- Indexes[I + I1] := Temp[I];
- end;
- end;
- procedure Recurse(I1, I2: Integer);
- var
- I, IX: Integer;
- begin
- if I1 = I2 then
- Indexes[I1] := I1
- else if Indexes[I1] = Indexes[I2] then
- begin
- if Values[I1].Point.X <= Values[I2].Point.X then
- begin
- for I := I1 to I2 do Indexes[I] := I;
- end
- else
- begin
- IX := I1 + I2;
- for I := I1 to I2 do Indexes[I] := IX - I;
- end;
- end
- else
- begin
- IX := (I1 + I2) div 2;
- Recurse(I1, IX);
- Recurse(IX + 1, I2);
- Merge(I1, IX, IX + 1, I2);
- end;
- end;
- var
- I, Index, S: Integer;
- begin
- SetLength(Temp, Length(Values));
- SetLength(Indexes, Length(Values));
- Index := 0;
- S := Math.Sign(Values[1].Point.X - Values[0].Point.X);
- if S = 0 then S := 1;
- Indexes[0] := 0;
- // initialize bounds
- Bounds.Left := Values[0].Point.X;
- Bounds.Top := Values[0].Point.Y;
- Bounds.Right := Bounds.Left;
- Bounds.Bottom := Bounds.Top;
- for I := 1 to High(Values) do
- begin
- if Math.Sign(Values[I].Point.X - Values[I - 1].Point.X) = -S then
- begin
- S := -S;
- Inc(Index);
- end;
- // determine bounds
- if Values[I].Point.X < Bounds.Left then
- Bounds.Left := Values[I].Point.X;
- if Values[I].Point.Y < Bounds.Top then
- Bounds.Top := Values[I].Point.Y;
- if Values[I].Point.X > Bounds.Right then
- Bounds.Right := Values[I].Point.X;
- if Values[I].Point.Y > Bounds.Bottom then
- Bounds.Bottom := Values[I].Point.Y;
- Indexes[I] := Index;
- end;
- Recurse(0, High(Values));
- end;
- // Note: GR32_VectorUtils contains a generic version of this function. Keep both in sync.
- function DelaunayTriangulation(Points: TArrayOfColor32FloatPoint): TArrayOfTriangleVertexIndices;
- var
- Complete: array of Byte;
- Edges: array of array [0 .. 1] of Integer;
- ByteIndex, Bit: Byte;
- MaxVerticesCount, EdgeCount, MaxEdgeCount, MaxTriangleCount: Integer;
- // For super triangle
- ScaledDeltaMax: TFloat;
- Mid: TFloatPoint;
- Bounds: TFloatRect;
- // General Variables
- SortedVertexIndices: TArrayOfInteger;
- TriangleCount, VertexCount, I, J, K: Integer;
- CenterX, CenterY, RadSqr: TFloat;
- Inside: Boolean;
- const
- CSuperTriangleCount = 3; // -> super triangle
- CTolerance = 0.000001;
- function InCircle(Pt, Pt1, Pt2, Pt3: TFloatPoint): Boolean;
- // Return TRUE if the point Pt(x, y) lies inside the circumcircle made up by
- // points Pt1(x, y) Pt2(x, y) Pt3(x, y)
- // The circumcircle centre is returned in (CenterX, CenterY) and the radius r
- // NOTE: A point on the edge is inside the circumcircle
- var
- M1, M2, MX1, MY1, MX2, MY2: Double;
- DeltaX, DeltaY, DeltaRadSqr, AbsY1Y2, AbsY2Y3: Double;
- begin
- AbsY1Y2 := Abs(Pt1.Y - Pt2.Y);
- AbsY2Y3 := Abs(Pt2.Y - Pt3.Y);
- // check for coincident points
- if (AbsY1Y2 < CTolerance) and (AbsY2Y3 < CTolerance) then
- begin
- Result := False;
- Exit;
- end;
- if AbsY1Y2 < CTolerance then
- begin
- M2 := -(Pt3.X - Pt2.X) / (Pt3.Y - Pt2.Y);
- MX2 := (Pt2.X + Pt3.X) * 0.5;
- MY2 := (Pt2.Y + Pt3.Y) * 0.5;
- CenterX := (Pt2.X + Pt1.X) * 0.5;
- CenterY := M2 * (CenterX - MX2) + MY2;
- end
- else if AbsY2Y3 < CTolerance then
- begin
- M1 := -(Pt2.X - Pt1.X) / (Pt2.Y - Pt1.Y);
- MX1 := (Pt1.X + Pt2.X) * 0.5;
- MY1 := (Pt1.Y + Pt2.Y) * 0.5;
- CenterX := (Pt3.X + Pt2.X) * 0.5;
- CenterY := M1 * (CenterX - MX1) + MY1;
- end
- else
- begin
- M1 := -(Pt2.X - Pt1.X) / (Pt2.Y - Pt1.Y);
- M2 := -(Pt3.X - Pt2.X) / (Pt3.Y - Pt2.Y);
- if Abs(M1 - M2) < CTolerance then
- begin
- Result := False;
- Exit;
- end;
- MX1 := (Pt1.X + Pt2.X) * 0.5;
- MX2 := (Pt2.X + Pt3.X) * 0.5;
- MY1 := (Pt1.Y + Pt2.Y) * 0.5;
- MY2 := (Pt2.Y + Pt3.Y) * 0.5;
- CenterX := (M1 * MX1 - M2 * Mx2 + My2 - MY1) / (M1 - M2);
- if (AbsY1Y2 > AbsY2Y3) then
- CenterY := M1 * (CenterX - MX1) + MY1
- else
- CenterY := M2 * (CenterX - MX2) + MY2;
- end;
- DeltaX := Pt2.X - CenterX;
- DeltaY := Pt2.Y - CenterY;
- RadSqr := DeltaX * DeltaX + DeltaY * DeltaY;
- DeltaX := Pt.X - CenterX;
- DeltaY := Pt.Y - CenterY;
- DeltaRadSqr := Sqr(DeltaX) + Sqr(DeltaY);
- Result := (DeltaRadSqr - RadSqr) <= CTolerance;
- end;
- begin
- VertexCount := Length(Points);
- MaxVerticesCount := VertexCount + CSuperTriangleCount;
- // Sort points by x value and find maximum and minimum vertex bounds.
- FastMergeSortX(Points, SortedVertexIndices, Bounds);
- SetLength(Points, MaxVerticesCount);
- MaxTriangleCount := 2 * (MaxVerticesCount - 1);
- SetLength(Result, MaxTriangleCount);
- MaxEdgeCount := 3 * (MaxVerticesCount - 1);
- SetLength(Edges, MaxEdgeCount);
- SetLength(Complete, (MaxTriangleCount + 7) shr 3);
- // This is to allow calculation of the bounding triangle
- with Bounds do
- begin
- ScaledDeltaMax := 30 * Max(Right - Left, Bottom - Top);
- Mid := FloatPoint((Left + Right) * 0.5, (Top + Bottom) * 0.5);
- end;
- // Set up the super triangle
- // This is a triangle which encompasses all the sample points. The super
- // triangle coordinates are added to the end of the vertex list. The super
- // triangle is the first triangle in the triangle list.
- Points[VertexCount].Point := FloatPoint(Mid.X - ScaledDeltaMax, Mid.Y - ScaledDeltaMax);
- Points[VertexCount + 1].Point := FloatPoint(Mid.X + ScaledDeltaMax, Mid.Y);
- Points[VertexCount + 2].Point := FloatPoint(Mid.X, Mid.Y + ScaledDeltaMax);
- Result[0, 0] := VertexCount;
- Result[0, 1] := VertexCount + 1;
- Result[0, 2] := VertexCount + 2;
- Complete[0] := 0;
- TriangleCount := 1;
- // Include each point one at a time into the existing mesh
- for I := 0 to VertexCount - 1 do
- begin
- EdgeCount := 0;
- // Set up the edge buffer.
- // If the point [x, y] lies inside the circumcircle then the hree edges of
- // that triangle are added to the edge buffer.
- J := 0;
- repeat
- if Complete[J shr 3] and (1 shl (J and $7)) = 0 then
- begin
- Inside := InCircle(Points[SortedVertexIndices[I]].Point,
- Points[Result[J, 0]].Point, Points[Result[J, 1]].Point,
- Points[Result[J, 2]].Point);
- ByteIndex := J shr 3;
- Bit := 1 shl (J and $7);
- if (CenterX < Points[SortedVertexIndices[I]].Point.X) and
- ((Sqr(Points[SortedVertexIndices[I]].Point.X - CenterX)) > RadSqr) then
- Complete[ByteIndex] := Complete[ByteIndex] or Bit
- else
- if Inside then
- begin
- Edges[EdgeCount + 0, 0] := Result[J, 0];
- Edges[EdgeCount + 0, 1] := Result[J, 1];
- Edges[EdgeCount + 1, 0] := Result[J, 1];
- Edges[EdgeCount + 1, 1] := Result[J, 2];
- Edges[EdgeCount + 2, 0] := Result[J, 2];
- Edges[EdgeCount + 2, 1] := Result[J, 0];
- EdgeCount := EdgeCount + 3;
- Assert(EdgeCount <= MaxEdgeCount);
- TriangleCount := TriangleCount - 1;
- Result[J] := Result[TriangleCount];
- Complete[ByteIndex] := (Complete[ByteIndex] and (not Bit))
- or (Complete[TriangleCount shr 3] and Bit);
- Continue;
- end;
- end;
- J := J + 1;
- until J >= TriangleCount;
- // Tag multiple edges
- // Note: if all triangles are specified anticlockwise then all
- // interior edges are opposite pointing in direction.
- for J := 0 to EdgeCount - 2 do
- begin
- if (Edges[J, 0] <> -1) or (Edges[J, 1] <> -1) then
- begin
- for K := J + 1 to EdgeCount - 1 do
- begin
- if (Edges[K, 0] <> -1) or (Edges[K, 1] <> -1) then
- begin
- if (Edges[J, 0] = Edges[K, 1]) and
- (Edges[J, 1] = Edges[K, 0]) then
- begin
- Edges[J, 0] := -1;
- Edges[J, 1] := -1;
- Edges[K, 1] := -1;
- Edges[K, 0] := -1;
- end;
- end;
- end;
- end;
- end;
- // Form new triangles for the current point.
- // Skipping over any tagged edges. All edges are arranged in clockwise
- // order.
- for J := 0 to EdgeCount - 1 do
- begin
- if (Edges[J, 0] <> -1) or (Edges[J, 1] <> -1) then
- begin
- Result[TriangleCount, 0] := Edges[J, 0];
- Result[TriangleCount, 1] := Edges[J, 1];
- Result[TriangleCount, 2] := SortedVertexIndices[I];
- ByteIndex := TriangleCount shr 3;
- Bit := 1 shl (TriangleCount and $7);
- Complete[ByteIndex] := Complete[ByteIndex] and not Bit;
- Inc(TriangleCount);
- Assert(TriangleCount <= MaxTriangleCount);
- end;
- end;
- end;
- // Remove triangles with supertriangle vertices
- // These are triangles which have a vertex number greater than VertexCount
- I := 0;
- repeat
- if (Result[I, 0] >= VertexCount) or
- (Result[I, 1] >= VertexCount) or
- (Result[I, 2] >= VertexCount) then
- begin
- TriangleCount := TriangleCount - 1;
- Result[I, 0] := Result[TriangleCount, 0];
- Result[I, 1] := Result[TriangleCount, 1];
- Result[I, 2] := Result[TriangleCount, 2];
- I := I - 1;
- end;
- I := I + 1;
- until I >= TriangleCount;
- SetLength(Points, Length(Points) - 3);
- SetLength(Result, TriangleCount);
- end;
- procedure TGourandShadedDelaunayTrianglesSampler.PrepareSampling;
- var
- Index: Integer;
- begin
- inherited;
- // perform triangulation
- FTriangles := DelaunayTriangulation(FColorPoints);
- // setup internal barycentric samplers
- SetLength(FBarycentric, Length(FTriangles));
- for Index := 0 to Length(FTriangles) - 1 do
- begin
- FBarycentric[Index] := TBarycentricGradientSampler.Create(
- FColorPoints[FTriangles[Index, 0]], FColorPoints[FTriangles[Index, 1]],
- FColorPoints[FTriangles[Index, 2]]);
- FBarycentric[Index].PrepareSampling;
- end;
- SetLength(FTriangles, 0);
- end;
- function TGourandShadedDelaunayTrianglesSampler.GetSampleFloat(X, Y: TFloat): TColor32;
- var
- Index: Integer;
- U, V, W: TFloat;
- Dist, MinDist: TFloat;
- MinIndex: Integer;
- begin
- if Length(FBarycentric) = 0 then
- begin
- Result := clRed32;
- Exit;
- end;
- // check first barycentric interpolator
- FBarycentric[0].CalculateBarycentricCoordinates(X, Y, U, V, W);
- if (U >= 0) and (V >= 0) and (W >= 0) then
- begin
- Result := Linear3PointInterpolation(FBarycentric[0].Color[0],
- FBarycentric[0].Color[1], FBarycentric[0].Color[2], U, V, W);
- Exit;
- end;
- // calculate minimum distance
- MinDist := Sqr(U - 0.5) + Sqr(V - 0.5) + Sqr(W - 0.5);
- MinIndex := 0;
- for Index := 1 to High(FBarycentric) do
- begin
- // check barycentric interpolator
- FBarycentric[Index].CalculateBarycentricCoordinates(X, Y, U, V, W);
- if (U >= 0) and (V >= 0) and (W >= 0) then
- begin
- Result := Linear3PointInterpolation(FBarycentric[Index].Color[0],
- FBarycentric[Index].Color[1], FBarycentric[Index].Color[2], U, V, W);
- Exit;
- end;
- // calculate distance and eventually update minimum distance
- Dist := Sqr(U - 0.5) + Sqr(V - 0.5) + Sqr(W - 0.5);
- if Dist < MinDist then
- begin
- MinDist := Dist;
- MinIndex := Index;
- end;
- end;
- FBarycentric[MinIndex].CalculateBarycentricCoordinates(X, Y, U, V, W);
- Result := Linear3PointInterpolation(FBarycentric[MinIndex].Color[0],
- FBarycentric[MinIndex].Color[1], FBarycentric[MinIndex].Color[2], U, V, W);
- end;
- procedure TGourandShadedDelaunayTrianglesSampler.FinalizeSampling;
- var
- Index: Integer;
- begin
- inherited;
- for Index := 0 to Length(FBarycentric) - 1 do
- begin
- FBarycentric[Index].FinalizeSampling;
- FBarycentric[Index].Free;
- end;
- end;
- { TCustomGradientSampler }
- constructor TCustomGradientSampler.Create(WrapMode: TWrapMode);
- begin
- inherited Create;
- FGradient := TColor32Gradient.Create(clNone32);
- FGradient.OnGradientColorsChanged := GradientChangedHandler;
- FWrapMode := WrapMode;
- WrapModeChanged;
- end;
- constructor TCustomGradientSampler.Create(ColorGradient: TColor32Gradient);
- begin
- Create;
- if Assigned(ColorGradient) then
- FGradient.Assign(ColorGradient);
- end;
- destructor TCustomGradientSampler.Destroy;
- begin
- FreeAndNil(FGradient);
- inherited;
- end;
- procedure TCustomGradientSampler.AssignTo(Dest: TPersistent);
- begin
- if Dest is TCustomGradientSampler then
- with TCustomGradientSampler(Dest) do
- begin
- FGradient.Assign(Self.FGradient);
- FInitialized := False;
- FWrapMode := Self.WrapMode;
- end
- else
- inherited;
- end;
- procedure TCustomGradientSampler.SetGradient(const Value: TColor32Gradient);
- begin
- if not Assigned(Value) then
- FGradient.ClearColorStops
- else
- Value.AssignTo(Self);
- GradientSamplerChanged;
- end;
- procedure TCustomGradientSampler.SetWrapMode(const Value: TWrapMode);
- begin
- if FWrapMode <> Value then
- begin
- FWrapMode := Value;
- WrapModeChanged;
- end;
- end;
- procedure TCustomGradientSampler.WrapModeChanged;
- begin
- end;
- function TCustomGradientSampler.GetSampleFixed(X, Y: TFixed): TColor32;
- begin
- Result := GetSampleFloat(X * FixedToFloat, Y * FixedToFloat);
- end;
- function TCustomGradientSampler.GetSampleInt(X, Y: Integer): TColor32;
- begin
- Result := GetSampleFloat(X, Y);
- end;
- procedure TCustomGradientSampler.GradientChangedHandler(Sender: TObject);
- begin
- GradientSamplerChanged;
- end;
- procedure TCustomGradientSampler.GradientSamplerChanged;
- begin
- FInitialized := False;
- end;
- procedure TCustomGradientSampler.PrepareSampling;
- begin
- inherited;
- if not FInitialized then
- begin
- UpdateInternals;
- FInitialized := True;
- end;
- end;
- { TCustomGradientLookUpTableSampler }
- procedure TCustomGradientLookUpTableSampler.AssignTo(Dest: TPersistent);
- begin
- inherited;
- if Dest is TCustomGradientLookUpTableSampler then
- with TCustomGradientLookUpTableSampler(Dest) do
- begin
- FGradientLUT.Assign(Self.FGradientLUT);
- FWrapProc := Self.FWrapProc;
- end
- end;
- constructor TCustomGradientLookUpTableSampler.Create(WrapMode: TWrapMode = wmMirror);
- begin
- FGradientLUT := TColor32LookupTable.Create;
- inherited Create(WrapMode);
- end;
- destructor TCustomGradientLookUpTableSampler.Destroy;
- begin
- FGradientLUT.Free;
- inherited;
- end;
- procedure TCustomGradientLookUpTableSampler.UpdateInternals;
- begin
- FGradient.FillColorLookUpTable(FGradientLUT);
- FLutPtr := FGradientLUT.Color32Ptr;
- FLutMask := FGradientLUT.Mask;
- FWrapProc := GetWrapProc(WrapMode, FGradientLUT.Mask);
- end;
- procedure TCustomGradientLookUpTableSampler.WrapModeChanged;
- begin
- inherited;
- FWrapProc := GetWrapProc(WrapMode);
- end;
- { TCustomCenterLutGradientSampler }
- constructor TCustomCenterLutGradientSampler.Create(WrapMode: TWrapMode = wmMirror);
- begin
- inherited Create(WrapMode);
- FCenter := FloatPoint(0, 0);
- end;
- procedure TCustomCenterLutGradientSampler.AssignTo(Dest: TPersistent);
- begin
- inherited;
- if Dest is TCustomCenterLutGradientSampler then
- TCustomCenterLutGradientSampler(Dest).FCenter := Self.FCenter;
- end;
- procedure TCustomCenterLutGradientSampler.Transform(var X, Y: TFloat);
- begin
- X := X - FCenter.X;
- Y := Y - FCenter.Y;
- inherited;
- end;
- { TConicGradientSampler }
- procedure TConicGradientSampler.AssignTo(Dest: TPersistent);
- begin
- inherited;
- if Dest is TConicGradientSampler then
- TConicGradientSampler(Dest).FAngle := Self.FAngle;
- end;
- function TConicGradientSampler.GetSampleFloat(X, Y: TFloat): TColor32;
- begin
- Transform(X, Y);
- Result := FLutPtr^[FWrapProc(Round(FScale * Abs(FAngle + ArcTan2(Y, X))),
- FLutMask)];
- end;
- procedure TConicGradientSampler.UpdateInternals;
- begin
- inherited;
- FLutMask := FGradientLUT.Mask;
- FScale := FLutMask / Pi;
- end;
- { TCustomCenterRadiusLutGradientSampler }
- constructor TCustomCenterRadiusLutGradientSampler.Create(WrapMode: TWrapMode = wmMirror);
- begin
- inherited Create(WrapMode);
- FRadius := 1;
- RadiusChanged;
- end;
- procedure TCustomCenterRadiusLutGradientSampler.AssignTo(Dest: TPersistent);
- begin
- inherited;
- if Dest is TCustomCenterRadiusLutGradientSampler then
- TCustomCenterRadiusLutGradientSampler(Dest).FRadius := Self.FRadius;
- end;
- procedure TCustomCenterRadiusLutGradientSampler.RadiusChanged;
- begin
- FInitialized := False;
- end;
- procedure TCustomCenterRadiusLutGradientSampler.SetRadius(
- const Value: TFloat);
- begin
- if (FRadius <> Value) and (Value > 0) then
- begin
- FRadius := Value;
- RadiusChanged;
- end;
- end;
- { TRadialGradientSampler }
- function TRadialGradientSampler.GetSampleFloat(X, Y: TFloat): TColor32;
- begin
- Transform(X, Y);
- Result := FGradientLUT.Color32Ptr^[
- FWrapProc(Round(Sqrt(Sqr(X) + Sqr(Y)) * FScale), FLutMask)];
- end;
- procedure TRadialGradientSampler.UpdateInternals;
- begin
- inherited;
- FScale := FLutMask / FRadius;
- end;
- { TRadialExGradientSampler }
- constructor TRadialExGradientSampler.Create(WrapMode: TWrapMode);
- begin
- inherited Create(WrapMode);
- FRadius1 := 1;
- FRadius2 := 1;
- end;
- procedure TRadialExGradientSampler.AssignTo(Dest: TPersistent);
- begin
- inherited;
- if Dest is TRadialExGradientSampler then
- begin
- TRadialExGradientSampler(Dest).FCenter1 := Self.FCenter1;
- TRadialExGradientSampler(Dest).FCenter2 := Self.FCenter2;
- TRadialExGradientSampler(Dest).FRadius1 := Self.FRadius1;
- TRadialExGradientSampler(Dest).FRadius2 := Self.FRadius2;
- end;
- end;
- procedure TRadialExGradientSampler.SetCenter1(const Value: TFloatPoint);
- begin
- FCenter1 := Value;
- FInitialized := False;
- end;
- procedure TRadialExGradientSampler.SetCenter2(const Value: TFloatPoint);
- begin
- FCenter2 := Value;
- FInitialized := False;
- end;
- procedure TRadialExGradientSampler.SetRadius1(const Value: TFloat);
- begin
- if (FRadius1 <> Value) and (Value > 0) then
- begin
- FRadius1 := Value;
- FInitialized := False;
- end;
- end;
- procedure TRadialExGradientSampler.SetRadius2(const Value: TFloat);
- begin
- if (FRadius2 <> Value) and (Value > 0) then
- begin
- FRadius2 := Value;
- FInitialized := False;
- end;
- end;
- procedure TRadialExGradientSampler.Transform(var X, Y: TFloat);
- begin
- X := X - FCenter1.X;
- Y := Y - FCenter1.Y;
- inherited;
- end;
- procedure TRadialExGradientSampler.UpdateInternals;
- begin
- inherited;
- // Calculate difference between the two circles
- FDeltaCenter := FCenter2 - FCenter1;
- FDeltaRadius := FRadius2 - FRadius1;
- FPolynomA := Sqr(FDeltaCenter.x) + Sqr(FDeltaCenter.y) - Sqr(FDeltaRadius);
- FPolynomAInv := 1 / FPolynomA;
- FRadius12 := FRadius1 * FDeltaRadius;
- FRadius1Sqr := Sqr(FRadius1);
- end;
- function TRadialExGradientSampler.GetSampleFloat(X, Y: TFloat): TColor32;
- var
- p: TFloatPoint;
- B, C, D: TFloat;
- Omega: TFloat;
- SqrtD: TFloat;
- Index: integer;
- begin
- Transform(X, Y);
- p := FloatPoint(X, Y);
- B := Dot(p, FDeltaCenter) + FRadius12;
- C := Dot(p, p) - FRadius1Sqr;
- if (Abs(FPolynomA) > 0.0000001) then
- begin
- // Discriminant
- D := Sqr(B) - (FPolynomA * C);
- if (D < 0) then
- Exit(FGradientLUT.Color32Ptr^[FLutMask]);
- SqrtD := Sqrt(D);
- // First root
- Omega := (B + SqrtD) * FPolynomAInv;
- if (Omega < 0) or (Omega > 1) then
- // Second root
- Omega := (B - SqrtD) * FPolynomAInv;
- end else
- begin
- Omega := 0.5 * C / B;
- if (Omega < 0) or (Omega > 1) then
- Exit(0);
- end;
- Index := Round(Omega * FLutMask);
- Result := FGradientLUT.Color32Ptr^[FWrapProc(Index, FLutMask)];
- end;
- { TCustomCenterRadiusAngleLutGradientSampler }
- constructor TCustomCenterRadiusAngleLutGradientSampler.Create(WrapMode: TWrapMode = wmMirror);
- begin
- inherited Create(WrapMode);
- FAngle := 0;
- FSinCos.X := 1;
- FSinCos.Y := 0;
- end;
- procedure TCustomCenterRadiusAngleLutGradientSampler.AssignTo(
- Dest: TPersistent);
- begin
- inherited;
- if Dest is TCustomCenterRadiusAngleLutGradientSampler then
- with TCustomCenterRadiusAngleLutGradientSampler(Dest) do
- begin
- FAngle := Self.FAngle;
- FSinCos := Self.FSinCos;
- end;
- end;
- procedure TCustomCenterRadiusAngleLutGradientSampler.RadiusChanged;
- begin
- inherited;
- FInitialized := False;
- end;
- procedure TCustomCenterRadiusAngleLutGradientSampler.AngleChanged;
- begin
- GR32_Math.SinCos(FAngle, FSinCos.X, FSinCos.Y);
- end;
- procedure TCustomCenterRadiusAngleLutGradientSampler.SetAngle(
- const Value: TFloat);
- begin
- if FAngle <> Value then
- begin
- FAngle := Value;
- AngleChanged;
- end;
- end;
- procedure TCustomCenterRadiusAngleLutGradientSampler.Transform(var X,
- Y: TFloat);
- var
- Temp: TFloat;
- begin
- X := X - FCenter.X;
- Y := Y - FCenter.Y;
- Temp := X * FSinCos.X + Y * FSinCos.Y;
- Y := X * FSinCos.Y - Y * FSinCos.X;
- X := Temp;
- end;
- { TDiamondGradientSampler }
- function TDiamondGradientSampler.GetSampleFloat(X, Y: TFloat): TColor32;
- begin
- Transform(X, Y);
- Result := FLutPtr^[FWrapProc(Round(Max(Abs(X), Abs(Y)) * FScale), FLutMask)];
- end;
- procedure TDiamondGradientSampler.UpdateInternals;
- begin
- inherited;
- FScale := FLutMask / FRadius;
- end;
- { TXGradientSampler }
- function TXGradientSampler.GetSampleFloat(X, Y: TFloat): TColor32;
- begin
- Transform(X, Y);
- Result := FLutPtr^[FWrapProc(Round(X * FScale), FLutMask)];
- end;
- function TXGradientSampler.GetStartPoint: TFloatPoint;
- begin
- Result := FCenter;
- end;
- function TXGradientSampler.GetEndPoint: TFloatPoint;
- var
- X, Y: TFloat;
- begin
- GR32_Math.SinCos(Angle - 0.5 * Pi, X, Y);
- Result := FloatPoint(FCenter.X + X, FCenter.Y + Y);
- end;
- procedure TXGradientSampler.SetEndPoint(const Value: TFloatPoint);
- begin
- SetPoints(StartPoint, Value);
- end;
- procedure TXGradientSampler.SetPoints(const StartPoint, EndPoint: TFloatPoint);
- begin
- FCenter := StartPoint;
- Radius := Distance(EndPoint, StartPoint);
- Angle := 0.5 * Pi + GetAngleOfPt2FromPt1(EndPoint, StartPoint);
- end;
- procedure TXGradientSampler.SetStartPoint(const Value: TFloatPoint);
- begin
- SetPoints(Value, EndPoint);
- end;
- procedure TXGradientSampler.SimpleGradient(
- const StartPoint: TFloatPoint; StartColor: TColor32;
- const EndPoint: TFloatPoint; EndColor: TColor32);
- begin
- SetPoints(StartPoint, EndPoint);
- if Assigned(FGradient) then
- begin
- FGradient.ClearColorStops;
- FGradient.StartColor := StartColor;
- FGradient.EndColor := EndColor;
- end;
- end;
- procedure TXGradientSampler.UpdateInternals;
- begin
- inherited;
- FScale := FLutMask / FRadius;
- end;
- { TXYGradientSampler }
- function TXYGradientSampler.GetSampleFloat(X, Y: TFloat): TColor32;
- begin
- Transform(X, Y);
- Result := FLutPtr^[FWrapProc(Round((Abs(X) * Abs(Y)) * FScale), FLutMask)];
- end;
- procedure TXYGradientSampler.UpdateInternals;
- begin
- inherited;
- FScale := FLutMask / Sqr(FRadius);
- end;
- { TXYSqrtGradientSampler }
- function TXYSqrtGradientSampler.GetSampleFloat(X, Y: TFloat): TColor32;
- begin
- Transform(X, Y);
- Result := FLutPtr^[FWrapProc(Round(Sqrt(Abs(X) * Abs(Y)) * FScale), FLutMask)];
- end;
- procedure TXYSqrtGradientSampler.UpdateInternals;
- begin
- inherited;
- FScale := FLutMask / FRadius;
- end;
- {TCustomGradientPolygonFiller}
- constructor TCustomGradientPolygonFiller.Create;
- begin
- Create(TColor32Gradient.Create(clNone32));
- FGradient.OnGradientColorsChanged := GradientColorsChangedHandler;
- FOwnsGradient := True;
- FWrapMode := wmClamp;
- FWrapProc := Clamp;
- end;
- constructor TCustomGradientPolygonFiller.Create(ColorGradient: TColor32Gradient);
- begin
- FOwnsGradient := False;
- FGradient := ColorGradient;
- inherited Create;
- FWrapMode := wmClamp;
- FWrapProc := Clamp;
- end;
- destructor TCustomGradientPolygonFiller.Destroy;
- begin
- if Assigned(FGradient) then
- if FOwnsGradient then
- FGradient.Free
- else
- FGradient.OnGradientColorsChanged := nil;
- inherited;
- end;
- procedure TCustomGradientPolygonFiller.FillLineNone(Dst: PColor32; DstX,
- DstY, Length: Integer; AlphaValues: PColor32; CombineMode: TCombineMode);
- begin
- // do nothing!
- end;
- procedure TCustomGradientPolygonFiller.FillLineSolid(Dst: PColor32; DstX,
- DstY, Length: Integer; AlphaValues: PColor32; CombineMode: TCombineMode);
- begin
- FillLineAlpha(Dst, AlphaValues, Length, FGradient.StartColor, CombineMode);
- end;
- procedure TCustomGradientPolygonFiller.GradientColorsChangedHandler(
- Sender: TObject);
- begin
- GradientFillerChanged;
- end;
- procedure TCustomGradientPolygonFiller.GradientFillerChanged;
- begin
- // do nothing
- end;
- procedure TCustomGradientPolygonFiller.SetWrapMode(const Value: TWrapMode);
- begin
- if FWrapMode <> Value then
- begin
- FWrapMode := Value;
- WrapModeChanged;
- end;
- end;
- procedure TCustomGradientPolygonFiller.WrapModeChanged;
- begin
- FWrapProc := GetWrapProc(FWrapMode);
- end;
- { TBarycentricGradientPolygonFiller }
- procedure TBarycentricGradientPolygonFiller.BeginRendering;
- var
- NormScale: TFloat;
- begin
- inherited;
- NormScale := 1 / ((FColorPoints[1].Point.Y - FColorPoints[2].Point.Y) *
- (FColorPoints[0].Point.X - FColorPoints[2].Point.X) +
- (FColorPoints[2].Point.X - FColorPoints[1].Point.X) *
- (FColorPoints[0].Point.Y - FColorPoints[2].Point.Y));
- FDists[0].X := NormScale * (FColorPoints[2].Point.X - FColorPoints[1].Point.X);
- FDists[0].Y := NormScale * (FColorPoints[1].Point.Y - FColorPoints[2].Point.Y);
- FDists[1].X := NormScale * (FColorPoints[0].Point.X - FColorPoints[2].Point.X);
- FDists[1].Y := NormScale * (FColorPoints[2].Point.Y - FColorPoints[0].Point.Y);
- end;
- procedure TBarycentricGradientPolygonFiller.FillLine(Dst: PColor32; DstX, DstY,
- Length: Integer; AlphaValues: PColor32; CombineMode: TCombineMode);
- var
- X: Integer;
- Color32: TColor32;
- Temp, DotY1, DotY2: TFloat;
- Barycentric: array [0..1] of TFloat;
- BlendMemEx: TBlendMemEx;
- begin
- BlendMemEx := BLEND_MEM_EX[CombineMode]^;
- Temp := DstY - FColorPoints[2].Point.Y;
- DotY1 := FDists[0].X * Temp;
- DotY2 := FDists[1].X * Temp;
- for X := DstX to DstX + Length - 1 do
- begin
- Temp := (X - FColorPoints[2].Point.X);
- Barycentric[0] := FDists[0].Y * Temp + DotY1;
- Barycentric[1] := FDists[1].Y * Temp + DotY2;
- Color32 := Linear3PointInterpolation(FColorPoints[0].Color32,
- FColorPoints[1].Color32, FColorPoints[2].Color32,
- Barycentric[0], Barycentric[1], 1 - Barycentric[1] - Barycentric[0]);
- BlendMemEx(Color32, Dst^, AlphaValues^);
- Inc(Dst);
- Inc(AlphaValues);
- end;
- end;
- function TBarycentricGradientPolygonFiller.GetColor(Index: Integer): TColor32;
- begin
- if Index in [0 .. 2] then
- Result := FColorPoints[Index].Color32
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- function TBarycentricGradientPolygonFiller.GetColorPoint(
- Index: Integer): TColor32FloatPoint;
- begin
- if Index in [0 .. 2] then
- Result := FColorPoints[Index]
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- function TBarycentricGradientPolygonFiller.GetCount: Integer;
- begin
- Result := 3;
- end;
- function TBarycentricGradientPolygonFiller.GetFillLine: TFillLineEvent;
- begin
- Result := FillLine;
- end;
- function TBarycentricGradientPolygonFiller.GetPoint(
- Index: Integer): TFloatPoint;
- begin
- if Index in [0 .. 2] then
- Result := FColorPoints[Index].Point
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- class function TBarycentricGradientPolygonFiller.Linear3PointInterpolation(
- A, B, C: TColor32; WeightA, WeightB, WeightC: Single): TColor32;
- begin
- Result := Linear3PointInterpolation(A, B, C, WeightA, WeightB, WeightC);
- end;
- procedure TBarycentricGradientPolygonFiller.SetColor(Index: Integer;
- const Value: TColor32);
- begin
- if Index in [0 .. 2] then
- FColorPoints[Index].Color32 := Value
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- procedure TBarycentricGradientPolygonFiller.SetColorPoints(
- ColorPoints: TArrayOfColor32FloatPoint);
- begin
- if Length(ColorPoints) <> 3 then
- raise Exception.Create(RCStrOnlyExactly3Point);
- FColorPoints[0] := ColorPoints[0];
- FColorPoints[1] := ColorPoints[1];
- FColorPoints[2] := ColorPoints[2];
- end;
- procedure TBarycentricGradientPolygonFiller.SetColorPoint(Index: Integer;
- const Value: TColor32FloatPoint);
- begin
- if Index in [0 .. 2] then
- FColorPoints[Index] := Value
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- procedure TBarycentricGradientPolygonFiller.SetColorPoints(
- Points: TArrayOfFloatPoint; Colors: TArrayOfColor32);
- begin
- if (Length(Points) <> 3) or (Length(Colors) <> 3) then
- raise Exception.Create(RCStrOnlyExactly3Point);
- FColorPoints[0] := Color32FloatPoint(Colors[0], Points[0]);
- FColorPoints[1] := Color32FloatPoint(Colors[1], Points[1]);
- FColorPoints[2] := Color32FloatPoint(Colors[2], Points[2]);
- end;
- procedure TBarycentricGradientPolygonFiller.SetPoint(Index: Integer;
- const Value: TFloatPoint);
- begin
- if Index in [0 .. 2] then
- FColorPoints[Index].Point := Value
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- procedure TBarycentricGradientPolygonFiller.SetPoints(
- Points: TArrayOfFloatPoint);
- var
- Index: Integer;
- begin
- if Length(Points) <> 3 then
- raise Exception.Create(RCStrOnlyExactly3Point);
- for Index := 0 to 2 do
- FColorPoints[Index].Point := Points[Index];
- end;
- { TCustomArbitrarySparsePointGradientPolygonFiller }
- procedure TCustomArbitrarySparsePointGradientPolygonFiller.Add(
- const Point: TFloatPoint;
- Color: TColor32);
- var
- Index: Integer;
- begin
- Index := Length(FColorPoints);
- SetLength(FColorPoints, Index + 1);
- FColorPoints[Index].Point := Point;
- FColorPoints[Index].Color32 := Color;
- end;
- procedure TCustomArbitrarySparsePointGradientPolygonFiller.Add(
- const ColorPoint: TColor32FloatPoint);
- var
- Index: Integer;
- begin
- Index := Length(FColorPoints);
- SetLength(FColorPoints, Index + 1);
- FColorPoints[Index].Point := ColorPoint.Point;
- FColorPoints[Index].Color32 := ColorPoint.Color32;
- end;
- procedure TCustomArbitrarySparsePointGradientPolygonFiller.Clear;
- begin
- SetLength(FColorPoints, 0);
- end;
- function TCustomArbitrarySparsePointGradientPolygonFiller.GetColor(
- Index: Integer): TColor32;
- begin
- if (Index >= 0) and (Index < Length(FColorPoints)) then
- Result := FColorPoints[Index].Color32
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- function TCustomArbitrarySparsePointGradientPolygonFiller.GetColorPoint(
- Index: Integer): TColor32FloatPoint;
- begin
- if (Index >= 0) and (Index < Length(FColorPoints)) then
- Result := FColorPoints[Index]
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- function TCustomArbitrarySparsePointGradientPolygonFiller.GetCount: Integer;
- begin
- Result := Length(FColorPoints);
- end;
- function TCustomArbitrarySparsePointGradientPolygonFiller.GetPoint(
- Index: Integer): TFloatPoint;
- begin
- if (Index >= 0) and (Index < Length(FColorPoints)) then
- Result := FColorPoints[Index].Point
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- procedure TCustomArbitrarySparsePointGradientPolygonFiller.SetColor(Index: Integer;
- const Value: TColor32);
- begin
- if (Index >= 0) and (Index < Length(FColorPoints)) then
- FColorPoints[Index].Color32 := Value
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- procedure TCustomArbitrarySparsePointGradientPolygonFiller.SetColorPoint(
- Index: Integer; const Value: TColor32FloatPoint);
- begin
- if (Index >= 0) and (Index < Length(FColorPoints)) then
- FColorPoints[Index] := Value
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- procedure TCustomArbitrarySparsePointGradientPolygonFiller.SetPoint(Index: Integer;
- const Value: TFloatPoint);
- begin
- if (Index >= 0) and (Index < Length(FColorPoints)) then
- FColorPoints[Index].Point := Value
- else
- raise Exception.CreateFmt(RCStrIndexOutOfBounds, [Index]);
- end;
- procedure TCustomArbitrarySparsePointGradientPolygonFiller.SetColorPoints(
- ColorPoints: TArrayOfColor32FloatPoint);
- var
- Index: Integer;
- begin
- SetLength(FColorPoints, Length(ColorPoints));
- for Index := 0 to High(FColorPoints) do
- FColorPoints[Index] := ColorPoints[Index];
- end;
- procedure TCustomArbitrarySparsePointGradientPolygonFiller.SetColorPoints(
- Points: TArrayOfFloatPoint; Colors: TArrayOfColor32);
- var
- Index: Integer;
- begin
- if Length(Points) <> Length(Colors) then
- raise Exception.Create(RCStrPointCountMismatch);
- SetLength(FColorPoints, Length(Points));
- for Index := 0 to High(FColorPoints) do
- begin
- FColorPoints[Index].Point := Points[Index];
- FColorPoints[Index].Color32 := Colors[Index];
- end;
- end;
- procedure TCustomArbitrarySparsePointGradientPolygonFiller.SetPoints(
- Points: TArrayOfFloatPoint);
- var
- Index: Integer;
- begin
- if Length(FColorPoints) <> Length(Points) then
- raise Exception.Create(RCStrPointCountMismatch);
- for Index := 0 to High(Points) do
- FColorPoints[Index].Point := Points[Index];
- end;
- { TGourandShadedDelaunayTrianglesPolygonFiller }
- procedure TGourandShadedDelaunayTrianglesPolygonFiller.BeginRendering;
- var
- Index: Integer;
- begin
- inherited;
- // perform triangulation
- FTriangles := DelaunayTriangulation(FColorPoints);
- // setup internal barycentric samplers
- SetLength(FBarycentric, Length(FTriangles));
- for Index := 0 to Length(FTriangles) - 1 do
- begin
- FBarycentric[Index] := TBarycentricGradientSampler.Create(
- FColorPoints[FTriangles[Index, 0]], FColorPoints[FTriangles[Index, 1]],
- FColorPoints[FTriangles[Index, 2]]);
- FBarycentric[Index].PrepareSampling;
- end;
- SetLength(FTriangles, 0);
- end;
- procedure TGourandShadedDelaunayTrianglesPolygonFiller.FillLine3(Dst: PColor32;
- DstX, DstY, Count: Integer; AlphaValues: PColor32; CombineMode: TCombineMode);
- var
- X: Integer;
- BlendMemEx: TBlendMemEx;
- begin
- BlendMemEx := BLEND_MEM_EX[CombineMode]^;
- for X := DstX to DstX + Count - 1 do
- begin
- BlendMemEx(FBarycentric[0].GetSampleFloat(X, DstY), Dst^, AlphaValues^);
- Inc(Dst);
- Inc(AlphaValues);
- end;
- end;
- procedure TGourandShadedDelaunayTrianglesPolygonFiller.FillLine(Dst: PColor32;
- DstX, DstY, Count: Integer; AlphaValues: PColor32; CombineMode: TCombineMode);
- var
- Index: Integer;
- U, V, W: TFloat;
- Dist, MinDist: TFloat;
- MinIndex: Integer;
- X: Integer;
- Color32: TColor32;
- BlendMemEx: TBlendMemEx;
- label
- DrawColor;
- begin
- BlendMemEx := BLEND_MEM_EX[CombineMode]^;
- for X := DstX to DstX + Count - 1 do
- begin
- // check first barycentric interpolator
- FBarycentric[0].CalculateBarycentricCoordinates(X, DstY, U, V, W);
- if (U >= 0) and (V >= 0) and (W >= 0) then
- begin
- Color32 := Linear3PointInterpolation(FBarycentric[0].Color[0],
- FBarycentric[0].Color[1], FBarycentric[0].Color[2], U, V, W);
- goto DrawColor;
- end;
- // calculate minimum distance
- MinDist := Sqr(U - 0.5) + Sqr(V - 0.5) + Sqr(W - 0.5);
- MinIndex := 0;
- for Index := 1 to High(FBarycentric) do
- begin
- // check barycentric interpolator
- FBarycentric[Index].CalculateBarycentricCoordinates(X, DstY, U, V, W);
- if (U >= 0) and (V >= 0) and (W >= 0) then
- begin
- Color32 := Linear3PointInterpolation(FBarycentric[Index].Color[0],
- FBarycentric[Index].Color[1], FBarycentric[Index].Color[2], U, V, W);
- goto DrawColor;
- end;
- // calculate distance and eventually update minimum distance
- Dist := Sqr(U - 0.5) + Sqr(V - 0.5) + Sqr(W - 0.5);
- if Dist < MinDist then
- begin
- MinDist := Dist;
- MinIndex := Index;
- end;
- end;
- FBarycentric[MinIndex].CalculateBarycentricCoordinates(X, DstY, U, V, W);
- Color32 := Linear3PointInterpolation(FBarycentric[MinIndex].Color[0],
- FBarycentric[MinIndex].Color[1], FBarycentric[MinIndex].Color[2], U, V, W);
- DrawColor:
- BlendMemEx(Color32, Dst^, AlphaValues^);
- Inc(Dst);
- Inc(AlphaValues);
- end;
- end;
- function TGourandShadedDelaunayTrianglesPolygonFiller.GetFillLine: TFillLineEvent;
- begin
- case Count of
- 0 .. 2:
- raise Exception.Create('Too few color points available');
- 3:
- Result := FillLine3;
- else
- Result := FillLine;
- end;
- end;
- { TCustomGradientLookupTablePolygonFiller }
- constructor TCustomGradientLookupTablePolygonFiller.Create;
- begin
- inherited Create;
- FUseLookUpTable := True;
- // eventually create lookup table if not specified otherwise
- if not Assigned(FGradientLUT) then
- begin
- FGradientLUT := TColor32LookupTable.Create;
- FGradientLUT.OnOrderChanged := LookUpTableChangedHandler;
- FOwnsLUT := True;
- end;
- end;
- constructor TCustomGradientLookupTablePolygonFiller.Create(
- LookupTable: TColor32LookupTable);
- begin
- if not Assigned(LookupTable) then
- raise Exception.Create(RCStrNoLookupTablePassed);
- FGradientLUT := LookupTable;
- FUseLookUpTable := True;
- FOwnsLUT := False;
- FGradient := nil;
- FOwnsGradient := False;
- FWrapMode := wmClamp;
- FWrapProc := Clamp;
- end;
- destructor TCustomGradientLookupTablePolygonFiller.Destroy;
- begin
- if FOwnsLUT and Assigned(FGradientLUT) then
- FGradientLUT.Free;
- inherited;
- end;
- function TCustomGradientLookupTablePolygonFiller.GetLUTNeedsUpdate: Boolean;
- begin
- Result := FLUTNeedsUpdate or (FUseLookUpTable and (not FOwnsLUT));
- end;
- procedure TCustomGradientLookupTablePolygonFiller.GradientFillerChanged;
- begin
- FLUTNeedsUpdate := True;
- end;
- procedure TCustomGradientLookupTablePolygonFiller.SetGradientLUT(
- const Value: TColor32LookupTable);
- begin
- if FGradientLUT <> Value then
- begin
- // check whether current look up table is owned and eventually free it
- if FOwnsLUT then
- FGradientLUT.Free;
- // set link to passed look up table
- FGradientLUT := Value;
- // if no look up table is specified don't use a look up table
- if not Assigned(FGradientLUT) then
- UseLookUpTable := False;
- end;
- end;
- procedure TCustomGradientLookupTablePolygonFiller.SetUseLookUpTable(
- const Value: Boolean);
- begin
- if FUseLookUpTable <> Value then
- begin
- FUseLookUpTable := Value;
- UseLookUpTableChanged;
- end;
- end;
- procedure TCustomGradientLookupTablePolygonFiller.UseLookUpTableChanged;
- begin
- if FUseLookUpTable then
- if not Assigned(FGradientLUT) then
- begin
- FGradientLUT := TColor32LookupTable.Create;
- FGradientLUT.OnOrderChanged := LookUpTableChangedHandler;
- FOwnsLUT := True;
- end
- else
- else
- if FOwnsLUT then
- begin
- if Assigned(FGradientLUT) then
- FreeAndNil(FGradientLUT);
- FOwnsLUT := False;
- end
- end;
- procedure TCustomGradientLookupTablePolygonFiller.LookUpTableChangedHandler(Sender: TObject);
- begin
- FLUTNeedsUpdate := True;
- end;
- { TCustomLinearGradientPolygonFiller }
- procedure TCustomLinearGradientPolygonFiller.SetStartPoint(
- const Value: TFloatPoint);
- begin
- if (FStartPoint.X <> Value.X) or (FStartPoint.Y <> Value.Y) then
- begin
- FStartPoint := Value;
- StartPointChanged;
- end;
- end;
- procedure TCustomLinearGradientPolygonFiller.SimpleGradient(
- const StartPoint: TFloatPoint; StartColor: TColor32;
- const EndPoint: TFloatPoint; EndColor: TColor32);
- begin
- SetPoints(StartPoint, EndPoint);
- if Assigned(FGradient) then
- begin
- FGradient.ClearColorStops;
- FGradient.StartColor := StartColor;
- FGradient.EndColor := EndColor;
- end;
- end;
- procedure TCustomLinearGradientPolygonFiller.SimpleGradientX(
- const StartX: TFloat; StartColor: TColor32; const EndX: TFloat;
- EndColor: TColor32);
- begin
- SimpleGradient(
- FloatPoint(StartX, 0), StartColor,
- FloatPoint(EndX, 0), EndColor);
- end;
- procedure TCustomLinearGradientPolygonFiller.SimpleGradientY(
- const StartY: TFloat; StartColor: TColor32; const EndY: TFloat;
- EndColor: TColor32);
- begin
- SimpleGradient(
- FloatPoint(0, StartY), StartColor,
- FloatPoint(0, EndY), EndColor);
- end;
- procedure TCustomLinearGradientPolygonFiller.SetEndPoint(
- const Value: TFloatPoint);
- begin
- if (FEndPoint.X <> Value.X) or (FEndPoint.Y <> Value.Y) then
- begin
- FEndPoint := Value;
- EndPointChanged;
- end;
- end;
- procedure TCustomLinearGradientPolygonFiller.SetPoints(const StartPoint,
- EndPoint: TFloatPoint);
- begin
- FStartPoint := StartPoint;
- FEndPoint := EndPoint;
- GradientFillerChanged;
- UpdateIncline;
- end;
- procedure TCustomLinearGradientPolygonFiller.StartPointChanged;
- begin
- GradientFillerChanged;
- UpdateIncline;
- end;
- procedure TCustomLinearGradientPolygonFiller.EndPointChanged;
- begin
- GradientFillerChanged;
- UpdateIncline;
- end;
- procedure TCustomLinearGradientPolygonFiller.UpdateIncline;
- begin
- if (FEndPoint.X - FStartPoint.X) <> 0 then
- FIncline := (FEndPoint.Y - FStartPoint.Y) / (FEndPoint.X - FStartPoint.X)
- else
- if (FEndPoint.Y - FStartPoint.Y) <> 0 then
- FIncline := 1 / (FEndPoint.Y - FStartPoint.Y);
- end;
- { TLinearGradientPolygonFiller }
- constructor TLinearGradientPolygonFiller.Create(ColorGradient: TColor32Gradient);
- begin
- Create(ColorGradient, True);
- end;
- constructor TLinearGradientPolygonFiller.Create(ColorGradient: TColor32Gradient; UseLookupTable: Boolean);
- begin
- // create lookup table (and set 'own' & 'use' flags)
- FGradientLUT := TColor32LookupTable.Create;
- FGradientLUT.OnOrderChanged := LookUpTableChangedHandler;
- FOwnsLUT := True;
- FUseLookUpTable := UseLookupTable;
- inherited Create(ColorGradient);
- FGradient.OnGradientColorsChanged := GradientColorsChangedHandler;
- end;
- function TLinearGradientPolygonFiller.ColorStopToScanLine(Index, Y: Integer): TFloat;
- var
- Offset: array [0 .. 1] of TFloat;
- begin
- Offset[0] := FGradient.FGradientColors[Index].Offset;
- Offset[1] := 1.0 - Offset[0];
- Result :=
- Offset[1] * FStartPoint.X +
- Offset[0] * FEndPoint.X +
- FIncline * (
- Offset[1] * (FStartPoint.Y - Y) +
- Offset[0] * (FEndPoint.Y - Y)
- );
- end;
- procedure TLinearGradientPolygonFiller.UseLookUpTableChanged;
- begin
- inherited;
- // Perfect gradients are only implemented for WrapMode = wmClamp
- if (not FUseLookUpTable) and (WrapMode <> wmClamp) then
- WrapMode := wmClamp;
- end;
- procedure TLinearGradientPolygonFiller.WrapModeChanged;
- begin
- inherited;
- // Perfect gradients are only implemented for WrapMode = wmClamp
- if (not FUseLookUpTable) and (WrapMode <> wmClamp) then
- UseLookUpTable := True;
- end;
- function TLinearGradientPolygonFiller.GetFillLine: TFillLineEvent;
- var
- GradientCount: Integer;
- begin
- if (FGradient <> nil) then
- GradientCount := FGradient.GradientCount
- else
- GradientCount := FGradientLUT.Size;
- case GradientCount of
- 0:
- Result := FillLineNone;
- 1:
- Result := FillLineSolid;
- else
- if FUseLookUpTable then
- begin
- if (FWrapMode = wmClamp) then
- begin
- if FStartPoint.X = FEndPoint.X then
- if FStartPoint.Y = FEndPoint.Y then
- Result := FillLineVerticalPadExtreme
- else
- Result := FillLineVerticalPad
- else
- if FStartPoint.X < FEndPoint.X then
- Result := FillLineHorizontalPadPos
- else
- Result := FillLineHorizontalPadNeg;
- end else
- // wmMirror, wmRepeat, wmReflect
- begin
- if FStartPoint.X = FEndPoint.X then
- Result := FillLineVerticalWrap
- else
- if FStartPoint.X < FEndPoint.X then
- Result := FillLineHorizontalWrapPos
- else
- Result := FillLineHorizontalWrapNeg;
- end;
- end else
- if FStartPoint.X = FEndPoint.X then
- begin
- if FStartPoint.Y = FEndPoint.Y then
- Result := FillLineVerticalExtreme
- else
- Result := FillLineVertical;
- end else
- if FStartPoint.X < FEndPoint.X then
- Result := FillLinePositive
- else
- Result := FillLineNegative;
- end;
- end;
- procedure TLinearGradientPolygonFiller.FillLineVertical(Dst: PColor32; DstX,
- DstY, Length: Integer; AlphaValues: PColor32; CombineMode: TCombineMode);
- var
- X: Integer;
- Color32: TColor32;
- BlendMemEx: TBlendMemEx;
- begin
- BlendMemEx := BLEND_MEM_EX[CombineMode]^;
- Color32 := FGradient.GetColorAt((DstY - FStartPoint.Y) * FIncline);
- for X := DstX to DstX + Length - 1 do
- begin
- BlendMemEx(Color32, Dst^, AlphaValues^);
- Inc(Dst);
- Inc(AlphaValues);
- end;
- end;
- procedure TLinearGradientPolygonFiller.FillLineVerticalExtreme(Dst: PColor32;
- DstX, DstY, Length: Integer; AlphaValues: PColor32; CombineMode: TCombineMode);
- var
- X: Integer;
- Color32: TColor32;
- BlendMemEx: TBlendMemEx;
- begin
- BlendMemEx := BLEND_MEM_EX[CombineMode]^;
- if DstY < FStartPoint.Y then
- Color32 := FGradient.StartColor
- else
- Color32 := FGradient.EndColor;
- for X := DstX to DstX + Length - 1 do
- begin
- BlendMemEx(Color32, Dst^, AlphaValues^);
- Inc(Dst);
- Inc(AlphaValues);
- end;
- end;
- procedure TLinearGradientPolygonFiller.FillLinePositive(Dst: PColor32; DstX,
- DstY, Length: Integer; AlphaValues: PColor32; CombineMode: TCombineMode);
- var
- X, Index: Integer;
- IntScale, IntValue: Integer;
- Colors: array [0..1] of TColor32;
- Scale: TFloat;
- XOffset: array [0..1] of TFloat;
- XPos: array [0..2] of Integer;
- BlendMemEx: TBlendMemEx;
- begin
- BlendMemEx := BLEND_MEM_EX[CombineMode]^;
- // set first offset/position
- XOffset[0] := ColorStopToScanLine(0, DstY);
- XPos[0] := Round(XOffset[0]);
- XPos[2] := DstX + Length;
- // check if only a solid start color should be drawn.
- if XPos[0] >= XPos[2] - 1 then
- begin
- FillLineSolid(Dst, DstX, DstY, Length, AlphaValues, CombineMode);
- Exit;
- end;
- // set start color
- Colors[0] := FGradient.FGradientColors[0].Color32;
- // eventually draw solid start color
- FillLineAlpha(Dst, AlphaValues, XPos[0] - DstX, Colors[0], CombineMode);
- Index := 1;
- repeat
- // set start position to be at least DstX
- if XPos[0] < DstX then
- XPos[0] := DstX;
- // set destination color and offset
- Colors[1] := FGradient.FGradientColors[Index].Color32;
- XOffset[1] := ColorStopToScanLine(Index, DstY);
- // calculate destination pixel position
- XPos[1] := Round(XOffset[1]);
- if XPos[1] > XPos[2] then
- XPos[1] := XPos[2];
- // check whether
- if XPos[1] > XPos[0] then
- begin
- Scale := 1 / (XOffset[1] - XOffset[0]);
- IntScale := Round($7FFFFFFF * Scale);
- IntValue := Round($7FFFFFFF * (XPos[0] - XOffset[0]) * Scale);
- for X := XPos[0] to XPos[1] - 1 do
- begin
- BlendMemEx(CombineReg(Colors[1], Colors[0], IntValue shr 23),
- Dst^, AlphaValues^);
- IntValue := IntValue + IntScale;
- Inc(Dst);
- Inc(AlphaValues);
- end;
- end;
- // check whether further drawing is still necessary
- if XPos[1] = XPos[2] then
- Exit;
- Inc(Index);
- XPos[0] := XPos[1];
- XOffset[0] := XOffset[1];
- Colors[0] := Colors[1];
- until (Index = FGradient.GradientCount);
- if XPos[0] < DstX then
- XPos[0] := DstX;
- FillLineAlpha(Dst, AlphaValues, XPos[2] - XPos[0], Colors[0], CombineMode);
- end;
- procedure TLinearGradientPolygonFiller.FillLineNegative(Dst: PColor32; DstX,
- DstY, Length: Integer; AlphaValues: PColor32; CombineMode: TCombineMode);
- var
- X, Index: Integer;
- IntScale, IntValue: Integer;
- Colors: array [0..1] of TColor32;
- Scale: TFloat;
- XOffset: array [0..1] of TFloat;
- XPos: array [0..2] of Integer;
- BlendMemEx: TBlendMemEx;
- begin
- BlendMemEx := BLEND_MEM_EX[CombineMode]^;
- Index := FGradient.GradientCount - 1;
- // set first offset/position
- XOffset[0] := ColorStopToScanLine(Index, DstY);
- XPos[0] := Round(XOffset[0]);
- XPos[2] := DstX + Length;
- // set start color
- Colors[0] := FGradient.FGradientColors[Index].Color32;
- // check if only a solid start color should be drawn.
- if XPos[0] >= XPos[2] - 1 then
- begin
- FillLineAlpha(Dst, AlphaValues, Length, Colors[0], CombineMode);
- Exit;
- end;
- // eventually draw solid start color
- FillLineAlpha(Dst, AlphaValues, XPos[0] - DstX, Colors[0], CombineMode);
- Dec(Index);
- repeat
- // set start position to be at least DstX
- if XPos[0] < DstX then
- XPos[0] := DstX;
- // set destination color and offset
- Colors[1] := FGradient.FGradientColors[Index].Color32;
- XOffset[1] := ColorStopToScanLine(Index, DstY);
- // calculate destination pixel position
- XPos[1] := Round(XOffset[1]);
- if XPos[1] > XPos[2] then
- XPos[1] := XPos[2];
- // check whether next color needs to be drawn
- if XPos[1] > XPos[0] then
- begin
- Scale := 1 / (XOffset[1] - XOffset[0]);
- IntScale := Round($7FFFFFFF * Scale);
- IntValue := Round($7FFFFFFF * (XPos[0] - XOffset[0]) * Scale);
- for X := XPos[0] to XPos[1] - 1 do
- begin
- BlendMemEx(CombineReg(Colors[1], Colors[0], IntValue shr 23),
- Dst^, AlphaValues^);
- IntValue := IntValue + IntScale;
- Inc(Dst);
- Inc(AlphaValues);
- end;
- end;
- // check whether further drawing is still necessary
- if XPos[1] = XPos[2] then
- Exit;
- Dec(Index);
- XPos[0] := XPos[1];
- XOffset[0] := XOffset[1];
- Colors[0] := Colors[1];
- until (Index < 0);
- if XPos[0] < DstX then
- XPos[0] := DstX;
- FillLineAlpha(Dst, AlphaValues, XPos[2] - XPos[0], Colors[0], CombineMode);
- end;
- procedure TLinearGradientPolygonFiller.FillLineVerticalPad(
- Dst: PColor32; DstX, DstY, Length: Integer; AlphaValues: PColor32;
- CombineMode: TCombineMode);
- var
- X: Integer;
- Color32: TColor32;
- BlendMemEx: TBlendMemEx;
- begin
- BlendMemEx := BLEND_MEM_EX[CombineMode]^;
- Color32 := FGradientLUT.Color32Ptr^[FWrapProc(Round(FGradientLUT.Mask *
- (DstY - FStartPoint.Y) * FIncline), FGradientLUT.Mask)];
- for X := DstX to DstX + Length - 1 do
- begin
- BlendMemEx(Color32, Dst^, AlphaValues^);
- Inc(Dst);
- Inc(AlphaValues);
- end;
- end;
- procedure TLinearGradientPolygonFiller.FillLineVerticalPadExtreme(
- Dst: PColor32; DstX, DstY, Length: Integer; AlphaValues: PColor32;
- CombineMode: TCombineMode);
- var
- X: Integer;
- Color32: TColor32;
- BlendMemEx: TBlendMemEx;
- begin
- BlendMemEx := BLEND_MEM_EX[CombineMode]^;
- if DstY < FStartPoint.Y then
- Color32 := FGradientLUT.Color32Ptr^[0]
- else
- Color32 := FGradientLUT.Color32Ptr^[FGradientLUT.Mask];
- for X := DstX to DstX + Length - 1 do
- begin
- BlendMemEx(Color32, Dst^, AlphaValues^);
- Inc(Dst);
- Inc(AlphaValues);
- end;
- end;
- procedure TLinearGradientPolygonFiller.FillLineVerticalWrap(
- Dst: PColor32; DstX, DstY, Length: Integer; AlphaValues: PColor32;
- CombineMode: TCombineMode);
- var
- X: Integer;
- Color32: TColor32;
- BlendMemEx: TBlendMemEx;
- begin
- BlendMemEx := BLEND_MEM_EX[CombineMode]^;
- X := Round(FGradientLUT.Mask * (DstY - FStartPoint.Y) * FIncline);
- Color32 := FGradientLUT.Color32Ptr^[FWrapProc(X, Integer(FGradientLUT.Mask))];
- for X := DstX to DstX + Length - 1 do
- begin
- BlendMemEx(Color32, Dst^, AlphaValues^);
- Inc(Dst);
- Inc(AlphaValues);
- end;
- end;
- procedure TLinearGradientPolygonFiller.FillLineHorizontalPadPos(
- Dst: PColor32; DstX, DstY, Length: Integer; AlphaValues: PColor32;
- CombineMode: TCombineMode);
- var
- X, XPos, Count, Mask: Integer;
- ColorLUT: PColor32Array;
- Scale: TFloat;
- XOffset: array [0..1] of TFloat;
- BlendMemEx: TBlendMemEx;
- begin
- BlendMemEx := BLEND_MEM_EX[CombineMode]^;
- XOffset[0] := FStartPoint.X + (FStartPoint.Y - DstY) * FIncline;
- XOffset[1] := FEndPoint.X + (FEndPoint.Y - DstY) * FIncline;
- XPos := Round(XOffset[0]);
- Count := Round(XOffset[1]) - XPos;
- ColorLUT := FGradientLUT.Color32Ptr;
- // check if only a solid start color should be drawn.
- if XPos >= DstX + Length then
- begin
- FillLineAlpha(Dst, AlphaValues, Length, ColorLUT^[0], CombineMode);
- Exit;
- end;
- Mask := FGradientLUT.Mask;
- // check if only a solid end color should be drawn.
- if XPos + Count < DstX then
- begin
- FillLineAlpha(Dst, AlphaValues, Length, ColorLUT^[Mask], CombineMode);
- Exit;
- end;
- Scale := Mask / (XOffset[1] - XOffset[0]);
- for X := DstX to DstX + Length - 1 do
- begin
- BlendMemEx(ColorLUT^[FWrapProc(Round((X - XOffset[0]) * Scale), Mask)], Dst^, AlphaValues^);
- Inc(Dst);
- Inc(AlphaValues);
- end;
- end;
- procedure TLinearGradientPolygonFiller.FillLineHorizontalPadNeg(
- Dst: PColor32; DstX, DstY, Length: Integer; AlphaValues: PColor32;
- CombineMode: TCombineMode);
- var
- X, XPos, Count, Mask: Integer;
- ColorLUT: PColor32Array;
- Scale: TFloat;
- XOffset: array [0..1] of TFloat;
- BlendMemEx: TBlendMemEx;
- begin
- BlendMemEx := BLEND_MEM_EX[CombineMode]^;
- XOffset[0] := FEndPoint.X + (FEndPoint.Y - DstY) * FIncline;
- XOffset[1] := FStartPoint.X + (FStartPoint.Y - DstY) * FIncline;
- XPos := Round(XOffset[0]);
- Count := Round(XOffset[1]) - XPos;
- Mask := FGradientLUT.Mask;
- ColorLUT := FGradientLUT.Color32Ptr;
- // check if only a solid start color should be drawn.
- if XPos >= DstX + Length then
- begin
- FillLineAlpha(Dst, AlphaValues, Length, ColorLUT^[Mask], CombineMode);
- Exit;
- end;
- // check if only a solid end color should be drawn.
- if XPos + Count < DstX then
- begin
- FillLineAlpha(Dst, AlphaValues, Length, ColorLUT^[0], CombineMode);
- Exit;
- end;
- Scale := Mask / (XOffset[1] - XOffset[0]);
- for X := DstX to DstX + Length - 1 do
- begin
- BlendMemEx(ColorLUT^[FWrapProc(Round((XOffset[1] - X) * Scale), Mask)], Dst^, AlphaValues^);
- Inc(Dst);
- Inc(AlphaValues);
- end;
- end;
- procedure TLinearGradientPolygonFiller.FillLineHorizontalWrapPos(
- Dst: PColor32; DstX, DstY, Length: Integer; AlphaValues: PColor32;
- CombineMode: TCombineMode);
- var
- X, Index, Mask: Integer;
- ColorLUT: PColor32Array;
- Scale: TFloat;
- XOffset: array [0..1] of TFloat;
- BlendMemEx: TBlendMemEx;
- begin
- BlendMemEx := BLEND_MEM_EX[CombineMode]^;
- XOffset[0] := FStartPoint.X + (FStartPoint.Y - DstY) * FIncline;
- XOffset[1] := FEndPoint.X + (FEndPoint.Y - DstY) * FIncline;
- Mask := Integer(FGradientLUT.Mask);
- ColorLUT := FGradientLUT.Color32Ptr;
- Scale := Mask / (XOffset[1] - XOffset[0]);
- for X := DstX to DstX + Length - 1 do
- begin
- Index := Round((X - XOffset[0]) * Scale);
- BlendMemEx(ColorLUT^[FWrapProc(Index, Mask)], Dst^, AlphaValues^);
- Inc(Dst);
- Inc(AlphaValues);
- end;
- end;
- procedure TLinearGradientPolygonFiller.FillLineHorizontalWrapNeg(
- Dst: PColor32; DstX, DstY, Length: Integer; AlphaValues: PColor32;
- CombineMode: TCombineMode);
- var
- X, Index, Mask: Integer;
- ColorLUT: PColor32Array;
- Scale: TFloat;
- XOffset: array [0..1] of TFloat;
- BlendMemEx: TBlendMemEx;
- begin
- BlendMemEx := BLEND_MEM_EX[CombineMode]^;
- XOffset[0] := FEndPoint.X + (FEndPoint.Y - DstY) * FIncline;
- XOffset[1] := FStartPoint.X + (FStartPoint.Y - DstY) * FIncline;
- Mask := Integer(FGradientLUT.Mask);
- ColorLUT := FGradientLUT.Color32Ptr;
- Scale := Mask / (XOffset[1] - XOffset[0]);
- for X := DstX to DstX + Length - 1 do
- begin
- Index := Round((XOffset[1] - X) * Scale);
- BlendMemEx(ColorLUT^[FWrapProc(Index, Mask)], Dst^, AlphaValues^);
- Inc(Dst);
- Inc(AlphaValues);
- end;
- end;
- procedure TLinearGradientPolygonFiller.BeginRendering;
- begin
- if LookUpTableNeedsUpdate then
- begin
- if FUseLookUpTable then
- begin
- if not Assigned(FGradientLUT) then
- raise Exception.Create(RCStrNoTColor32LookupTable);
- if Assigned(FGradient) then
- FGradient.FillColorLookUpTable(FGradientLUT);
- end
- else
- if not Assigned(FGradient) then
- raise Exception.Create(RCStrNoTColor32Gradient);
- inherited;
- end;
- end;
- { TCustomRadialGradientPolygonFiller }
- procedure TCustomRadialGradientPolygonFiller.SetEllipseBounds(
- const Value: TFloatRect);
- begin
- if (FEllipseBounds.Left <> Value.Left) or (FEllipseBounds.Top <> Value.Top) or
- (FEllipseBounds.Right <> Value.Right) or
- (FEllipseBounds.Bottom <> Value.Bottom) then
- begin
- FEllipseBounds := Value;
- EllipseBoundsChanged;
- end;
- end;
- { TRadialGradientPolygonFiller }
- constructor TRadialGradientPolygonFiller.Create(Radius: TFloatPoint);
- begin
- inherited Create;
- FRadius := Radius;
- UpdateEllipseBounds;
- UpdateRadiusScale;
- end;
- constructor TRadialGradientPolygonFiller.Create(Radius, Center: TFloatPoint);
- begin
- inherited Create;
- FRadius := Radius;
- FCenter := Center;
- UpdateEllipseBounds;
- UpdateRadiusScale;
- end;
- constructor TRadialGradientPolygonFiller.Create(BoundingBox: TFloatRect);
- begin
- Create(FloatPoint(0.5 * (BoundingBox.Right - BoundingBox.Left),
- 0.5 * (BoundingBox.Bottom - BoundingBox.Top)),
- FloatPoint(0.5 * (BoundingBox.Right + BoundingBox.Left),
- 0.5 * (BoundingBox.Bottom + BoundingBox.Top)));
- end;
- procedure TRadialGradientPolygonFiller.EllipseBoundsChanged;
- begin
- with FEllipseBounds do
- begin
- FCenter := FloatPoint((Left + Right) * 0.5, (Top + Bottom) * 0.5);
- FRadius.X := System.Round((Right - Left) * 0.5);
- FRadius.Y := System.Round((Bottom - Top) * 0.5);
- end;
- UpdateRadiusScale;
- end;
- procedure TRadialGradientPolygonFiller.SetCenter(const Value: TFloatPoint);
- begin
- if (FCenter.X <> Value.X) or (FCenter.Y <> Value.Y) then
- begin
- FCenter := Value;
- UpdateEllipseBounds;
- end;
- end;
- procedure TRadialGradientPolygonFiller.SetRadius(const Value: TFloatPoint);
- begin
- if (FRadius.X <> Value.X) or (FRadius.Y <> Value.Y) then
- begin
- FRadius := Value;
- UpdateRadiusScale;
- UpdateEllipseBounds;
- end;
- end;
- procedure TRadialGradientPolygonFiller.UpdateEllipseBounds;
- begin
- with FEllipseBounds do
- begin
- Left := FCenter.X - FRadius.X;
- Top := FCenter.X + FRadius.X;
- Right := FCenter.Y - FRadius.Y;
- Bottom := FCenter.Y + FRadius.Y;
- end;
- end;
- procedure TRadialGradientPolygonFiller.UpdateRadiusScale;
- begin
- FRadScale := FRadius.X / FRadius.Y;
- FRadXInv := 1 / FRadius.X;
- end;
- procedure TRadialGradientPolygonFiller.BeginRendering;
- begin
- if LookUpTableNeedsUpdate then
- begin
- if FUseLookUpTable then
- begin
- if not Assigned(FGradientLUT) then
- raise Exception.Create(RCStrNoTColor32LookupTable);
- if Assigned(FGradient) then
- FGradient.FillColorLookUpTable(FGradientLUT);
- end
- else
- if not Assigned(FGradient) then
- raise Exception.Create(RCStrNoTColor32Gradient);
- inherited;
- end;
- end;
- function TRadialGradientPolygonFiller.GetFillLine: TFillLineEvent;
- begin
- case FWrapMode of
- wmClamp:
- Result := FillLineClamp;
- wmMirror:
- Result := FillLineMirror;
- wmRepeat:
- Result := FillLineRepeat;
- {$ifdef GR32_WRAPMODE_REFLECT}
- wmReflect:
- Result := FillLineReflect;
- {$endif}
- end;
- end;
- procedure TRadialGradientPolygonFiller.FillLineClamp(Dst: PColor32; DstX,
- DstY, Length: Integer; AlphaValues: PColor32; CombineMode: TCombineMode);
- var
- X, Index, Count, Mask: Integer;
- SqrRelRad, RadMax: TFloat;
- ColorLUT: PColor32Array;
- YDist, SqrInvRadius: TFloat;
- Color32: TColor32;
- BlendMemEx: TBlendMemEx;
- begin
- BlendMemEx := BLEND_MEM_EX[CombineMode]^;
- Mask := Integer(FGradientLUT.Mask);
- ColorLUT := FGradientLUT.Color32Ptr;
- // small optimization
- Index := Ceil(FCenter.X - FRadius.X);
- if Index > DstX then
- begin
- Count := Min((Index - DstX), Length);
- FillLineAlpha(Dst, AlphaValues, Count, ColorLUT^[Mask], CombineMode);
- Length := Length - Count;
- if Length = 0 then
- Exit;
- DstX := Index;
- end;
- // further optimization
- if Abs(DstY - FCenter.Y) > FRadius.Y then
- begin
- FillLineAlpha(Dst, AlphaValues, Length, ColorLUT^[Mask], CombineMode);
- Exit;
- end;
- SqrInvRadius := Sqr(FRadXInv);
- YDist := Sqr((DstY - FCenter.Y) * FRadScale);
- RadMax := (Sqr(FRadius.X) + YDist) * SqrInvRadius;
- for X := DstX to DstX + Length - 1 do
- begin
- SqrRelRad := (Sqr(X - FCenter.X) + YDist) * SqrInvRadius;
- // Clamp
- if SqrRelRad > RadMax then
- Index := Mask
- else
- Index := Min(Round(Mask * FastSqrt(SqrRelRad)), Mask);
- Color32 := ColorLUT^[Index];
- BlendMemEx(Color32, Dst^, AlphaValues^);
- Inc(Dst);
- Inc(AlphaValues);
- end;
- end;
- procedure TRadialGradientPolygonFiller.FillLineReflect(Dst: PColor32;
- DstX, DstY, Length: Integer; AlphaValues: PColor32;
- CombineMode: TCombineMode);
- var
- X, Index, Mask: Integer;
- SqrInvRadius: TFloat;
- YDist: TFloat;
- ColorLUT: PColor32Array;
- ReflectProc: TWrapProc;
- Color32: TColor32;
- BlendMemEx: TBlendMemEx;
- begin
- BlendMemEx := BLEND_MEM_EX[CombineMode]^;
- SqrInvRadius := Sqr(FRadXInv);
- YDist := Sqr((DstY - FCenter.Y) * FRadScale);
- Mask := Integer(FGradientLUT.Mask);
- ColorLUT := FGradientLUT.Color32Ptr;
- ReflectProc := GetOptimalReflect(FGradientLUT.Size-1);
- for X := DstX to DstX + Length - 1 do
- begin
- Index := Round(Mask * FastSqrt((Sqr(X - FCenter.X) + YDist) * SqrInvRadius));
- // Reflect
- Index := ReflectProc(Index, FGradientLUT.Size-1);
- (*
- DivResult := DivMod(Index, FGradientLUT.Size, Index);
- if Odd(DivResult) then
- Index := Mask - Index;
- *)
- Color32 := ColorLUT^[Index];
- BlendMemEx(Color32, Dst^, AlphaValues^);
- Inc(Dst);
- Inc(AlphaValues);
- end;
- end;
- procedure TRadialGradientPolygonFiller.FillLineMirror(Dst: PColor32;
- DstX, DstY, Length: Integer; AlphaValues: PColor32;
- CombineMode: TCombineMode);
- var
- X, Index, Mask: Integer;
- SqrInvRadius: TFloat;
- YDist: TFloat;
- ColorLUT: PColor32Array;
- Color32: TColor32;
- BlendMemEx: TBlendMemEx;
- begin
- BlendMemEx := BLEND_MEM_EX[CombineMode]^;
- SqrInvRadius := Sqr(FRadXInv);
- YDist := Sqr((DstY - FCenter.Y) * FRadScale);
- Mask := Integer(FGradientLUT.Mask);
- ColorLUT := FGradientLUT.Color32Ptr;
- for X := DstX to DstX + Length - 1 do
- begin
- Index := Round(Mask * FastSqrt((Sqr(X - FCenter.X) + YDist) * SqrInvRadius));
- // Mirror
- Index := Mirror(Index, FGradientLUT.Size-1);
- Color32 := ColorLUT^[Index];
- BlendMemEx(Color32, Dst^, AlphaValues^);
- Inc(Dst);
- Inc(AlphaValues);
- end;
- end;
- procedure TRadialGradientPolygonFiller.FillLineRepeat(Dst: PColor32;
- DstX, DstY, Length: Integer; AlphaValues: PColor32; CombineMode: TCombineMode);
- var
- X, Mask: Integer;
- Index: Integer;
- YDist, SqrInvRadius: TFloat;
- ColorLUT: PColor32Array;
- WrapProc: TWrapProc;
- Color32: TColor32;
- BlendMemEx: TBlendMemEx;
- begin
- BlendMemEx := BLEND_MEM_EX[CombineMode]^;
- SqrInvRadius := Sqr(FRadXInv);
- YDist := Sqr((DstY - FCenter.Y) * FRadScale);
- Mask := Integer(FGradientLUT.Mask);
- ColorLUT := FGradientLUT.Color32Ptr;
- WrapProc := GetOptimalWrap(FGradientLUT.Size-1);
- for X := DstX to DstX + Length - 1 do
- begin
- // Wrap
- Index := WrapProc(Round(Mask * FastSqrt((Sqr(X - FCenter.X) + YDist) * SqrInvRadius)), FGradientLUT.Size-1);
- // Index := Round(Mask * FastSqrt((Sqr(X - FCenter.X) + YDist) * SqrInvRadius)) mod FGradientLUT.Size;
- Color32 := ColorLUT^[Index];
- BlendMemEx(Color32, Dst^, AlphaValues^);
- Inc(Dst);
- Inc(AlphaValues);
- end;
- end;
- { TSVGRadialGradientPolygonFiller }
- constructor TSVGRadialGradientPolygonFiller.Create(EllipseBounds: TFloatRect);
- begin
- inherited Create;
- SetParameters(EllipseBounds);
- end;
- constructor TSVGRadialGradientPolygonFiller.Create(EllipseBounds: TFloatRect;
- FocalPoint: TFloatPoint);
- begin
- inherited Create;
- SetParameters(EllipseBounds, FocalPoint);
- end;
- procedure TSVGRadialGradientPolygonFiller.EllipseBoundsChanged;
- begin
- GradientFillerChanged;
- end;
- procedure TSVGRadialGradientPolygonFiller.SetFocalPoint(const Value: TFloatPoint);
- begin
- if (FFocalPointNative.X <> Value.X) and (FFocalPointNative.Y <> Value.Y) then
- begin
- FFocalPointNative := Value;
- GradientFillerChanged;
- end;
- end;
- procedure TSVGRadialGradientPolygonFiller.SetParameters(
- EllipseBounds: TFloatRect);
- begin
- FEllipseBounds := EllipseBounds;
- FFocalPointNative := FloatPoint(
- 0.5 * (FEllipseBounds.Left + FEllipseBounds.Right),
- 0.5 * (FEllipseBounds.Top + FEllipseBounds.Bottom));
- GradientFillerChanged;
- end;
- procedure TSVGRadialGradientPolygonFiller.SetParameters(
- EllipseBounds: TFloatRect; FocalPoint: TFloatPoint);
- begin
- FEllipseBounds := EllipseBounds;
- FFocalPointNative := FocalPoint;
- GradientFillerChanged;
- end;
- procedure TSVGRadialGradientPolygonFiller.InitMembers;
- var
- X, Y: TFloat;
- Temp: TFloat;
- begin
- FRadius.X := (FEllipseBounds.Right - FEllipseBounds.Left) * 0.5;
- FRadius.Y := (FEllipseBounds.Bottom - FEllipseBounds.Top) * 0.5;
- FCenter.X := (FEllipseBounds.Right + FEllipseBounds.Left) * 0.5;
- FCenter.Y := (FEllipseBounds.Bottom + FEllipseBounds.Top) * 0.5;
- FOffset.X := FEllipseBounds.Left;
- FOffset.Y := FEllipseBounds.Top;
- // make FFocalPoint relative to the ellipse midpoint ...
- FFocalPt.X := FFocalPointNative.X - FCenter.X;
- FFocalPt.Y := FFocalPointNative.Y - FCenter.Y;
- // make sure the focal point stays within the bounding ellipse ...
- if Abs(FFocalPt.X) < CFloatTolerance then
- begin
- X := 0;
- if FFocalPt.Y < 0 then
- Y := -1
- else
- Y := 1;
- end
- else
- begin
- Temp := FRadius.X * FFocalPt.Y / (FRadius.Y * FFocalPt.X);
- X := 1 / FastSqrtBab1(1 + Sqr(Temp));
- Y := Temp * X;
- end;
- if FFocalPt.X < 0 then
- begin
- X := -X;
- Y := -Y;
- end;
- X := X * FRadius.X;
- Y := Y * FRadius.Y;
- if (Y * Y + X * X) < (Sqr(FFocalPt.X) + Sqr(FFocalPt.Y)) then
- begin
- FFocalPt.X := 0.999 * X;
- FFocalPt.Y := 0.999 * Y;
- end;
- // Because the slope of vertical lines is infinite, we need to find where a
- // vertical line through the FocalPoint intersects with the Ellipse, and
- // store the distances from the focal point to these 2 intersections points
- FVertDist := FRadius.Y * FastSqrtBab1(1.0 - Sqr(FFocalPt.X) / Sqr(FRadius.X));
- end;
- procedure TSVGRadialGradientPolygonFiller.BeginRendering;
- begin
- if LookUpTableNeedsUpdate then
- begin
- if FUseLookUpTable then
- begin
- if not Assigned(FGradientLUT) then
- raise Exception.Create(RCStrNoTColor32LookupTable);
- if Assigned(FGradient) then
- FGradient.FillColorLookUpTable(FGradientLUT);
- end
- else
- if not Assigned(FGradient) then
- raise Exception.Create(RCStrNoTColor32Gradient);
- inherited;
- end;
- InitMembers;
- end;
- function TSVGRadialGradientPolygonFiller.GetFillLine: TFillLineEvent;
- begin
- Result := FillLineEllipse;
- end;
- procedure TSVGRadialGradientPolygonFiller.FillLineEllipse(Dst: PColor32;
- DstX, DstY, Length: Integer; AlphaValues: PColor32;
- CombineMode: TCombineMode);
- var
- X, Mask: Integer;
- ColorLUT: PColor32Array;
- Rad, Rad2, X2, Y2: TFloat;
- m, b, Qa, Qb, Qc, Qz, XSqr: Double;
- RelPos: TFloatPoint;
- Color32: TColor32;
- BlendMemEx: TBlendMemEx;
- begin
- BlendMemEx := BLEND_MEM_EX[CombineMode]^;
- if (FRadius.X = 0) or (FRadius.Y = 0) then
- Exit;
- ColorLUT := FGradientLUT.Color32Ptr;
- RelPos.Y := DstY - FCenter.Y - FFocalPt.Y;
- Mask := Integer(FGradientLUT.Mask);
- // check if out of bounds (vertically)
- if (DstY < FOffset.Y) or (DstY >= (FRadius.Y * 2) + 1 + FOffset.Y) then
- begin
- FillLineAlpha(Dst, AlphaValues, Length, ColorLUT^[Mask], CombineMode);
- Exit;
- end;
- for X := DstX to DstX + Length - 1 do
- begin
- // check if out of bounds (horizontally)
- if (X < FOffset.X) or (X >= (FRadius.X * 2) + 1 + FOffset.X) then
- Color32 := ColorLUT^[Mask]
- else
- begin
- RelPos.X := X - FCenter.X - FFocalPt.X;
- if Abs(RelPos.X) < CFloatTolerance then //ie on the vertical line (see above)
- begin
- Assert(Abs(X - FCenter.X) <= FRadius.X);
- Rad := Abs(RelPos.Y);
- if Abs(Abs(X - FCenter.X)) <= FRadius.X then
- begin
- if RelPos.Y < 0 then
- Rad2 := Abs(-FVertDist - FFocalPt.Y)
- else
- Rad2 := Abs( FVertDist - FFocalPt.Y);
- if Rad >= Rad2 then
- Color32 := ColorLUT^[Mask]
- else
- Color32 := ColorLUT^[Round(Mask * Rad / Rad2)];
- end else
- Color32 := ColorLUT^[Mask];
- end
- else
- begin
- m := RelPos.Y / RelPos.X;
- b := FFocalPt.Y - m * FFocalPt.X;
- XSqr := Sqr(FRadius.X);
- // apply quadratic equation ...
- Qa := 2 * (Sqr(FRadius.Y) + XSqr * m * m);
- Qb := XSqr * 2 * m * b;
- Qc := XSqr * (b * b - Sqr(FRadius.Y));
- Qz := Qb * Qb - 2 * Qa * Qc;
- if Qz >= 0 then
- begin
- Qz := FastSqrtBab2(Qz);
- Qa := 1 / Qa;
- X2 := (-Qb + Qz) * Qa;
- if (FFocalPt.X > X2) = (RelPos.X > 0) then
- X2 := -(Qb + Qz) * Qa;
- Y2 := m * X2 + b;
- Rad := Sqr(RelPos.X) + Sqr(RelPos.Y);
- Rad2 := Sqr(X2 - FFocalPt.X) + Sqr(Y2 - FFocalPt.Y);
- if Rad >= Rad2 then
- Color32 := ColorLUT^[Mask]
- else
- Color32 := ColorLUT^[Round(Mask * FastSqrtBab1(Rad / Rad2))];
- end else
- Color32 := ColorLUT^[Mask]
- end;
- end;
- BlendMemEx(Color32, Dst^, AlphaValues^);
- Inc(Dst);
- Inc(AlphaValues);
- end;
- end;
- procedure RegisterBindings;
- begin
- GradientRegistry := NewRegistry('GR32_ColorGradients bindings');
- GradientRegistry.RegisterBinding(FID_LINEAR3, @@Linear3PointInterpolation, 'Linear3PointInterpolation');
- GradientRegistry.RegisterBinding(FID_LINEAR4, @@Linear4PointInterpolation, 'Linear4PointInterpolation');
- // Pure pascal
- GradientRegistry[@@Linear3PointInterpolation].Add(@Linear3PointInterpolation_Pas, [isPascal]).name := 'Linear3PointInterpolation_Pas';
- GradientRegistry[@@Linear4PointInterpolation].Add(@Linear4PointInterpolation_Pas, [isPascal]).Name := 'Linear4PointInterpolation_Pas';
- {$IFNDEF PUREPASCAL}
- {$IFNDEF OMIT_SSE2}
- GradientRegistry[@@Linear3PointInterpolation].Add(@Linear3PointInterpolation_SSE2, [isSSE2]).Name := 'Linear3PointInterpolation_SSE2';
- GradientRegistry[@@Linear4PointInterpolation].Add(@Linear4PointInterpolation_SSE2, [isSSE2]).Name := 'Linear4PointInterpolation_SSE2';
- {$ENDIF}
- {$ENDIF}
- GradientRegistry.RebindAll;
- end;
- initialization
- RegisterBindings;
- end.
|