gifimage.pas 359 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693
  1. unit GIFImage;
  2. ////////////////////////////////////////////////////////////////////////////////
  3. // //
  4. // Project: GIF Graphics Object //
  5. // Module: gifimage //
  6. // Description: TGraphic implementation of the GIF89a graphics format //
  7. // Version: 2.2 //
  8. // Release: 5 //
  9. // Date: 23-MAY-1999 //
  10. // Target: Win32, Delphi 2, 3, 4 & 5, C++ Builder 3 & 4 //
  11. // Author(s): anme: Anders Melander, anders@melander.dk //
  12. // fila: Filip Larsen //
  13. // rps: Reinier Sterkenburg //
  14. // Copyright: (c) 1997-99 Anders Melander. //
  15. // All rights reserved. //
  16. // Formatting: 2 space indent, 8 space tabs, 80 columns. //
  17. // //
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // Changed 2001.07.23 by Finn Tolderlund: //
  20. // Changed according to e-mail from "Rolf Frei" <rolf@eicom.ch> //
  21. // on 2001.07.23 so that it works in Delphi 6. //
  22. // //
  23. // Changed 2002.07.07 by Finn Tolderlund: //
  24. // Incorporated additional modifications by Alexey Barkovoy (clootie@reactor.ru)
  25. // found in his Delphi 6 GifImage.pas (from 22-Dec-2001). //
  26. // Alexey Barkovoy's Delphi 6 gifimage.pas can be downloaded from //
  27. // http://clootie.narod.ru/delphi/download_vcl.html //
  28. // These changes made showing of animated gif files more stable. The code //
  29. // from 2001.07.23 could crash sometimes with an Execption EAccessViolation. //
  30. // //
  31. // Changed 2002.10.06 by Finn Tolderlund: //
  32. // Delphi 7 compatible. //
  33. // //
  34. // Changed 2003-03-06 by Finn Tolderlund: //
  35. // Changes made as a result of postings in borland.public.delphi.graphics //
  36. // from 2003-02-28 to 2003-03-05 where white (255,255,255) in a bitmap //
  37. // was converted to (254,254,254) in the gif. //
  38. // The doCreateOptimizedPaletteFromSingleBitmap function and //
  39. // the CreateOptimizedPaletteFromManyBitmaps function is changed so that //
  40. // the correct offset 246 is used instead of 245. //
  41. // The ReduceColors function is changed according to Anders Melander's post //
  42. // so that a colour get converted to the precise colour if that colour is //
  43. // present in the palette when using ColorReduction rmQuantize. //
  44. // //
  45. // Changed 2003-03-09 by Finn Tolderlund: //
  46. // Delphi 7 version is now assumed if unknown compiler version is unknown //
  47. // for better compatibility with future Delphi versions. //
  48. // Hopefully this code is now compatible with future Delphi versions, //
  49. // unless Borland makes some changes that breaks existing code. //
  50. // //
  51. // Changed 2003-08-04 by Finn Tolderlund: //
  52. // Changed procedure AddMaskOnly so that it doesn't leak a GDI HBitmap-object //
  53. // and it doesn't release the handle of the source bitmap which //
  54. // is used to assign to the GIF object as in gif.assign(bm); //
  55. // These changes were made as a result of a news post made by Renate Schaaf //
  56. // with the subject "TGifImage HBitmap leak on assign?" //
  57. // in borland.public.delphi.graphics on Mon 28 Jul 2003 and Sun 03 Aug 2003. //
  58. // //
  59. // Changed 2004.03.09 by Finn Tolderlund: //
  60. // Added a ForceFrame property to the TGIFImage class. //
  61. // The ForceFrame property can be used to make TGIFImage display a apecific //
  62. // sub frame from an animated gif. //
  63. // How to use: Set the Animate property to False and set the ForceFrame //
  64. // property to a desired frame number (0-N) //
  65. // Normal display: Set the ForceFrame property to -1 and set Animate to True. //
  66. // If ForceFrame is negative TGIFImage behaves just as before this change. //
  67. // Note that if the sub frame in the gif only contains part of the image //
  68. // (i.e. only the changes from previous frames) the result is unpredictable. //
  69. // The result is best if each sub frame contains a whole image. //
  70. // If the sub frame is transparent the background is not automatically //
  71. // restored, you must do so yourself if you want that. //
  72. // If you are using a TImage to display the gif you can use //
  73. // Image.Parent.Invalidate or Image.Parent.Refresh to restore the background. //
  74. // This change was made as a result of a email correspondance with //
  75. // Tineke Kosmis (http://www.classe.nl/) which requested such a property. //
  76. // //
  77. ////////////////////////////////////////////////////////////////////////////////
  78. // //
  79. // Please read the "Conditions of use" in the release notes. //
  80. // //
  81. ////////////////////////////////////////////////////////////////////////////////
  82. // Known problems:
  83. //
  84. // * The combination of buffered, tiled and transparent draw will display the
  85. // background incorrectly (scaled).
  86. // If this is a problem for you, use non-buffered (goDirectDraw) drawing
  87. // instead.
  88. //
  89. // * The combination of non-buffered, transparent and stretched draw is
  90. // sometimes distorted with a pattern effect when the image is displayed
  91. // smaller than the real size (shrinked).
  92. //
  93. // * Buffered display flickers when TGIFImage is used by a transparent TImage
  94. // component.
  95. // This is a problem with TImage caused by the fact that TImage was designed
  96. // with static images in mind. Not much I can do about it.
  97. //
  98. ////////////////////////////////////////////////////////////////////////////////
  99. // To do (in rough order of priority):
  100. // { TODO -oanme -cFeature : TImage hook for destroy notification. }
  101. // { TODO -oanme -cFeature : TBitmap pool to limit resource consumption on Win95/98. }
  102. // { TODO -oanme -cImprovement : Make BitsPerPixel property writable. }
  103. // { TODO -oanme -cFeature : Visual GIF component. }
  104. // { TODO -oanme -cImprovement : Easier method to determine DrawPainter status. }
  105. // { TODO -oanme -cFeature : Import to 256+ color GIF. }
  106. // { TODO -oanme -cFeature : Make some of TGIFImage's properties persistent (DrawOptions etc). }
  107. // { TODO -oanme -cFeature : Add TGIFImage.Persistent property. Should save published properties in application extension when this options is set. }
  108. // { TODO -oanme -cBugFix : Solution for background buffering in scrollbox. }
  109. //
  110. //////////////////////////////////////////////////////////////////////////////////
  111. {$ifdef BCB}
  112. {$ObjExportAll On}
  113. {$endif}
  114. interface
  115. ////////////////////////////////////////////////////////////////////////////////
  116. //
  117. // Conditional Compiler Symbols
  118. //
  119. ////////////////////////////////////////////////////////////////////////////////
  120. (*
  121. DEBUG Must be defined if any of the DEBUG_xxx
  122. symbols are defined.
  123. If the symbol is defined the source will not be
  124. optimized and overflow- and range checks will be
  125. enabled.
  126. DEBUG_HASHPERFORMANCE Calculates hash table performance data.
  127. DEBUG_HASHFILLFACTOR Calculates fill factor of hash table -
  128. Interferes with DEBUG_HASHPERFORMANCE.
  129. DEBUG_COMPRESSPERFORMANCE Calculates LZW compressor performance data.
  130. DEBUG_DECOMPRESSPERFORMANCE Calculates LZW decompressor performance data.
  131. DEBUG_DITHERPERFORMANCE Calculates color reduction performance data.
  132. DEBUG_DRAWPERFORMANCE Calculates low level drawing performance data.
  133. The performance data for DEBUG_DRAWPERFORMANCE
  134. will be displayed when you press the Ctrl key.
  135. DEBUG_RENDERPERFORMANCE Calculates performance data for the GIF to
  136. bitmap converter.
  137. The performance data for DEBUG_DRAWPERFORMANCE
  138. will be displayed when you press the Ctrl key.
  139. GIF_NOSAFETY Define this symbol to disable overflow- and
  140. range checks.
  141. Ignored if the DEBUG symbol is defined.
  142. STRICT_MOZILLA Define to mimic Mozilla as closely as possible.
  143. If not defined, a slightly more "optimal"
  144. implementation is used (IMHO).
  145. FAST_AS_HELL Define this symbol to use strictly GIF compliant
  146. (but too fast) animation timing.
  147. Since our paint routines are much faster and
  148. more precise timed than Mozilla's, the standard
  149. GIF and Mozilla values causes animations to loop
  150. faster than they would in Mozilla.
  151. If the symbol is _not_ defined, an alternative
  152. set of tweaked timing values will be used.
  153. The tweaked values are not optimal but are based
  154. on tests performed on my reference system:
  155. - Windows 95
  156. - 133 MHz Pentium
  157. - 64Mb RAM
  158. - Diamond Stealth64/V3000
  159. - 1600*1200 in 256 colors
  160. The alternate values can be modified if you are
  161. not satisfied with my defaults (they can be
  162. found a few pages down).
  163. REGISTER_TGIFIMAGE Define this symbol to register TGIFImage with
  164. the TPicture class and integrate with TImage.
  165. This is required to be able to display GIFs in
  166. the TImage component.
  167. The symbol is defined by default.
  168. Undefine if you use another GIF library to
  169. provide GIF support for TImage.
  170. PIXELFORMAT_TOO_SLOW When this symbol is defined, the internal
  171. PixelFormat routines are used in some places
  172. instead of TBitmap.PixelFormat.
  173. The current implementation (Delphi4, Builder 3)
  174. of TBitmap.PixelFormat can in some situation
  175. degrade performance.
  176. The symbol is defined by default.
  177. CREATEDIBSECTION_SLOW If this symbol is defined, TDIBWriter will
  178. use global memory as scanline storage, instead
  179. of a DIB section.
  180. Benchmarks have shown that a DIB section is
  181. twice as slow as global memory.
  182. The symbol is defined by default.
  183. The symbol requires that PIXELFORMAT_TOO_SLOW
  184. is defined.
  185. SERIALIZE_RENDER Define this symbol to serialize threaded
  186. GIF to bitmap rendering.
  187. When a GIF is displayed with the goAsync option
  188. (the default), the GIF to bitmap rendering is
  189. executed in the context of the draw thread.
  190. If more than one thread is drawing the same GIF
  191. or the GIF is being modified while it is
  192. animating, the GIF to bitmap rendering should be
  193. serialized to guarantee that the bitmap isn't
  194. modified by more than one thread at a time. If
  195. SERIALIZE_RENDER is defined, the draw threads
  196. uses TThread.Synchronize to serialize GIF to
  197. bitmap rendering.
  198. *)
  199. {$DEFINE REGISTER_TGIFIMAGE}
  200. {$DEFINE PIXELFORMAT_TOO_SLOW}
  201. {$DEFINE CREATEDIBSECTION_SLOW}
  202. ////////////////////////////////////////////////////////////////////////////////
  203. //
  204. // Determine Delphi and C++ Builder version
  205. //
  206. ////////////////////////////////////////////////////////////////////////////////
  207. // Delphi 1.x
  208. {$IFDEF VER80}
  209. 'Error: TGIFImage does not support Delphi 1.x'
  210. {$ENDIF}
  211. // Delphi 2.x
  212. {$IFDEF VER90}
  213. {$DEFINE VER9x}
  214. {$ENDIF}
  215. // C++ Builder 1.x
  216. {$IFDEF VER93}
  217. // Good luck...
  218. {$DEFINE VER9x}
  219. {$ENDIF}
  220. // Delphi 3.x
  221. {$IFDEF VER100}
  222. {$DEFINE VER10_PLUS}
  223. {$DEFINE D3_BCB3}
  224. {$ENDIF}
  225. // C++ Builder 3.x
  226. {$IFDEF VER110}
  227. {$DEFINE VER10_PLUS}
  228. {$DEFINE VER11_PLUS}
  229. {$DEFINE D3_BCB3}
  230. {$DEFINE BAD_STACK_ALIGNMENT}
  231. {$ENDIF}
  232. // Delphi 4.x
  233. {$IFDEF VER120}
  234. {$DEFINE VER10_PLUS}
  235. {$DEFINE VER11_PLUS}
  236. {$DEFINE VER12_PLUS}
  237. {$DEFINE BAD_STACK_ALIGNMENT}
  238. {$ENDIF}
  239. // C++ Builder 4.x
  240. {$IFDEF VER125}
  241. {$DEFINE VER10_PLUS}
  242. {$DEFINE VER11_PLUS}
  243. {$DEFINE VER12_PLUS}
  244. {$DEFINE VER125_PLUS}
  245. {$DEFINE BAD_STACK_ALIGNMENT}
  246. {$ENDIF}
  247. // Delphi 5.x
  248. {$IFDEF VER130}
  249. {$DEFINE VER10_PLUS}
  250. {$DEFINE VER11_PLUS}
  251. {$DEFINE VER12_PLUS}
  252. {$DEFINE VER125_PLUS}
  253. {$DEFINE VER13_PLUS}
  254. {$DEFINE BAD_STACK_ALIGNMENT}
  255. {$ENDIF}
  256. // Delphi 6.x
  257. {$IFDEF VER140}
  258. {$WARN SYMBOL_PLATFORM OFF}
  259. {$DEFINE VER10_PLUS}
  260. {$DEFINE VER11_PLUS}
  261. {$DEFINE VER12_PLUS}
  262. {$DEFINE VER125_PLUS}
  263. {$DEFINE VER13_PLUS}
  264. {$DEFINE VER14_PLUS}
  265. {$DEFINE BAD_STACK_ALIGNMENT}
  266. {$ENDIF}
  267. // Delphi 7.x
  268. {$IFDEF VER150}
  269. {$WARN SYMBOL_PLATFORM OFF}
  270. {$DEFINE VER10_PLUS}
  271. {$DEFINE VER11_PLUS}
  272. {$DEFINE VER12_PLUS}
  273. {$DEFINE VER125_PLUS}
  274. {$DEFINE VER13_PLUS}
  275. {$DEFINE VER14_PLUS}
  276. {$DEFINE VER15_PLUS}
  277. {$DEFINE BAD_STACK_ALIGNMENT}
  278. {$ENDIF}
  279. // 2003.03.09 ->
  280. // Unknown compiler version - assume D4 compatible
  281. //{$IFNDEF VER9x}
  282. // {$IFNDEF VER10_PLUS}
  283. // {$DEFINE VER10_PLUS}
  284. // {$DEFINE VER11_PLUS}
  285. // {$DEFINE VER12_PLUS}
  286. // {$DEFINE BAD_STACK_ALIGNMENT}
  287. // {$ENDIF}
  288. //{$ENDIF}
  289. // 2003.03.09 <-
  290. // 2003.03.09 ->
  291. // Unknown compiler version - assume D7 compatible
  292. {$IFNDEF VER9x}
  293. {$IFNDEF VER10_PLUS}
  294. {$WARN SYMBOL_PLATFORM OFF}
  295. {$DEFINE VER10_PLUS}
  296. {$DEFINE VER11_PLUS}
  297. {$DEFINE VER12_PLUS}
  298. {$DEFINE VER125_PLUS}
  299. {$DEFINE VER13_PLUS}
  300. {$DEFINE VER14_PLUS}
  301. {$DEFINE VER15_PLUS}
  302. {$DEFINE BAD_STACK_ALIGNMENT}
  303. {$ENDIF}
  304. {$ENDIF}
  305. // 2003.03.09 <-
  306. ////////////////////////////////////////////////////////////////////////////////
  307. //
  308. // Compiler Options required to compile this library
  309. //
  310. ////////////////////////////////////////////////////////////////////////////////
  311. {$A+,B-,H+,J+,K-,M-,T-,X+}
  312. // Debug control - You can safely change these settings
  313. {$IFDEF DEBUG}
  314. {$C+} // ASSERTIONS
  315. {$O-} // OPTIMIZATION
  316. {$Q+} // OVERFLOWCHECKS
  317. {$R+} // RANGECHECKS
  318. {$ELSE}
  319. {$C-} // ASSERTIONS
  320. {$IFDEF GIF_NOSAFETY}
  321. {$Q-}// OVERFLOWCHECKS
  322. {$R-}// RANGECHECKS
  323. {$ENDIF}
  324. {$ENDIF}
  325. // Special options for Time2Help parser
  326. {$ifdef TIME2HELP}
  327. {$UNDEF PIXELFORMAT_TOO_SLOW}
  328. {$endif}
  329. ////////////////////////////////////////////////////////////////////////////////
  330. //
  331. // External dependecies
  332. //
  333. ////////////////////////////////////////////////////////////////////////////////
  334. uses
  335. sysutils,
  336. Windows,
  337. Graphics,
  338. Classes;
  339. ////////////////////////////////////////////////////////////////////////////////
  340. //
  341. // TGIFImage library version
  342. //
  343. ////////////////////////////////////////////////////////////////////////////////
  344. const
  345. GIFVersion = $0202;
  346. GIFVersionMajor = 2;
  347. GIFVersionMinor = 2;
  348. GIFVersionRelease = 5;
  349. ////////////////////////////////////////////////////////////////////////////////
  350. //
  351. // Misc constants and support types
  352. //
  353. ////////////////////////////////////////////////////////////////////////////////
  354. const
  355. GIFMaxColors = 256; // Max number of colors supported by GIF
  356. // Don't bother changing this value!
  357. BitmapAllocationThreshold = 500000; // Bitmap pixel count limit at which
  358. // a newly allocated bitmap will be
  359. // converted to 1 bit format before
  360. // being resized and converted to 8 bit.
  361. var
  362. {$IFDEF FAST_AS_HELL}
  363. GIFDelayExp: integer = 10; // Delay multiplier in mS.
  364. {$ELSE}
  365. GIFDelayExp: integer = 12; // Delay multiplier in mS. Tweaked.
  366. {$ENDIF}
  367. // * GIFDelayExp:
  368. // The following delay values should all
  369. // be multiplied by this value to
  370. // calculate the effective time (in mS).
  371. // According to the GIF specs, this
  372. // value should be 10.
  373. // Since our paint routines are much
  374. // faster than Mozilla's, you might need
  375. // to increase this value if your
  376. // animations loops too fast. The
  377. // optimal value is impossible to
  378. // determine since it depends on the
  379. // speed of the CPU, the viceo card,
  380. // memory and many other factors.
  381. GIFDefaultDelay: integer = 10; // * GIFDefaultDelay:
  382. // Default animation delay.
  383. // This value is used if no GCE is
  384. // defined.
  385. // (10 = 100 mS)
  386. {$IFDEF FAST_AS_HELL}
  387. GIFMinimumDelay: integer = 1; // Minimum delay (from Mozilla source).
  388. // (1 = 10 mS)
  389. {$ELSE}
  390. GIFMinimumDelay: integer = 3; // Minimum delay - Tweaked.
  391. {$ENDIF}
  392. // * GIFMinimumDelay:
  393. // The minumum delay used in the Mozilla
  394. // source is 10mS. This corresponds to a
  395. // value of 1. However, since our paint
  396. // routines are much faster than
  397. // Mozilla's, a value of 3 or 4 gives
  398. // better results.
  399. GIFMaximumDelay: integer = 1000; // * GIFMaximumDelay:
  400. // Maximum delay when painter is running
  401. // in main thread (goAsync is not set).
  402. // This value guarantees that a very
  403. // long and slow GIF does not hang the
  404. // system.
  405. // (1000 = 10000 mS = 10 Seconds)
  406. type
  407. TGIFVersion = (gvUnknown, gv87a, gv89a);
  408. TGIFVersionRec = array[0..2] of char;
  409. const
  410. GIFVersions : array[gv87a..gv89a] of TGIFVersionRec = ('87a', '89a');
  411. type
  412. // TGIFImage mostly throws exceptions of type GIFException
  413. GIFException = class(EInvalidGraphic);
  414. // Severity level as indicated in the Warning methods and the OnWarning event
  415. TGIFSeverity = (gsInfo, gsWarning, gsError);
  416. ////////////////////////////////////////////////////////////////////////////////
  417. //
  418. // Delphi 2.x support
  419. //
  420. ////////////////////////////////////////////////////////////////////////////////
  421. {$IFDEF VER9x}
  422. // Delphi 2 doesn't support TBitmap.PixelFormat
  423. {$DEFINE PIXELFORMAT_TOO_SLOW}
  424. type
  425. // TThreadList from Delphi 3 classes.pas
  426. TThreadList = class
  427. private
  428. FList: TList;
  429. FLock: TRTLCriticalSection;
  430. public
  431. constructor Create;
  432. destructor Destroy; override;
  433. procedure Add(Item: Pointer);
  434. procedure Clear;
  435. function LockList: TList;
  436. procedure Remove(Item: Pointer);
  437. procedure UnlockList;
  438. end;
  439. // From Delphi 3 sysutils.pas
  440. EOutOfMemory = class(Exception);
  441. // From Delphi 3 classes.pas
  442. EOutOfResources = class(EOutOfMemory);
  443. // From Delphi 3 windows.pas
  444. PMaxLogPalette = ^TMaxLogPalette;
  445. TMaxLogPalette = packed record
  446. palVersion: Word;
  447. palNumEntries: Word;
  448. palPalEntry: array [Byte] of TPaletteEntry;
  449. end; { TMaxLogPalette }
  450. // From Delphi 3 graphics.pas. Used by the D3 TGraphic class.
  451. TProgressStage = (psStarting, psRunning, psEnding);
  452. TProgressEvent = procedure (Sender: TObject; Stage: TProgressStage;
  453. PercentDone: Byte; RedrawNow: Boolean; const R: TRect; const Msg: string) of object;
  454. // From Delphi 3 windows.pas
  455. PRGBTriple = ^TRGBTriple;
  456. {$ENDIF}
  457. ////////////////////////////////////////////////////////////////////////////////
  458. //
  459. // Forward declarations
  460. //
  461. ////////////////////////////////////////////////////////////////////////////////
  462. type
  463. TGIFImage = class;
  464. TGIFSubImage = class;
  465. ////////////////////////////////////////////////////////////////////////////////
  466. //
  467. // TGIFItem
  468. //
  469. ////////////////////////////////////////////////////////////////////////////////
  470. TGIFItem = class(TPersistent)
  471. private
  472. FGIFImage: TGIFImage;
  473. protected
  474. function GetVersion: TGIFVersion; virtual;
  475. procedure Warning(Severity: TGIFSeverity; Message: string); virtual;
  476. public
  477. constructor Create(GIFImage: TGIFImage); virtual;
  478. procedure SaveToStream(Stream: TStream); virtual; abstract;
  479. procedure LoadFromStream(Stream: TStream); virtual; abstract;
  480. procedure SaveToFile(const Filename: string); virtual;
  481. procedure LoadFromFile(const Filename: string); virtual;
  482. property Version: TGIFVersion read GetVersion;
  483. property Image: TGIFImage read FGIFImage;
  484. end;
  485. ////////////////////////////////////////////////////////////////////////////////
  486. //
  487. // TGIFList
  488. //
  489. ////////////////////////////////////////////////////////////////////////////////
  490. TGIFList = class(TPersistent)
  491. private
  492. FItems: TList;
  493. FImage: TGIFImage;
  494. protected
  495. function GetItem(Index: Integer): TGIFItem;
  496. procedure SetItem(Index: Integer; Item: TGIFItem);
  497. function GetCount: Integer;
  498. procedure Warning(Severity: TGIFSeverity; Message: string); virtual;
  499. public
  500. constructor Create(Image: TGIFImage);
  501. destructor Destroy; override;
  502. function Add(Item: TGIFItem): Integer;
  503. procedure Clear;
  504. procedure Delete(Index: Integer);
  505. procedure Exchange(Index1, Index2: Integer);
  506. function First: TGIFItem;
  507. function IndexOf(Item: TGIFItem): Integer;
  508. procedure Insert(Index: Integer; Item: TGIFItem);
  509. function Last: TGIFItem;
  510. procedure Move(CurIndex, NewIndex: Integer);
  511. function Remove(Item: TGIFItem): Integer;
  512. procedure SaveToStream(Stream: TStream); virtual;
  513. procedure LoadFromStream(Stream: TStream; Parent: TObject); virtual; abstract;
  514. property Items[Index: Integer]: TGIFItem read GetItem write SetItem; default;
  515. property Count: Integer read GetCount;
  516. property List: TList read FItems;
  517. property Image: TGIFImage read FImage;
  518. end;
  519. ////////////////////////////////////////////////////////////////////////////////
  520. //
  521. // TGIFColorMap
  522. //
  523. ////////////////////////////////////////////////////////////////////////////////
  524. // One way to do it:
  525. // TBaseColor = (bcRed, bcGreen, bcBlue);
  526. // TGIFColor = array[bcRed..bcBlue] of BYTE;
  527. // Another way:
  528. TGIFColor = packed record
  529. Red: byte;
  530. Green: byte;
  531. Blue: byte;
  532. end;
  533. TColorMap = packed array[0..GIFMaxColors-1] of TGIFColor;
  534. PColorMap = ^TColorMap;
  535. TUsageCount = record
  536. Count : integer; // # of pixels using color index
  537. Index : integer; // Color index
  538. end;
  539. TColormapHistogram = array[0..255] of TUsageCount;
  540. TColormapReverse = array[0..255] of byte;
  541. TGIFColorMap = class(TPersistent)
  542. private
  543. FColorMap : PColorMap;
  544. FCount : integer;
  545. FCapacity : integer;
  546. FOptimized : boolean;
  547. protected
  548. function GetColor(Index: integer): TColor;
  549. procedure SetColor(Index: integer; Value: TColor);
  550. function GetBitsPerPixel: integer;
  551. function DoOptimize: boolean;
  552. procedure SetCapacity(Size: integer);
  553. procedure Warning(Severity: TGIFSeverity; Message: string); virtual; abstract;
  554. procedure BuildHistogram(var Histogram: TColormapHistogram); virtual; abstract;
  555. procedure MapImages(var Map: TColormapReverse); virtual; abstract;
  556. public
  557. constructor Create;
  558. destructor Destroy; override;
  559. class function Color2RGB(Color: TColor): TGIFColor;
  560. class function RGB2Color(Color: TGIFColor): TColor;
  561. procedure SaveToStream(Stream: TStream);
  562. procedure LoadFromStream(Stream: TStream; Count: integer);
  563. procedure Assign(Source: TPersistent); override;
  564. function IndexOf(Color: TColor): integer;
  565. function Add(Color: TColor): integer;
  566. function AddUnique(Color: TColor): integer;
  567. procedure Delete(Index: integer);
  568. procedure Clear;
  569. function Optimize: boolean; virtual; abstract;
  570. procedure Changed; virtual; abstract;
  571. procedure ImportPalette(Palette: HPalette);
  572. procedure ImportColorTable(Pal: pointer; Count: integer);
  573. procedure ImportDIBColors(Handle: HDC);
  574. procedure ImportColorMap(Map: TColorMap; Count: integer);
  575. function ExportPalette: HPalette;
  576. property Colors[Index: integer]: TColor read GetColor write SetColor; default;
  577. property Data: PColorMap read FColorMap;
  578. property Count: integer read FCount;
  579. property Optimized: boolean read FOptimized write FOptimized;
  580. property BitsPerPixel: integer read GetBitsPerPixel;
  581. end;
  582. ////////////////////////////////////////////////////////////////////////////////
  583. //
  584. // TGIFHeader
  585. //
  586. ////////////////////////////////////////////////////////////////////////////////
  587. TLogicalScreenDescriptor = packed record
  588. ScreenWidth: word; { logical screen width }
  589. ScreenHeight: word; { logical screen height }
  590. PackedFields: byte; { packed fields }
  591. BackgroundColorIndex: byte; { index to global color table }
  592. AspectRatio: byte; { actual ratio = (AspectRatio + 15) / 64 }
  593. end;
  594. TGIFHeader = class(TGIFItem)
  595. private
  596. FLogicalScreenDescriptor: TLogicalScreenDescriptor;
  597. FColorMap : TGIFColorMap;
  598. procedure Prepare;
  599. protected
  600. function GetVersion: TGIFVersion; override;
  601. function GetBackgroundColor: TColor;
  602. procedure SetBackgroundColor(Color: TColor);
  603. procedure SetBackgroundColorIndex(Index: BYTE);
  604. function GetBitsPerPixel: integer;
  605. function GetColorResolution: integer;
  606. public
  607. constructor Create(GIFImage: TGIFImage); override;
  608. destructor Destroy; override;
  609. procedure Assign(Source: TPersistent); override;
  610. procedure SaveToStream(Stream: TStream); override;
  611. procedure LoadFromStream(Stream: TStream); override;
  612. procedure Clear;
  613. property Version: TGIFVersion read GetVersion;
  614. property Width: WORD read FLogicalScreenDescriptor.ScreenWidth
  615. write FLogicalScreenDescriptor.ScreenWidth;
  616. property Height: WORD read FLogicalScreenDescriptor.ScreenHeight
  617. write FLogicalScreenDescriptor.Screenheight;
  618. property BackgroundColorIndex: BYTE read FLogicalScreenDescriptor.BackgroundColorIndex
  619. write SetBackgroundColorIndex;
  620. property BackgroundColor: TColor read GetBackgroundColor
  621. write SetBackgroundColor;
  622. property AspectRatio: BYTE read FLogicalScreenDescriptor.AspectRatio
  623. write FLogicalScreenDescriptor.AspectRatio;
  624. property ColorMap: TGIFColorMap read FColorMap;
  625. property BitsPerPixel: integer read GetBitsPerPixel;
  626. property ColorResolution: integer read GetColorResolution;
  627. end;
  628. ////////////////////////////////////////////////////////////////////////////////
  629. //
  630. // TGIFExtension
  631. //
  632. ////////////////////////////////////////////////////////////////////////////////
  633. TGIFExtensionType = BYTE;
  634. TGIFExtension = class;
  635. TGIFExtensionClass = class of TGIFExtension;
  636. TGIFGraphicControlExtension = class;
  637. TGIFExtension = class(TGIFItem)
  638. private
  639. FSubImage: TGIFSubImage;
  640. protected
  641. function GetExtensionType: TGIFExtensionType; virtual; abstract;
  642. function GetVersion: TGIFVersion; override;
  643. function DoReadFromStream(Stream: TStream): TGIFExtensionType;
  644. class procedure RegisterExtension(elabel: BYTE; eClass: TGIFExtensionClass);
  645. class function FindExtension(Stream: TStream): TGIFExtensionClass;
  646. class function FindSubExtension(Stream: TStream): TGIFExtensionClass; virtual;
  647. public
  648. // Ignore compiler warning about hiding base class constructor
  649. constructor Create(ASubImage: TGIFSubImage); {$IFDEF VER12_PLUS} reintroduce; {$ENDIF} virtual;
  650. destructor Destroy; override;
  651. procedure SaveToStream(Stream: TStream); override;
  652. procedure LoadFromStream(Stream: TStream); override;
  653. property ExtensionType: TGIFExtensionType read GetExtensionType;
  654. property SubImage: TGIFSubImage read FSubImage;
  655. end;
  656. ////////////////////////////////////////////////////////////////////////////////
  657. //
  658. // TGIFSubImage
  659. //
  660. ////////////////////////////////////////////////////////////////////////////////
  661. TGIFExtensionList = class(TGIFList)
  662. protected
  663. function GetExtension(Index: Integer): TGIFExtension;
  664. procedure SetExtension(Index: Integer; Extension: TGIFExtension);
  665. public
  666. procedure LoadFromStream(Stream: TStream; Parent: TObject); override;
  667. property Extensions[Index: Integer]: TGIFExtension read GetExtension write SetExtension; default;
  668. end;
  669. TImageDescriptor = packed record
  670. Separator: byte; { fixed value of ImageSeparator }
  671. Left: word; { Column in pixels in respect to left edge of logical screen }
  672. Top: word; { row in pixels in respect to top of logical screen }
  673. Width: word; { width of image in pixels }
  674. Height: word; { height of image in pixels }
  675. PackedFields: byte; { Bit fields }
  676. end;
  677. TGIFSubImage = class(TGIFItem)
  678. private
  679. FBitmap : TBitmap;
  680. FMask : HBitmap;
  681. FNeedMask : boolean;
  682. FLocalPalette : HPalette;
  683. FData : PChar;
  684. FDataSize : integer;
  685. FColorMap : TGIFColorMap;
  686. FImageDescriptor : TImageDescriptor;
  687. FExtensions : TGIFExtensionList;
  688. FTransparent : boolean;
  689. FGCE : TGIFGraphicControlExtension;
  690. procedure Prepare;
  691. procedure Compress(Stream: TStream);
  692. procedure Decompress(Stream: TStream);
  693. protected
  694. function GetVersion: TGIFVersion; override;
  695. function GetInterlaced: boolean;
  696. procedure SetInterlaced(Value: boolean);
  697. function GetColorResolution: integer;
  698. function GetBitsPerPixel: integer;
  699. procedure AssignTo(Dest: TPersistent); override;
  700. function DoGetBitmap: TBitmap;
  701. function DoGetDitherBitmap: TBitmap;
  702. function GetBitmap: TBitmap;
  703. procedure SetBitmap(Value: TBitmap);
  704. procedure FreeMask;
  705. function GetEmpty: Boolean;
  706. function GetPalette: HPALETTE;
  707. procedure SetPalette(Value: HPalette);
  708. function GetActiveColorMap: TGIFColorMap;
  709. function GetBoundsRect: TRect;
  710. procedure SetBoundsRect(const Value: TRect);
  711. procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);
  712. function GetClientRect: TRect;
  713. function GetPixel(x, y: integer): BYTE;
  714. function GetScanline(y: integer): pointer;
  715. procedure NewBitmap;
  716. procedure FreeBitmap;
  717. procedure NewImage;
  718. procedure FreeImage;
  719. procedure NeedImage;
  720. function ScaleRect(DestRect: TRect): TRect;
  721. function HasMask: boolean;
  722. function GetBounds(Index: integer): WORD;
  723. procedure SetBounds(Index: integer; Value: WORD);
  724. function GetHasBitmap: boolean;
  725. procedure SetHasBitmap(Value: boolean);
  726. public
  727. constructor Create(GIFImage: TGIFImage); override;
  728. destructor Destroy; override;
  729. procedure Clear;
  730. procedure SaveToStream(Stream: TStream); override;
  731. procedure LoadFromStream(Stream: TStream); override;
  732. procedure Assign(Source: TPersistent); override;
  733. procedure Draw(ACanvas: TCanvas; const Rect: TRect;
  734. DoTransparent, DoTile: boolean);
  735. procedure StretchDraw(ACanvas: TCanvas; const Rect: TRect;
  736. DoTransparent, DoTile: boolean);
  737. procedure Crop;
  738. procedure Merge(Previous: TGIFSubImage);
  739. property HasBitmap: boolean read GetHasBitmap write SetHasBitmap;
  740. property Left: WORD index 1 read GetBounds write SetBounds;
  741. property Top: WORD index 2 read GetBounds write SetBounds;
  742. property Width: WORD index 3 read GetBounds write SetBounds;
  743. property Height: WORD index 4 read GetBounds write SetBounds;
  744. property BoundsRect: TRect read GetBoundsRect write SetBoundsRect;
  745. property ClientRect: TRect read GetClientRect;
  746. property Interlaced: boolean read GetInterlaced write SetInterlaced;
  747. property ColorMap: TGIFColorMap read FColorMap;
  748. property ActiveColorMap: TGIFColorMap read GetActiveColorMap;
  749. property Data: PChar read FData;
  750. property DataSize: integer read FDataSize;
  751. property Extensions: TGIFExtensionList read FExtensions;
  752. property Version: TGIFVersion read GetVersion;
  753. property ColorResolution: integer read GetColorResolution;
  754. property BitsPerPixel: integer read GetBitsPerPixel;
  755. property Bitmap: TBitmap read GetBitmap write SetBitmap;
  756. property Mask: HBitmap read FMask;
  757. property Palette: HPALETTE read GetPalette write SetPalette;
  758. property Empty: boolean read GetEmpty;
  759. property Transparent: boolean read FTransparent;
  760. property GraphicControlExtension: TGIFGraphicControlExtension read FGCE;
  761. property Pixels[x, y: integer]: BYTE read GetPixel;
  762. property Scanline[y: integer]: pointer read GetScanline;
  763. end;
  764. ////////////////////////////////////////////////////////////////////////////////
  765. //
  766. // TGIFTrailer
  767. //
  768. ////////////////////////////////////////////////////////////////////////////////
  769. TGIFTrailer = class(TGIFItem)
  770. procedure SaveToStream(Stream: TStream); override;
  771. procedure LoadFromStream(Stream: TStream); override;
  772. end;
  773. ////////////////////////////////////////////////////////////////////////////////
  774. //
  775. // TGIFGraphicControlExtension
  776. //
  777. ////////////////////////////////////////////////////////////////////////////////
  778. // Graphic Control Extension block a.k.a GCE
  779. TGIFGCERec = packed record
  780. BlockSize: byte; { should be 4 }
  781. PackedFields: Byte;
  782. DelayTime: Word; { in centiseconds }
  783. TransparentColorIndex: Byte;
  784. Terminator: Byte;
  785. end;
  786. TDisposalMethod = (dmNone, dmNoDisposal, dmBackground, dmPrevious);
  787. TGIFGraphicControlExtension = class(TGIFExtension)
  788. private
  789. FGCExtension: TGIFGCERec;
  790. protected
  791. function GetExtensionType: TGIFExtensionType; override;
  792. function GetTransparent: boolean;
  793. procedure SetTransparent(Value: boolean);
  794. function GetTransparentColor: TColor;
  795. procedure SetTransparentColor(Color: TColor);
  796. function GetTransparentColorIndex: BYTE;
  797. procedure SetTransparentColorIndex(Value: BYTE);
  798. function GetDelay: WORD;
  799. procedure SetDelay(Value: WORD);
  800. function GetUserInput: boolean;
  801. procedure SetUserInput(Value: boolean);
  802. function GetDisposal: TDisposalMethod;
  803. procedure SetDisposal(Value: TDisposalMethod);
  804. public
  805. constructor Create(ASubImage: TGIFSubImage); override;
  806. destructor Destroy; override;
  807. procedure SaveToStream(Stream: TStream); override;
  808. procedure LoadFromStream(Stream: TStream); override;
  809. property Delay: WORD read GetDelay write SetDelay;
  810. property Transparent: boolean read GetTransparent write SetTransparent;
  811. property TransparentColorIndex: BYTE read GetTransparentColorIndex
  812. write SetTransparentColorIndex;
  813. property TransparentColor: TColor read GetTransparentColor write SetTransparentColor;
  814. property UserInput: boolean read GetUserInput write SetUserInput;
  815. property Disposal: TDisposalMethod read GetDisposal write SetDisposal;
  816. end;
  817. ////////////////////////////////////////////////////////////////////////////////
  818. //
  819. // TGIFTextExtension
  820. //
  821. ////////////////////////////////////////////////////////////////////////////////
  822. TGIFPlainTextExtensionRec = packed record
  823. BlockSize: byte; { should be 12 }
  824. Left, Top, Width, Height: Word;
  825. CellWidth, CellHeight: Byte;
  826. TextFGColorIndex,
  827. TextBGColorIndex: Byte;
  828. end;
  829. TGIFTextExtension = class(TGIFExtension)
  830. private
  831. FText : TStrings;
  832. FPlainTextExtension : TGIFPlainTextExtensionRec;
  833. protected
  834. function GetExtensionType: TGIFExtensionType; override;
  835. function GetForegroundColor: TColor;
  836. procedure SetForegroundColor(Color: TColor);
  837. function GetBackgroundColor: TColor;
  838. procedure SetBackgroundColor(Color: TColor);
  839. function GetBounds(Index: integer): WORD;
  840. procedure SetBounds(Index: integer; Value: WORD);
  841. function GetCharWidthHeight(Index: integer): BYTE;
  842. procedure SetCharWidthHeight(Index: integer; Value: BYTE);
  843. function GetColorIndex(Index: integer): BYTE;
  844. procedure SetColorIndex(Index: integer; Value: BYTE);
  845. public
  846. constructor Create(ASubImage: TGIFSubImage); override;
  847. destructor Destroy; override;
  848. procedure SaveToStream(Stream: TStream); override;
  849. procedure LoadFromStream(Stream: TStream); override;
  850. property Left: WORD index 1 read GetBounds write SetBounds;
  851. property Top: WORD index 2 read GetBounds write SetBounds;
  852. property GridWidth: WORD index 3 read GetBounds write SetBounds;
  853. property GridHeight: WORD index 4 read GetBounds write SetBounds;
  854. property CharWidth: BYTE index 1 read GetCharWidthHeight write SetCharWidthHeight;
  855. property CharHeight: BYTE index 2 read GetCharWidthHeight write SetCharWidthHeight;
  856. property ForegroundColorIndex: BYTE index 1 read GetColorIndex write SetColorIndex;
  857. property ForegroundColor: TColor read GetForegroundColor;
  858. property BackgroundColorIndex: BYTE index 2 read GetColorIndex write SetColorIndex;
  859. property BackgroundColor: TColor read GetBackgroundColor;
  860. property Text: TStrings read FText write FText;
  861. end;
  862. ////////////////////////////////////////////////////////////////////////////////
  863. //
  864. // TGIFCommentExtension
  865. //
  866. ////////////////////////////////////////////////////////////////////////////////
  867. TGIFCommentExtension = class(TGIFExtension)
  868. private
  869. FText : TStrings;
  870. protected
  871. function GetExtensionType: TGIFExtensionType; override;
  872. public
  873. constructor Create(ASubImage: TGIFSubImage); override;
  874. destructor Destroy; override;
  875. procedure SaveToStream(Stream: TStream); override;
  876. procedure LoadFromStream(Stream: TStream); override;
  877. property Text: TStrings read FText;
  878. end;
  879. ////////////////////////////////////////////////////////////////////////////////
  880. //
  881. // TGIFApplicationExtension
  882. //
  883. ////////////////////////////////////////////////////////////////////////////////
  884. TGIFIdentifierCode = array[0..7] of char;
  885. TGIFAuthenticationCode = array[0..2] of char;
  886. TGIFApplicationRec = packed record
  887. Identifier : TGIFIdentifierCode;
  888. Authentication : TGIFAuthenticationCode;
  889. end;
  890. TGIFApplicationExtension = class;
  891. TGIFAppExtensionClass = class of TGIFApplicationExtension;
  892. TGIFApplicationExtension = class(TGIFExtension)
  893. private
  894. FIdent : TGIFApplicationRec;
  895. function GetAuthentication: string;
  896. function GetIdentifier: string;
  897. protected
  898. function GetExtensionType: TGIFExtensionType; override;
  899. procedure SetAuthentication(const Value: string);
  900. procedure SetIdentifier(const Value: string);
  901. procedure SaveData(Stream: TStream); virtual; abstract;
  902. procedure LoadData(Stream: TStream); virtual; abstract;
  903. public
  904. constructor Create(ASubImage: TGIFSubImage); override;
  905. destructor Destroy; override;
  906. procedure SaveToStream(Stream: TStream); override;
  907. procedure LoadFromStream(Stream: TStream); override;
  908. class procedure RegisterExtension(eIdent: TGIFApplicationRec; eClass: TGIFAppExtensionClass);
  909. class function FindSubExtension(Stream: TStream): TGIFExtensionClass; override;
  910. property Identifier: string read GetIdentifier write SetIdentifier;
  911. property Authentication: string read GetAuthentication write SetAuthentication;
  912. end;
  913. ////////////////////////////////////////////////////////////////////////////////
  914. //
  915. // TGIFUnknownAppExtension
  916. //
  917. ////////////////////////////////////////////////////////////////////////////////
  918. TGIFBlock = class(TObject)
  919. private
  920. FSize : BYTE;
  921. FData : pointer;
  922. public
  923. constructor Create(ASize: integer);
  924. destructor Destroy; override;
  925. procedure SaveToStream(Stream: TStream);
  926. procedure LoadFromStream(Stream: TStream);
  927. property Size: BYTE read FSize;
  928. property Data: pointer read FData;
  929. end;
  930. TGIFUnknownAppExtension = class(TGIFApplicationExtension)
  931. private
  932. FBlocks : TList;
  933. protected
  934. procedure SaveData(Stream: TStream); override;
  935. procedure LoadData(Stream: TStream); override;
  936. public
  937. constructor Create(ASubImage: TGIFSubImage); override;
  938. destructor Destroy; override;
  939. property Blocks: TList read FBlocks;
  940. end;
  941. ////////////////////////////////////////////////////////////////////////////////
  942. //
  943. // TGIFAppExtNSLoop
  944. //
  945. ////////////////////////////////////////////////////////////////////////////////
  946. TGIFAppExtNSLoop = class(TGIFApplicationExtension)
  947. private
  948. FLoops : WORD;
  949. FBufferSize : DWORD;
  950. protected
  951. procedure SaveData(Stream: TStream); override;
  952. procedure LoadData(Stream: TStream); override;
  953. public
  954. constructor Create(ASubImage: TGIFSubImage); override;
  955. property Loops: WORD read FLoops write FLoops;
  956. property BufferSize: DWORD read FBufferSize write FBufferSize;
  957. end;
  958. ////////////////////////////////////////////////////////////////////////////////
  959. //
  960. // TGIFImage
  961. //
  962. ////////////////////////////////////////////////////////////////////////////////
  963. TGIFImageList = class(TGIFList)
  964. protected
  965. function GetImage(Index: Integer): TGIFSubImage;
  966. procedure SetImage(Index: Integer; SubImage: TGIFSubImage);
  967. public
  968. procedure LoadFromStream(Stream: TStream; Parent: TObject); override;
  969. procedure SaveToStream(Stream: TStream); override;
  970. property SubImages[Index: Integer]: TGIFSubImage read GetImage write SetImage; default;
  971. end;
  972. // Compression algorithms
  973. TGIFCompression =
  974. (gcLZW, // Normal LZW compression
  975. gcRLE // GIF compatible RLE compression
  976. );
  977. // Color reduction methods
  978. TColorReduction =
  979. (rmNone, // Do not perform color reduction
  980. rmWindows20, // Reduce to the Windows 20 color system palette
  981. rmWindows256, // Reduce to the Windows 256 color halftone palette (Only works in 256 color display mode)
  982. rmWindowsGray, // Reduce to the Windows 4 grayscale colors
  983. rmMonochrome, // Reduce to a black/white monochrome palette
  984. rmGrayScale, // Reduce to a uniform 256 shade grayscale palette
  985. rmNetscape, // Reduce to the Netscape 216 color palette
  986. rmQuantize, // Reduce to optimal 2^n color palette
  987. rmQuantizeWindows, // Reduce to optimal 256 color windows palette
  988. rmPalette // Reduce to custom palette
  989. );
  990. TDitherMode =
  991. (dmNearest, // Nearest color matching w/o error correction
  992. dmFloydSteinberg, // Floyd Steinberg Error Diffusion dithering
  993. dmStucki, // Stucki Error Diffusion dithering
  994. dmSierra, // Sierra Error Diffusion dithering
  995. dmJaJuNI, // Jarvis, Judice & Ninke Error Diffusion dithering
  996. dmSteveArche, // Stevenson & Arche Error Diffusion dithering
  997. dmBurkes // Burkes Error Diffusion dithering
  998. // dmOrdered, // Ordered dither
  999. );
  1000. // Optimization options
  1001. TGIFOptimizeOption =
  1002. (ooCrop, // Crop animated GIF frames
  1003. ooMerge, // Merge pixels of same color
  1004. ooCleanup, // Remove comments and application extensions
  1005. ooColorMap, // Sort color map by usage and remove unused entries
  1006. ooReduceColors // Reduce color depth ***NOT IMPLEMENTED***
  1007. );
  1008. TGIFOptimizeOptions = set of TGIFOptimizeOption;
  1009. TGIFDrawOption =
  1010. (goAsync, // Asyncronous draws (paint in thread)
  1011. goTransparent, // Transparent draws
  1012. goAnimate, // Animate draws
  1013. goLoop, // Loop animations
  1014. goLoopContinously, // Ignore loop count and loop forever
  1015. goValidateCanvas, // Validate canvas in threaded paint ***NOT IMPLEMENTED***
  1016. goDirectDraw, // Draw() directly on canvas
  1017. goClearOnLoop, // Clear animation on loop
  1018. goTile, // Tiled display
  1019. goDither, // Dither to Netscape palette
  1020. goAutoDither // Only dither on 256 color systems
  1021. );
  1022. TGIFDrawOptions = set of TGIFDrawOption;
  1023. // Note: if goAsync is not set then goDirectDraw should be set. Otherwise
  1024. // the image will not be displayed.
  1025. PGIFPainter = ^TGIFPainter;
  1026. TGIFPainter = class(TThread)
  1027. private
  1028. FImage : TGIFImage; // The TGIFImage that owns this painter
  1029. FCanvas : TCanvas; // Destination canvas
  1030. FRect : TRect; // Destination rect
  1031. FDrawOptions : TGIFDrawOptions;// Paint options
  1032. FAnimationSpeed : integer; // Animation speed %
  1033. FActiveImage : integer; // Current frame
  1034. Disposal , // Used by synchronized paint
  1035. OldDisposal : TDisposalMethod;// Used by synchronized paint
  1036. BackupBuffer : TBitmap; // Used by synchronized paint
  1037. FrameBuffer : TBitmap; // Used by synchronized paint
  1038. Background : TBitmap; // Used by synchronized paint
  1039. ValidateDC : HDC;
  1040. DoRestart : boolean; // Flag used to restart animation
  1041. FStarted : boolean; // Flag used to signal start of paint
  1042. PainterRef : PGIFPainter; // Pointer to var referencing painter
  1043. FEventHandle : THandle; // Animation delay event
  1044. ExceptObject : Exception; // Eaten exception
  1045. ExceptAddress : pointer; // Eaten exceptions address
  1046. FEvent : TNotifyEvent; // Used by synchronized events
  1047. FOnStartPaint : TNotifyEvent;
  1048. FOnPaint : TNotifyEvent;
  1049. FOnAfterPaint : TNotifyEvent;
  1050. FOnLoop : TNotifyEvent;
  1051. FOnEndPaint : TNotifyEvent;
  1052. procedure DoOnTerminate(Sender: TObject);// Sync. shutdown procedure
  1053. procedure DoSynchronize(Method: TThreadMethod);// Conditional sync stub
  1054. {$ifdef SERIALIZE_RENDER}
  1055. procedure PrefetchBitmap; // Sync. bitmap prefetch
  1056. {$endif}
  1057. procedure DoPaintFrame; // Sync. buffered paint procedure
  1058. procedure DoPaint; // Sync. paint procedure
  1059. procedure DoEvent;
  1060. procedure SetActiveImage(const Value: integer);// Sync. event procedure
  1061. protected
  1062. procedure Execute; override;
  1063. procedure SetAnimationSpeed(Value: integer);
  1064. public
  1065. constructor Create(AImage: TGIFImage; ACanvas: TCanvas; ARect: TRect;
  1066. Options: TGIFDrawOptions);
  1067. constructor CreateRef(Painter: PGIFPainter; AImage: TGIFImage; ACanvas: TCanvas; ARect: TRect;
  1068. Options: TGIFDrawOptions);
  1069. destructor Destroy; override;
  1070. procedure Start;
  1071. procedure Stop;
  1072. procedure Restart;
  1073. property Image: TGIFImage read FImage;
  1074. property Canvas: TCanvas read FCanvas;
  1075. property Rect: TRect read FRect write FRect;
  1076. property DrawOptions: TGIFDrawOptions read FDrawOptions write FDrawOptions;
  1077. property AnimationSpeed: integer read FAnimationSpeed write SetAnimationSpeed;
  1078. property Started: boolean read FStarted;
  1079. property ActiveImage: integer read FActiveImage write SetActiveImage;
  1080. property OnStartPaint: TNotifyEvent read FOnStartPaint write FOnStartPaint;
  1081. property OnPaint: TNotifyEvent read FOnPaint write FOnPaint;
  1082. property OnAfterPaint: TNotifyEvent read FOnAfterPaint write FOnAfterPaint;
  1083. property OnLoop: TNotifyEvent read FOnLoop write FOnLoop;
  1084. property OnEndPaint : TNotifyEvent read FOnEndPaint write FOnEndPaint ;
  1085. property EventHandle: THandle read FEventHandle;
  1086. end;
  1087. TGIFWarning = procedure(Sender: TObject; Severity: TGIFSeverity; Message: string) of object;
  1088. TGIFImage = class(TGraphic)
  1089. private
  1090. IsDrawing : Boolean;
  1091. IsInsideGetPalette : boolean;
  1092. FImages : TGIFImageList;
  1093. FHeader : TGIFHeader;
  1094. FGlobalPalette : HPalette;
  1095. FPainters : TThreadList;
  1096. FDrawOptions : TGIFDrawOptions;
  1097. FColorReduction : TColorReduction;
  1098. FReductionBits : integer;
  1099. FDitherMode : TDitherMode;
  1100. FCompression : TGIFCompression;
  1101. FOnWarning : TGIFWarning;
  1102. FBitmap : TBitmap;
  1103. FDrawPainter : TGIFPainter;
  1104. FThreadPriority : TThreadPriority;
  1105. FAnimationSpeed : integer;
  1106. FForceFrame: Integer; // 2004.03.09
  1107. FDrawBackgroundColor: TColor;
  1108. FOnStartPaint : TNotifyEvent;
  1109. FOnPaint : TNotifyEvent;
  1110. FOnAfterPaint : TNotifyEvent;
  1111. FOnLoop : TNotifyEvent;
  1112. FOnEndPaint : TNotifyEvent;
  1113. {$IFDEF VER9x}
  1114. FPaletteModified : Boolean;
  1115. FOnProgress : TProgressEvent;
  1116. {$ENDIF}
  1117. function GetAnimate: Boolean; // 2002.07.07
  1118. procedure SetAnimate(const Value: Boolean); // 2002.07.07
  1119. procedure SetForceFrame(const Value: Integer); // 2004.03.09
  1120. protected
  1121. // Obsolete: procedure Changed(Sender: TObject); {$IFDEF VER9x} virtual; {$ELSE} override; {$ENDIF}
  1122. function GetHeight: Integer; override;
  1123. procedure SetHeight(Value: Integer); override;
  1124. function GetWidth: Integer; override;
  1125. procedure SetWidth(Value: Integer); override;
  1126. procedure AssignTo(Dest: TPersistent); override;
  1127. function InternalPaint(Painter: PGIFPainter; ACanvas: TCanvas; const Rect: TRect; Options: TGIFDrawOptions): TGIFPainter;
  1128. procedure Draw(ACanvas: TCanvas; const Rect: TRect); override;
  1129. function Equals(Graphic: TGraphic): Boolean; override;
  1130. function GetPalette: HPALETTE; {$IFDEF VER9x} virtual; {$ELSE} override; {$ENDIF}
  1131. procedure SetPalette(Value: HPalette); {$IFDEF VER9x} virtual; {$ELSE} override; {$ENDIF}
  1132. function GetEmpty: Boolean; override;
  1133. procedure WriteData(Stream: TStream); override;
  1134. function GetIsTransparent: Boolean;
  1135. function GetVersion: TGIFVersion;
  1136. function GetColorResolution: integer;
  1137. function GetBitsPerPixel: integer;
  1138. function GetBackgroundColorIndex: BYTE;
  1139. procedure SetBackgroundColorIndex(const Value: BYTE);
  1140. function GetBackgroundColor: TColor;
  1141. procedure SetBackgroundColor(const Value: TColor);
  1142. function GetAspectRatio: BYTE;
  1143. procedure SetAspectRatio(const Value: BYTE);
  1144. procedure SetDrawOptions(Value: TGIFDrawOptions);
  1145. procedure SetAnimationSpeed(Value: integer);
  1146. procedure SetReductionBits(Value: integer);
  1147. procedure NewImage;
  1148. function GetBitmap: TBitmap;
  1149. function NewBitmap: TBitmap;
  1150. procedure FreeBitmap;
  1151. function GetColorMap: TGIFColorMap;
  1152. function GetDoDither: boolean;
  1153. property DrawPainter: TGIFPainter read FDrawPainter; // Extremely volatile
  1154. property DoDither: boolean read GetDoDither;
  1155. {$IFDEF VER9x}
  1156. procedure Progress(Sender: TObject; Stage: TProgressStage;
  1157. PercentDone: Byte; RedrawNow: Boolean; const R: TRect; const Msg: string); dynamic;
  1158. {$ENDIF}
  1159. public
  1160. constructor Create; override;
  1161. destructor Destroy; override;
  1162. procedure SaveToStream(Stream: TStream); override;
  1163. procedure LoadFromStream(Stream: TStream); override;
  1164. procedure LoadFromResourceName(Instance: THandle; const ResName: String); // 2002.07.07
  1165. function Add(Source: TPersistent): integer;
  1166. procedure Pack;
  1167. procedure OptimizeColorMap;
  1168. procedure Optimize(Options: TGIFOptimizeOptions;
  1169. ColorReduction: TColorReduction; DitherMode: TDitherMode;
  1170. ReductionBits: integer);
  1171. procedure Clear;
  1172. procedure StopDraw;
  1173. function Paint(ACanvas: TCanvas; const Rect: TRect; Options: TGIFDrawOptions): TGIFPainter;
  1174. procedure PaintStart;
  1175. procedure PaintPause;
  1176. procedure PaintStop;
  1177. procedure PaintResume;
  1178. procedure PaintRestart;
  1179. procedure Warning(Sender: TObject; Severity: TGIFSeverity; Message: string); virtual;
  1180. procedure Assign(Source: TPersistent); override;
  1181. procedure LoadFromClipboardFormat(AFormat: Word; AData: THandle;
  1182. APalette: HPALETTE); override;
  1183. procedure SaveToClipboardFormat(var AFormat: Word; var AData: THandle;
  1184. var APalette: HPALETTE); override;
  1185. property GlobalColorMap: TGIFColorMap read GetColorMap;
  1186. property Version: TGIFVersion read GetVersion;
  1187. property Images: TGIFImageList read FImages;
  1188. property ColorResolution: integer read GetColorResolution;
  1189. property BitsPerPixel: integer read GetBitsPerPixel;
  1190. property BackgroundColorIndex: BYTE read GetBackgroundColorIndex write SetBackgroundColorIndex;
  1191. property BackgroundColor: TColor read GetBackgroundColor write SetBackgroundColor;
  1192. property AspectRatio: BYTE read GetAspectRatio write SetAspectRatio;
  1193. property Header: TGIFHeader read FHeader; // ***OBSOLETE***
  1194. property IsTransparent: boolean read GetIsTransparent;
  1195. property DrawOptions: TGIFDrawOptions read FDrawOptions write SetDrawOptions;
  1196. property DrawBackgroundColor: TColor read FDrawBackgroundColor write FDrawBackgroundColor;
  1197. property ColorReduction: TColorReduction read FColorReduction write FColorReduction;
  1198. property ReductionBits: integer read FReductionBits write SetReductionBits;
  1199. property DitherMode: TDitherMode read FDitherMode write FDitherMode;
  1200. property Compression: TGIFCompression read FCompression write FCompression;
  1201. property AnimationSpeed: integer read FAnimationSpeed write SetAnimationSpeed;
  1202. property Animate: Boolean read GetAnimate write SetAnimate; // 2002.07.07
  1203. property ForceFrame: Integer read FForceFrame write SetForceFrame; // 2004.03.09
  1204. property Painters: TThreadList read FPainters;
  1205. property ThreadPriority: TThreadPriority read FThreadPriority write FThreadPriority;
  1206. property Bitmap: TBitmap read GetBitmap; // Volatile - beware!
  1207. property OnWarning: TGIFWarning read FOnWarning write FOnWarning;
  1208. property OnStartPaint: TNotifyEvent read FOnStartPaint write FOnStartPaint;
  1209. property OnPaint: TNotifyEvent read FOnPaint write FOnPaint;
  1210. property OnAfterPaint: TNotifyEvent read FOnAfterPaint write FOnAfterPaint;
  1211. property OnLoop: TNotifyEvent read FOnLoop write FOnLoop;
  1212. property OnEndPaint : TNotifyEvent read FOnEndPaint write FOnEndPaint ;
  1213. {$IFDEF VER9x}
  1214. property Palette: HPALETTE read GetPalette write SetPalette;
  1215. property PaletteModified: Boolean read FPaletteModified write FPaletteModified;
  1216. property OnProgress: TProgressEvent read FOnProgress write FOnProgress;
  1217. {$ENDIF}
  1218. end;
  1219. ////////////////////////////////////////////////////////////////////////////////
  1220. //
  1221. // Utility routines
  1222. //
  1223. ////////////////////////////////////////////////////////////////////////////////
  1224. // WebPalette creates a 216 color uniform palette a.k.a. the Netscape Palette
  1225. function WebPalette: HPalette;
  1226. // ReduceColors
  1227. // Map colors in a bitmap to their nearest representation in a palette using
  1228. // the methods specified by the ColorReduction and DitherMode parameters.
  1229. // The ReductionBits parameter specifies the desired number of colors (bits
  1230. // per pixel) when the reduction method is rmQuantize. The CustomPalette
  1231. // specifies the palette when the rmPalette reduction method is used.
  1232. function ReduceColors(Bitmap: TBitmap; ColorReduction: TColorReduction;
  1233. DitherMode: TDitherMode; ReductionBits: integer; CustomPalette: hPalette): TBitmap;
  1234. // CreateOptimizedPaletteFromManyBitmaps
  1235. //: Performs Color Quantization on multiple bitmaps.
  1236. // The Bitmaps parameter is a list of bitmaps. Returns an optimized palette.
  1237. function CreateOptimizedPaletteFromManyBitmaps(Bitmaps: TList; Colors, ColorBits: integer;
  1238. Windows: boolean): hPalette;
  1239. {$IFDEF VER9x}
  1240. // From Delphi 3 graphics.pas
  1241. type
  1242. TPixelFormat = (pfDevice, pf1bit, pf4bit, pf8bit, pf15bit, pf16bit, pf24bit, pf32bit, pfCustom);
  1243. {$ENDIF}
  1244. procedure InternalGetDIBSizes(Bitmap: HBITMAP; var InfoHeaderSize: Integer;
  1245. var ImageSize: longInt; PixelFormat: TPixelFormat);
  1246. function InternalGetDIB(Bitmap: HBITMAP; Palette: HPALETTE;
  1247. var BitmapInfo; var Bits; PixelFormat: TPixelFormat): Boolean;
  1248. ////////////////////////////////////////////////////////////////////////////////
  1249. //
  1250. // Global variables
  1251. //
  1252. ////////////////////////////////////////////////////////////////////////////////
  1253. // GIF Clipboard format identifier for use by LoadFromClipboardFormat and
  1254. // SaveToClipboardFormat.
  1255. // Set in Initialization section.
  1256. var
  1257. CF_GIF: WORD;
  1258. ////////////////////////////////////////////////////////////////////////////////
  1259. //
  1260. // Library defaults
  1261. //
  1262. ////////////////////////////////////////////////////////////////////////////////
  1263. var
  1264. //: Default options for TGIFImage.DrawOptions.
  1265. GIFImageDefaultDrawOptions : TGIFDrawOptions =
  1266. [goAsync, goLoop, goTransparent, goAnimate, goDither, goAutoDither
  1267. {$IFDEF STRICT_MOZILLA}
  1268. ,goClearOnLoop
  1269. {$ENDIF}
  1270. ];
  1271. // WARNING! Do not use goAsync and goDirectDraw unless you have absolute
  1272. // control of the destination canvas.
  1273. // TGIFPainter will continue to write on the canvas even after the canvas has
  1274. // been deleted, unless *you* prevent it.
  1275. // The goValidateCanvas option will fix this problem if it is ever implemented.
  1276. //: Default color reduction methods for bitmap import.
  1277. // These are the fastest settings, but also the ones that gives the
  1278. // worst result (in most cases).
  1279. GIFImageDefaultColorReduction: TColorReduction = rmNetscape;
  1280. GIFImageDefaultColorReductionBits: integer = 8; // Range 3 - 8
  1281. GIFImageDefaultDitherMode: TDitherMode = dmNearest;
  1282. //: Default encoder compression method.
  1283. GIFImageDefaultCompression: TGIFCompression = gcLZW;
  1284. //: Default painter thread priority
  1285. GIFImageDefaultThreadPriority: TThreadPriority = tpNormal;
  1286. //: Default animation speed in % of normal speed (range 0 - 1000)
  1287. GIFImageDefaultAnimationSpeed: integer = 100;
  1288. // DoAutoDither is set to True in the initializaion section if the desktop DC
  1289. // supports 256 colors or less.
  1290. // It can be modified in your application to disable/enable Auto Dithering
  1291. DoAutoDither: boolean = False;
  1292. // Palette is set to True in the initialization section if the desktop DC
  1293. // supports 256 colors or less.
  1294. // You should NOT modify it.
  1295. PaletteDevice: boolean = False;
  1296. // Set GIFImageRenderOnLoad to True to render (convert to bitmap) the
  1297. // GIF frames as they are loaded instead of rendering them on-demand.
  1298. // This might increase resource consumption and will increase load time,
  1299. // but will cause animated GIFs to display more smoothly.
  1300. GIFImageRenderOnLoad: boolean = False;
  1301. // If GIFImageOptimizeOnStream is true, the GIF will be optimized
  1302. // before it is streamed to the DFM file.
  1303. // This will not affect TGIFImage.SaveToStream or SaveToFile.
  1304. GIFImageOptimizeOnStream: boolean = False;
  1305. ////////////////////////////////////////////////////////////////////////////////
  1306. //
  1307. // Design Time support
  1308. //
  1309. ////////////////////////////////////////////////////////////////////////////////
  1310. // Dummy component registration for design time support of GIFs in TImage
  1311. procedure Register;
  1312. ////////////////////////////////////////////////////////////////////////////////
  1313. //
  1314. // Error messages
  1315. //
  1316. ////////////////////////////////////////////////////////////////////////////////
  1317. {$ifndef VER9x}
  1318. resourcestring
  1319. {$else}
  1320. const
  1321. {$endif}
  1322. // GIF Error messages
  1323. sOutOfData = 'Premature end of data';
  1324. sTooManyColors = 'Color table overflow';
  1325. sBadColorIndex = 'Invalid color index';
  1326. sBadVersion = 'Unsupported GIF version';
  1327. sBadSignature = 'Invalid GIF signature';
  1328. sScreenBadColorSize = 'Invalid number of colors specified in Screen Descriptor';
  1329. sImageBadColorSize = 'Invalid number of colors specified in Image Descriptor';
  1330. sUnknownExtension = 'Unknown extension type';
  1331. sBadExtensionLabel = 'Invalid extension introducer';
  1332. sOutOfMemDIB = 'Failed to allocate memory for GIF DIB';
  1333. sDIBCreate = 'Failed to create DIB from Bitmap';
  1334. sDecodeTooFewBits = 'Decoder bit buffer under-run';
  1335. sDecodeCircular = 'Circular decoder table entry';
  1336. sBadTrailer = 'Invalid Image trailer';
  1337. sBadExtensionInstance = 'Internal error: Extension Instance does not match Extension Label';
  1338. sBadBlockSize = 'Unsupported Application Extension block size';
  1339. sBadBlock = 'Unknown GIF block type';
  1340. sUnsupportedClass = 'Object type not supported for operation';
  1341. sInvalidData = 'Invalid GIF data';
  1342. sBadHeight = 'Image height too small for contained frames';
  1343. sBadWidth = 'Image width too small for contained frames';
  1344. {$IFNDEF REGISTER_TGIFIMAGE}
  1345. sGIFToClipboard = 'Clipboard operations not supported for GIF objects';
  1346. {$ELSE}
  1347. sFailedPaste = 'Failed to store GIF on clipboard';
  1348. {$IFDEF VER9x}
  1349. sUnknownClipboardFormat= 'Unsupported clipboard format';
  1350. {$ENDIF}
  1351. {$ENDIF}
  1352. sScreenSizeExceeded = 'Image exceeds Logical Screen size';
  1353. sNoColorTable = 'No global or local color table defined';
  1354. sBadPixelCoordinates = 'Invalid pixel coordinates';
  1355. sUnsupportedBitmap = 'Unsupported bitmap format';
  1356. sInvalidPixelFormat = 'Unsupported PixelFormat';
  1357. sBadDimension = 'Invalid image dimensions';
  1358. sNoDIB = 'Image has no DIB';
  1359. sInvalidStream = 'Invalid stream operation';
  1360. sInvalidColor = 'Color not in color table';
  1361. sInvalidBitSize = 'Invalid Bits Per Pixel value';
  1362. sEmptyColorMap = 'Color table is empty';
  1363. sEmptyImage = 'Image is empty';
  1364. sInvalidBitmapList = 'Invalid bitmap list';
  1365. sInvalidReduction = 'Invalid reduction method';
  1366. {$IFDEF VER9x}
  1367. // From Delphi 3 consts.pas
  1368. SOutOfResources = 'Out of system resources';
  1369. SInvalidBitmap = 'Bitmap image is not valid';
  1370. SScanLine = 'Scan line index out of range';
  1371. {$ENDIF}
  1372. ////////////////////////////////////////////////////////////////////////////////
  1373. //
  1374. // Misc texts
  1375. //
  1376. ////////////////////////////////////////////////////////////////////////////////
  1377. // File filter name
  1378. sGIFImageFile = 'GIF Image';
  1379. // Progress messages
  1380. sProgressLoading = 'Loading...';
  1381. sProgressSaving = 'Saving...';
  1382. sProgressConverting = 'Converting...';
  1383. sProgressRendering = 'Rendering...';
  1384. sProgressCopying = 'Copying...';
  1385. sProgressOptimizing = 'Optimizing...';
  1386. ////////////////////////////////////////////////////////////////////////////////
  1387. ////////////////////////////////////////////////////////////////////////////////
  1388. //
  1389. // Implementation
  1390. //
  1391. ////////////////////////////////////////////////////////////////////////////////
  1392. ////////////////////////////////////////////////////////////////////////////////
  1393. implementation
  1394. { This makes me long for the C preprocessor... }
  1395. {$ifdef DEBUG}
  1396. {$ifdef DEBUG_COMPRESSPERFORMANCE}
  1397. {$define DEBUG_PERFORMANCE}
  1398. {$else}
  1399. {$ifdef DEBUG_DECOMPRESSPERFORMANCE}
  1400. {$define DEBUG_PERFORMANCE}
  1401. {$else}
  1402. {$ifdef DEBUG_DITHERPERFORMANCE}
  1403. {$define DEBUG_PERFORMANCE}
  1404. {$else}
  1405. {$ifdef DEBUG_DITHERPERFORMANCE}
  1406. {$define DEBUG_PERFORMANCE}
  1407. {$else}
  1408. {$ifdef DEBUG_DRAWPERFORMANCE}
  1409. {$define DEBUG_PERFORMANCE}
  1410. {$else}
  1411. {$ifdef DEBUG_RENDERPERFORMANCE}
  1412. {$define DEBUG_PERFORMANCE}
  1413. {$endif}
  1414. {$endif}
  1415. {$endif}
  1416. {$endif}
  1417. {$endif}
  1418. {$endif}
  1419. {$endif}
  1420. uses
  1421. {$ifdef DEBUG}
  1422. dialogs,
  1423. {$endif}
  1424. mmsystem, // timeGetTime()
  1425. messages,
  1426. Consts;
  1427. ////////////////////////////////////////////////////////////////////////////////
  1428. //
  1429. // Misc consts
  1430. //
  1431. ////////////////////////////////////////////////////////////////////////////////
  1432. const
  1433. { Extension/block label values }
  1434. bsPlainTextExtension = $01;
  1435. bsGraphicControlExtension = $F9;
  1436. bsCommentExtension = $FE;
  1437. bsApplicationExtension = $FF;
  1438. bsImageDescriptor = Ord(',');
  1439. bsExtensionIntroducer = Ord('!');
  1440. bsTrailer = ord(';');
  1441. // Thread messages - Used by TThread.Synchronize()
  1442. CM_DESTROYWINDOW = $8FFE; // Defined in classes.pas
  1443. CM_EXECPROC = $8FFF; // Defined in classes.pas
  1444. ////////////////////////////////////////////////////////////////////////////////
  1445. //
  1446. // Design Time support
  1447. //
  1448. ////////////////////////////////////////////////////////////////////////////////
  1449. //: Dummy component registration to add design-time support of GIFs to TImage.
  1450. // Since TGIFImage isn't a component there's nothing to register here, but
  1451. // since Register is only called at design time we can set the design time
  1452. // GIF paint options here (modify as you please):
  1453. procedure Register;
  1454. begin
  1455. // Don't loop animations at design-time. Animated GIFs will animate once and
  1456. // then stop thus not using CPU resources and distracting the developer.
  1457. Exclude(GIFImageDefaultDrawOptions, goLoop);
  1458. end;
  1459. ////////////////////////////////////////////////////////////////////////////////
  1460. //
  1461. // Utilities
  1462. //
  1463. ////////////////////////////////////////////////////////////////////////////////
  1464. //: Creates a 216 color uniform non-dithering Netscape palette.
  1465. function WebPalette: HPalette;
  1466. type
  1467. TLogWebPalette = packed record
  1468. palVersion : word;
  1469. palNumEntries : word;
  1470. PalEntries : array[0..5,0..5,0..5] of TPaletteEntry;
  1471. end;
  1472. var
  1473. r, g, b : byte;
  1474. LogWebPalette : TLogWebPalette;
  1475. LogPalette : TLogpalette absolute LogWebPalette; // Stupid typecast
  1476. begin
  1477. with LogWebPalette do
  1478. begin
  1479. palVersion:= $0300;
  1480. palNumEntries:= 216;
  1481. for r:=0 to 5 do
  1482. for g:=0 to 5 do
  1483. for b:=0 to 5 do
  1484. begin
  1485. with PalEntries[r,g,b] do
  1486. begin
  1487. peRed := 51 * r;
  1488. peGreen := 51 * g;
  1489. peBlue := 51 * b;
  1490. peFlags := 0;
  1491. end;
  1492. end;
  1493. end;
  1494. Result := CreatePalette(Logpalette);
  1495. end;
  1496. (*
  1497. ** GDI Error handling
  1498. ** Adapted from graphics.pas
  1499. *)
  1500. {$IFOPT R+}
  1501. {$DEFINE R_PLUS}
  1502. {$RANGECHECKS OFF}
  1503. {$ENDIF}
  1504. {$ifdef D3_BCB3}
  1505. function GDICheck(Value: Integer): Integer;
  1506. {$else}
  1507. function GDICheck(Value: Cardinal): Cardinal;
  1508. {$endif}
  1509. var
  1510. ErrorCode : integer;
  1511. Buf : array [byte] of char;
  1512. function ReturnAddr: Pointer;
  1513. // From classes.pas
  1514. asm
  1515. MOV EAX,[EBP+4] // sysutils.pas says [EBP-4], but this works !
  1516. end;
  1517. begin
  1518. if (Value = 0) then
  1519. begin
  1520. ErrorCode := GetLastError;
  1521. if (ErrorCode <> 0) and (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil,
  1522. ErrorCode, LOCALE_USER_DEFAULT, Buf, sizeof(Buf), nil) <> 0) then
  1523. raise EOutOfResources.Create(Buf) at ReturnAddr
  1524. else
  1525. raise EOutOfResources.Create(SOutOfResources) at ReturnAddr;
  1526. end;
  1527. Result := Value;
  1528. end;
  1529. {$IFDEF R_PLUS}
  1530. {$RANGECHECKS ON}
  1531. {$UNDEF R_PLUS}
  1532. {$ENDIF}
  1533. (*
  1534. ** Raise error condition
  1535. *)
  1536. procedure Error(msg: string);
  1537. function ReturnAddr: Pointer;
  1538. // From classes.pas
  1539. asm
  1540. MOV EAX,[EBP+4] // sysutils.pas says [EBP-4] !
  1541. end;
  1542. begin
  1543. raise GIFException.Create(msg) at ReturnAddr;
  1544. end;
  1545. (*
  1546. ** Return number bytes required to
  1547. ** hold a given number of bits.
  1548. *)
  1549. function ByteAlignBit(Bits: Cardinal): Cardinal;
  1550. begin
  1551. Result := (Bits+7) SHR 3;
  1552. end;
  1553. // Rounded up to nearest 2
  1554. function WordAlignBit(Bits: Cardinal): Cardinal;
  1555. begin
  1556. Result := ((Bits+15) SHR 4) SHL 1;
  1557. end;
  1558. // Rounded up to nearest 4
  1559. function DWordAlignBit(Bits: Cardinal): Cardinal;
  1560. begin
  1561. Result := ((Bits+31) SHR 5) SHL 2;
  1562. end;
  1563. // Round to arbitrary number of bits
  1564. function AlignBit(Bits, BitsPerPixel, Alignment: Cardinal): Cardinal;
  1565. begin
  1566. Dec(Alignment);
  1567. Result := ((Bits * BitsPerPixel) + Alignment) and not Alignment;
  1568. Result := Result SHR 3;
  1569. end;
  1570. (*
  1571. ** Compute Bits per Pixel from Number of Colors
  1572. ** (Return the ceiling log of n)
  1573. *)
  1574. function Colors2bpp(Colors: integer): integer;
  1575. var
  1576. MaxColor : integer;
  1577. begin
  1578. (*
  1579. ** This might be faster computed by multiple if then else statements
  1580. *)
  1581. if (Colors = 0) then
  1582. Result := 0
  1583. else
  1584. begin
  1585. Result := 1;
  1586. MaxColor := 2;
  1587. while (Colors > MaxColor) do
  1588. begin
  1589. inc(Result);
  1590. MaxColor := MaxColor SHL 1;
  1591. end;
  1592. end;
  1593. end;
  1594. (*
  1595. ** Write an ordinal byte value to a stream
  1596. *)
  1597. procedure WriteByte(Stream: TStream; b: BYTE);
  1598. begin
  1599. Stream.Write(b, 1);
  1600. end;
  1601. (*
  1602. ** Read an ordinal byte value from a stream
  1603. *)
  1604. function ReadByte(Stream: TStream): BYTE;
  1605. begin
  1606. Stream.Read(Result, 1);
  1607. end;
  1608. (*
  1609. ** Read data from stream and raise exception of EOF
  1610. *)
  1611. procedure ReadCheck(Stream: TStream; var Buffer; Size: LongInt);
  1612. var
  1613. ReadSize : integer;
  1614. begin
  1615. ReadSize := Stream.Read(Buffer, Size);
  1616. if (ReadSize <> Size) then
  1617. Error(sOutOfData);
  1618. end;
  1619. (*
  1620. ** Write a string list to a stream as multiple blocks
  1621. ** of max 255 characters in each.
  1622. *)
  1623. procedure WriteStrings(Stream: TStream; Text: TStrings);
  1624. var
  1625. i : integer;
  1626. b : BYTE;
  1627. size : integer;
  1628. s : string;
  1629. begin
  1630. for i := 0 to Text.Count-1 do
  1631. begin
  1632. s := Text[i];
  1633. size := length(s);
  1634. if (size > 255) then
  1635. b := 255
  1636. else
  1637. b := size;
  1638. while (size > 0) do
  1639. begin
  1640. dec(size, b);
  1641. WriteByte(Stream, b);
  1642. Stream.Write(PChar(s)^, b);
  1643. delete(s, 1, b);
  1644. if (b > size) then
  1645. b := size;
  1646. end;
  1647. end;
  1648. // Terminating zero (length = 0)
  1649. WriteByte(Stream, 0);
  1650. end;
  1651. (*
  1652. ** Read a string list from a stream as multiple blocks
  1653. ** of max 255 characters in each.
  1654. *)
  1655. { TODO -oanme -cImprovement : Replace ReadStrings with TGIFReader. }
  1656. procedure ReadStrings(Stream: TStream; Text: TStrings);
  1657. var
  1658. size : BYTE;
  1659. buf : array[0..255] of char;
  1660. begin
  1661. Text.Clear;
  1662. if (Stream.Read(size, 1) <> 1) then
  1663. exit;
  1664. while (size > 0) do
  1665. begin
  1666. ReadCheck(Stream, buf, size);
  1667. buf[size] := #0;
  1668. Text.Add(Buf);
  1669. if (Stream.Read(size, 1) <> 1) then
  1670. exit;
  1671. end;
  1672. end;
  1673. ////////////////////////////////////////////////////////////////////////////////
  1674. //
  1675. // Delphi 2.x / C++ Builder 1.x support
  1676. //
  1677. ////////////////////////////////////////////////////////////////////////////////
  1678. {$IFDEF VER9x}
  1679. var
  1680. // From Delphi 3 graphics.pas
  1681. SystemPalette16: HPalette; // 16 color palette that maps to the system palette
  1682. type
  1683. TPixelFormats = set of TPixelFormat;
  1684. const
  1685. // Only pf1bit, pf4bit and pf8bit is supported since they are the only ones
  1686. // with palettes
  1687. SupportedPixelformats: TPixelFormats = [pf1bit, pf4bit, pf8bit];
  1688. {$ENDIF}
  1689. // --------------------------
  1690. // InitializeBitmapInfoHeader
  1691. // --------------------------
  1692. // Fills a TBitmapInfoHeader with the values of a bitmap when converted to a
  1693. // DIB of a specified PixelFormat.
  1694. //
  1695. // Parameters:
  1696. // Bitmap The handle of the source bitmap.
  1697. // Info The TBitmapInfoHeader buffer that will receive the values.
  1698. // PixelFormat The pixel format of the destination DIB.
  1699. //
  1700. {$IFDEF BAD_STACK_ALIGNMENT}
  1701. // Disable optimization to circumvent optimizer bug...
  1702. {$IFOPT O+}
  1703. {$DEFINE O_PLUS}
  1704. {$O-}
  1705. {$ENDIF}
  1706. {$ENDIF}
  1707. procedure InitializeBitmapInfoHeader(Bitmap: HBITMAP; var Info: TBitmapInfoHeader;
  1708. PixelFormat: TPixelFormat);
  1709. // From graphics.pas, "optimized" for our use
  1710. var
  1711. DIB : TDIBSection;
  1712. Bytes : Integer;
  1713. begin
  1714. DIB.dsbmih.biSize := 0;
  1715. Bytes := GetObject(Bitmap, SizeOf(DIB), @DIB);
  1716. if (Bytes = 0) then
  1717. Error(sInvalidBitmap);
  1718. if (Bytes >= (sizeof(DIB.dsbm) + sizeof(DIB.dsbmih))) and
  1719. (DIB.dsbmih.biSize >= sizeof(DIB.dsbmih)) then
  1720. Info := DIB.dsbmih
  1721. else
  1722. begin
  1723. FillChar(Info, sizeof(Info), 0);
  1724. with Info, DIB.dsbm do
  1725. begin
  1726. biSize := SizeOf(Info);
  1727. biWidth := bmWidth;
  1728. biHeight := bmHeight;
  1729. end;
  1730. end;
  1731. case PixelFormat of
  1732. pf1bit: Info.biBitCount := 1;
  1733. pf4bit: Info.biBitCount := 4;
  1734. pf8bit: Info.biBitCount := 8;
  1735. pf24bit: Info.biBitCount := 24;
  1736. else
  1737. Error(sInvalidPixelFormat);
  1738. // Info.biBitCount := DIB.dsbm.bmBitsPixel * DIB.dsbm.bmPlanes;
  1739. end;
  1740. Info.biPlanes := 1;
  1741. Info.biCompression := BI_RGB; // Always return data in RGB format
  1742. Info.biSizeImage := AlignBit(Info.biWidth, Info.biBitCount, 32) * Cardinal(abs(Info.biHeight));
  1743. end;
  1744. {$IFDEF O_PLUS}
  1745. {$O+}
  1746. {$UNDEF O_PLUS}
  1747. {$ENDIF}
  1748. // -------------------
  1749. // InternalGetDIBSizes
  1750. // -------------------
  1751. // Calculates the buffer sizes nescessary for convertion of a bitmap to a DIB
  1752. // of a specified PixelFormat.
  1753. // See the GetDIBSizes API function for more info.
  1754. //
  1755. // Parameters:
  1756. // Bitmap The handle of the source bitmap.
  1757. // InfoHeaderSize
  1758. // The returned size of a buffer that will receive the DIB's
  1759. // TBitmapInfo structure.
  1760. // ImageSize The returned size of a buffer that will receive the DIB's
  1761. // pixel data.
  1762. // PixelFormat The pixel format of the destination DIB.
  1763. //
  1764. procedure InternalGetDIBSizes(Bitmap: HBITMAP; var InfoHeaderSize: Integer;
  1765. var ImageSize: longInt; PixelFormat: TPixelFormat);
  1766. // From graphics.pas, "optimized" for our use
  1767. var
  1768. Info : TBitmapInfoHeader;
  1769. begin
  1770. InitializeBitmapInfoHeader(Bitmap, Info, PixelFormat);
  1771. // Check for palette device format
  1772. if (Info.biBitCount > 8) then
  1773. begin
  1774. // Header but no palette
  1775. InfoHeaderSize := SizeOf(TBitmapInfoHeader);
  1776. if ((Info.biCompression and BI_BITFIELDS) <> 0) then
  1777. Inc(InfoHeaderSize, 12);
  1778. end else
  1779. // Header and palette
  1780. InfoHeaderSize := SizeOf(TBitmapInfoHeader) + SizeOf(TRGBQuad) * (1 shl Info.biBitCount);
  1781. ImageSize := Info.biSizeImage;
  1782. end;
  1783. // --------------
  1784. // InternalGetDIB
  1785. // --------------
  1786. // Converts a bitmap to a DIB of a specified PixelFormat.
  1787. //
  1788. // Parameters:
  1789. // Bitmap The handle of the source bitmap.
  1790. // Pal The handle of the source palette.
  1791. // BitmapInfo The buffer that will receive the DIB's TBitmapInfo structure.
  1792. // A buffer of sufficient size must have been allocated prior to
  1793. // calling this function.
  1794. // Bits The buffer that will receive the DIB's pixel data.
  1795. // A buffer of sufficient size must have been allocated prior to
  1796. // calling this function.
  1797. // PixelFormat The pixel format of the destination DIB.
  1798. //
  1799. // Returns:
  1800. // True on success, False on failure.
  1801. //
  1802. // Note: The InternalGetDIBSizes function can be used to calculate the
  1803. // nescessary sizes of the BitmapInfo and Bits buffers.
  1804. //
  1805. function InternalGetDIB(Bitmap: HBITMAP; Palette: HPALETTE;
  1806. var BitmapInfo; var Bits; PixelFormat: TPixelFormat): Boolean;
  1807. // From graphics.pas, "optimized" for our use
  1808. var
  1809. OldPal : HPALETTE;
  1810. DC : HDC;
  1811. begin
  1812. InitializeBitmapInfoHeader(Bitmap, TBitmapInfoHeader(BitmapInfo), PixelFormat);
  1813. OldPal := 0;
  1814. DC := CreateCompatibleDC(0);
  1815. try
  1816. if (Palette <> 0) then
  1817. begin
  1818. OldPal := SelectPalette(DC, Palette, False);
  1819. RealizePalette(DC);
  1820. end;
  1821. Result := (GetDIBits(DC, Bitmap, 0, abs(TBitmapInfoHeader(BitmapInfo).biHeight),
  1822. @Bits, TBitmapInfo(BitmapInfo), DIB_RGB_COLORS) <> 0);
  1823. finally
  1824. if (OldPal <> 0) then
  1825. SelectPalette(DC, OldPal, False);
  1826. DeleteDC(DC);
  1827. end;
  1828. end;
  1829. // ----------
  1830. // DIBFromBit
  1831. // ----------
  1832. // Converts a bitmap to a DIB of a specified PixelFormat.
  1833. // The DIB is returned in a TMemoryStream ready for streaming to a BMP file.
  1834. //
  1835. // Note: As opposed to D2's DIBFromBit function, the returned stream also
  1836. // contains a TBitmapFileHeader at offset 0.
  1837. //
  1838. // Parameters:
  1839. // Stream The TMemoryStream used to store the bitmap data.
  1840. // The stream must be allocated and freed by the caller prior to
  1841. // calling this function.
  1842. // Src The handle of the source bitmap.
  1843. // Pal The handle of the source palette.
  1844. // PixelFormat The pixel format of the destination DIB.
  1845. // DIBHeader A pointer to the DIB's TBitmapInfo (or TBitmapInfoHeader)
  1846. // structure in the memory stream.
  1847. // The size of the structure can either be deduced from the
  1848. // pixel format (i.e. number of colors) or calculated by
  1849. // subtracting the DIBHeader pointer from the DIBBits pointer.
  1850. // DIBBits A pointer to the DIB's pixel data in the memory stream.
  1851. //
  1852. procedure DIBFromBit(Stream: TMemoryStream; Src: HBITMAP;
  1853. Pal: HPALETTE; PixelFormat: TPixelFormat; var DIBHeader, DIBBits: Pointer);
  1854. // (From D2 graphics.pas, "optimized" for our use)
  1855. var
  1856. HeaderSize : integer;
  1857. FileSize : longInt;
  1858. ImageSize : longInt;
  1859. BitmapFileHeader : PBitmapFileHeader;
  1860. begin
  1861. if (Src = 0) then
  1862. Error(sInvalidBitmap);
  1863. // Get header- and pixel data size for new pixel format
  1864. InternalGetDIBSizes(Src, HeaderSize, ImageSize, PixelFormat);
  1865. // Make room in stream for a TBitmapInfo and pixel data
  1866. FileSize := sizeof(TBitmapFileHeader) + HeaderSize + ImageSize;
  1867. Stream.SetSize(FileSize);
  1868. // Get pointer to TBitmapFileHeader
  1869. BitmapFileHeader := Stream.Memory;
  1870. // Get pointer to TBitmapInfo
  1871. DIBHeader := Pointer(Longint(BitmapFileHeader) + sizeof(TBitmapFileHeader));
  1872. // Get pointer to pixel data
  1873. DIBBits := Pointer(Longint(DIBHeader) + HeaderSize);
  1874. // Initialize file header
  1875. FillChar(BitmapFileHeader^, sizeof(TBitmapFileHeader), 0);
  1876. with BitmapFileHeader^ do
  1877. begin
  1878. bfType := $4D42; // 'BM' = Windows BMP signature
  1879. bfSize := FileSize; // File size (not needed)
  1880. bfOffBits := sizeof(TBitmapFileHeader) + HeaderSize; // Offset of pixel data
  1881. end;
  1882. // Get pixel data in new pixel format
  1883. InternalGetDIB(Src, Pal, DIBHeader^, DIBBits^, PixelFormat);
  1884. end;
  1885. // --------------
  1886. // GetPixelFormat
  1887. // --------------
  1888. // Returns the current pixel format of a bitmap.
  1889. //
  1890. // Replacement for delphi 3 TBitmap.PixelFormat getter.
  1891. //
  1892. // Parameters:
  1893. // Bitmap The bitmap which pixel format is returned.
  1894. //
  1895. // Returns:
  1896. // The PixelFormat of the bitmap
  1897. //
  1898. function GetPixelFormat(Bitmap: TBitmap): TPixelFormat;
  1899. {$IFDEF VER9x}
  1900. // From graphics.pas, "optimized" for our use
  1901. var
  1902. DIBSection : TDIBSection;
  1903. Bytes : Integer;
  1904. Handle : HBitmap;
  1905. begin
  1906. Result := pfCustom; // This value is never returned
  1907. // BAD_STACK_ALIGNMENT
  1908. // Note: To work around an optimizer bug, we do not use Bitmap.Handle
  1909. // directly. Instead we store the value and use it indirectly. Unless we do
  1910. // this, the register containing Bitmap.Handle will be overwritten!
  1911. Handle := Bitmap.Handle;
  1912. if (Handle <> 0) then
  1913. begin
  1914. Bytes := GetObject(Handle, SizeOf(DIBSection), @DIBSection);
  1915. if (Bytes = 0) then
  1916. Error(sInvalidBitmap);
  1917. with (DIBSection) do
  1918. begin
  1919. // Check for NT bitmap
  1920. if (Bytes < (SizeOf(dsbm) + SizeOf(dsbmih))) or (dsbmih.biSize < SizeOf(dsbmih)) then
  1921. DIBSection.dsBmih.biBitCount := dsbm.bmBitsPixel * dsbm.bmPlanes;
  1922. case (dsBmih.biBitCount) of
  1923. 0: Result := pfDevice;
  1924. 1: Result := pf1bit;
  1925. 4: Result := pf4bit;
  1926. 8: Result := pf8bit;
  1927. 16: case (dsBmih.biCompression) of
  1928. BI_RGB:
  1929. Result := pf15Bit;
  1930. BI_BITFIELDS:
  1931. if (dsBitFields[1] = $07E0) then
  1932. Result := pf16Bit;
  1933. end;
  1934. 24: Result := pf24Bit;
  1935. 32: if (dsBmih.biCompression = BI_RGB) then
  1936. Result := pf32Bit;
  1937. else
  1938. Error(sUnsupportedBitmap);
  1939. end;
  1940. end;
  1941. end else
  1942. // Result := pfDevice;
  1943. Error(sUnsupportedBitmap);
  1944. end;
  1945. {$ELSE}
  1946. begin
  1947. Result := Bitmap.PixelFormat;
  1948. end;
  1949. {$ENDIF}
  1950. // --------------
  1951. // SetPixelFormat
  1952. // --------------
  1953. // Changes the pixel format of a TBitmap.
  1954. //
  1955. // Replacement for delphi 3 TBitmap.PixelFormat setter.
  1956. // The returned TBitmap will always be a DIB.
  1957. //
  1958. // Note: Under Delphi 3.x this function will leak a palette handle each time it
  1959. // converts a TBitmap to pf8bit format!
  1960. // If possible, use SafeSetPixelFormat instead to avoid this.
  1961. //
  1962. // Parameters:
  1963. // Bitmap The bitmap to modify.
  1964. // PixelFormat The pixel format to convert to.
  1965. //
  1966. procedure SetPixelFormat(Bitmap: TBitmap; PixelFormat: TPixelFormat);
  1967. {$IFDEF VER9x}
  1968. var
  1969. Stream : TMemoryStream;
  1970. Header ,
  1971. Bits : Pointer;
  1972. begin
  1973. // Can't change anything without a handle
  1974. if (Bitmap.Handle = 0) then
  1975. Error(sInvalidBitmap);
  1976. // Only convert to supported formats
  1977. if not(PixelFormat in SupportedPixelformats) then
  1978. Error(sInvalidPixelFormat);
  1979. // No need to convert to same format
  1980. if (GetPixelFormat(Bitmap) = PixelFormat) then
  1981. exit;
  1982. Stream := TMemoryStream.Create;
  1983. try
  1984. // Convert to DIB file in memory stream
  1985. DIBFromBit(Stream, Bitmap.Handle, Bitmap.Palette, PixelFormat, Header, Bits);
  1986. // Load DIB from stream
  1987. Stream.Position := 0;
  1988. Bitmap.LoadFromStream(Stream);
  1989. finally
  1990. Stream.Free;
  1991. end;
  1992. end;
  1993. {$ELSE}
  1994. begin
  1995. Bitmap.PixelFormat := PixelFormat;
  1996. end;
  1997. {$ENDIF}
  1998. {$IFDEF VER100}
  1999. var
  2000. pf8BitBitmap: TBitmap = nil;
  2001. {$ENDIF}
  2002. // ------------------
  2003. // SafeSetPixelFormat
  2004. // ------------------
  2005. // Changes the pixel format of a TBitmap but doesn't preserve the contents.
  2006. //
  2007. // Replacement for Delphi 3 TBitmap.PixelFormat setter.
  2008. // The returned TBitmap will always be an empty DIB of the same size as the
  2009. // original bitmap.
  2010. //
  2011. // This function is used to avoid the palette handle leak that Delphi 3's
  2012. // SetPixelFormat and TBitmap.PixelFormat suffers from.
  2013. //
  2014. // Parameters:
  2015. // Bitmap The bitmap to modify.
  2016. // PixelFormat The pixel format to convert to.
  2017. //
  2018. procedure SafeSetPixelFormat(Bitmap: TBitmap; PixelFormat: TPixelFormat);
  2019. {$IFDEF VER9x}
  2020. begin
  2021. SetPixelFormat(Bitmap, PixelFormat);
  2022. end;
  2023. {$ELSE}
  2024. {$IFNDEF VER100}
  2025. var
  2026. Palette : hPalette;
  2027. begin
  2028. Bitmap.PixelFormat := PixelFormat;
  2029. // Work around a bug in TBitmap:
  2030. // When converting to pf8bit format, the palette assigned to TBitmap.Palette
  2031. // will be a half tone palette (which only contains the 20 system colors).
  2032. // Unfortunately this is not the palette used to render the bitmap and it
  2033. // is also not the palette saved with the bitmap.
  2034. if (PixelFormat = pf8bit) then
  2035. begin
  2036. // Disassociate the wrong palette from the bitmap (without affecting
  2037. // the DIB color table)
  2038. Palette := Bitmap.ReleasePalette;
  2039. if (Palette <> 0) then
  2040. DeleteObject(Palette);
  2041. // Recreate the palette from the DIB color table
  2042. Bitmap.Palette;
  2043. end;
  2044. end;
  2045. {$ELSE}
  2046. var
  2047. Width ,
  2048. Height : integer;
  2049. begin
  2050. if (PixelFormat = pf8bit) then
  2051. begin
  2052. // Partial solution to "TBitmap.PixelFormat := pf8bit" leak
  2053. // by Greg Chapman <glc@well.com>
  2054. if (pf8BitBitmap = nil) then
  2055. begin
  2056. // Create a "template" bitmap
  2057. // The bitmap is deleted in the finalization section of the unit.
  2058. pf8BitBitmap:= TBitmap.Create;
  2059. // Convert template to pf8bit format
  2060. // This will leak 1 palette handle, but only once
  2061. pf8BitBitmap.PixelFormat:= pf8Bit;
  2062. end;
  2063. // Store the size of the original bitmap
  2064. Width := Bitmap.Width;
  2065. Height := Bitmap.Height;
  2066. // Convert to pf8bit format by copying template
  2067. Bitmap.Assign(pf8BitBitmap);
  2068. // Restore the original size
  2069. Bitmap.Width := Width;
  2070. Bitmap.Height := Height;
  2071. end else
  2072. // This is safe since only pf8bit leaks
  2073. Bitmap.PixelFormat := PixelFormat;
  2074. end;
  2075. {$ENDIF}
  2076. {$ENDIF}
  2077. {$IFDEF VER9x}
  2078. // -----------
  2079. // CopyPalette
  2080. // -----------
  2081. // Copies a HPALETTE.
  2082. //
  2083. // Copied from D3 graphics.pas.
  2084. // This is declared private in some old versions of Delphi 2 so we have to
  2085. // implement it here to support those old versions.
  2086. //
  2087. // Parameters:
  2088. // Palette The palette to copy.
  2089. //
  2090. // Returns:
  2091. // The handle to a new palette.
  2092. //
  2093. function CopyPalette(Palette: HPALETTE): HPALETTE;
  2094. var
  2095. PaletteSize: Integer;
  2096. LogPal: TMaxLogPalette;
  2097. begin
  2098. Result := 0;
  2099. if Palette = 0 then Exit;
  2100. PaletteSize := 0;
  2101. if GetObject(Palette, SizeOf(PaletteSize), @PaletteSize) = 0 then Exit;
  2102. if PaletteSize = 0 then Exit;
  2103. with LogPal do
  2104. begin
  2105. palVersion := $0300;
  2106. palNumEntries := PaletteSize;
  2107. GetPaletteEntries(Palette, 0, PaletteSize, palPalEntry);
  2108. end;
  2109. Result := CreatePalette(PLogPalette(@LogPal)^);
  2110. end;
  2111. // TThreadList implementation from Delphi 3 classes.pas
  2112. constructor TThreadList.Create;
  2113. begin
  2114. inherited Create;
  2115. InitializeCriticalSection(FLock);
  2116. FList := TList.Create;
  2117. end;
  2118. destructor TThreadList.Destroy;
  2119. begin
  2120. LockList; // Make sure nobody else is inside the list.
  2121. try
  2122. FList.Free;
  2123. inherited Destroy;
  2124. finally
  2125. UnlockList;
  2126. DeleteCriticalSection(FLock);
  2127. end;
  2128. end;
  2129. procedure TThreadList.Add(Item: Pointer);
  2130. begin
  2131. LockList;
  2132. try
  2133. if FList.IndexOf(Item) = -1 then
  2134. FList.Add(Item);
  2135. finally
  2136. UnlockList;
  2137. end;
  2138. end;
  2139. procedure TThreadList.Clear;
  2140. begin
  2141. LockList;
  2142. try
  2143. FList.Clear;
  2144. finally
  2145. UnlockList;
  2146. end;
  2147. end;
  2148. function TThreadList.LockList: TList;
  2149. begin
  2150. EnterCriticalSection(FLock);
  2151. Result := FList;
  2152. end;
  2153. procedure TThreadList.Remove(Item: Pointer);
  2154. begin
  2155. LockList;
  2156. try
  2157. FList.Remove(Item);
  2158. finally
  2159. UnlockList;
  2160. end;
  2161. end;
  2162. procedure TThreadList.UnlockList;
  2163. begin
  2164. LeaveCriticalSection(FLock);
  2165. end;
  2166. // End of TThreadList implementation
  2167. // From Delphi 3 sysutils.pas
  2168. { CompareMem performs a binary compare of Length bytes of memory referenced
  2169. by P1 to that of P2. CompareMem returns True if the memory referenced by
  2170. P1 is identical to that of P2. }
  2171. function CompareMem(P1, P2: Pointer; Length: Integer): Boolean; assembler;
  2172. asm
  2173. PUSH ESI
  2174. PUSH EDI
  2175. MOV ESI,P1
  2176. MOV EDI,P2
  2177. MOV EDX,ECX
  2178. XOR EAX,EAX
  2179. AND EDX,3
  2180. SHR ECX,1
  2181. SHR ECX,1
  2182. REPE CMPSD
  2183. JNE @@2
  2184. MOV ECX,EDX
  2185. REPE CMPSB
  2186. JNE @@2
  2187. @@1: INC EAX
  2188. @@2: POP EDI
  2189. POP ESI
  2190. end;
  2191. // Dummy ASSERT procedure since ASSERT does not exist in Delphi 2.x
  2192. procedure ASSERT(Condition: boolean; Message: string);
  2193. begin
  2194. end;
  2195. {$ENDIF} // Delphi 2.x stuff
  2196. ////////////////////////////////////////////////////////////////////////////////
  2197. //
  2198. // TDIB Classes
  2199. //
  2200. // These classes gives read and write access to TBitmap's pixel data
  2201. // independently of the Delphi version used.
  2202. //
  2203. ////////////////////////////////////////////////////////////////////////////////
  2204. type
  2205. TDIB = class(TObject)
  2206. private
  2207. FBitmap : TBitmap;
  2208. FPixelFormat : TPixelFormat;
  2209. protected
  2210. function GetScanline(Row: integer): pointer; virtual; abstract;
  2211. constructor Create(ABitmap: TBitmap; APixelFormat: TPixelFormat);
  2212. public
  2213. property Scanline[Row: integer]: pointer read GetScanline;
  2214. property Bitmap: TBitmap read FBitmap;
  2215. property PixelFormat: TPixelFormat read FPixelFormat;
  2216. end;
  2217. TDIBReader = class(TDIB)
  2218. private
  2219. {$ifdef VER9x}
  2220. FDIB : TDIBSection;
  2221. FDC : HDC;
  2222. FScanLine : pointer;
  2223. FLastRow : integer;
  2224. FInfo : PBitmapInfo;
  2225. FBytes : integer;
  2226. {$endif}
  2227. protected
  2228. function GetScanline(Row: integer): pointer; override;
  2229. public
  2230. constructor Create(ABitmap: TBitmap; APixelFormat: TPixelFormat);
  2231. destructor Destroy; override;
  2232. end;
  2233. TDIBWriter = class(TDIB)
  2234. private
  2235. {$ifdef PIXELFORMAT_TOO_SLOW}
  2236. FDIBInfo : PBitmapInfo;
  2237. FDIBBits : pointer;
  2238. FDIBInfoSize : integer;
  2239. FDIBBitsSize : longInt;
  2240. {$ifndef CREATEDIBSECTION_SLOW}
  2241. FDIB : HBITMAP;
  2242. {$endif}
  2243. {$endif}
  2244. FPalette : HPalette;
  2245. FHeight : integer;
  2246. FWidth : integer;
  2247. protected
  2248. procedure CreateDIB;
  2249. procedure FreeDIB;
  2250. procedure NeedDIB;
  2251. function GetScanline(Row: integer): pointer; override;
  2252. public
  2253. constructor Create(ABitmap: TBitmap; APixelFormat: TPixelFormat;
  2254. AWidth, AHeight: integer; APalette: HPalette);
  2255. destructor Destroy; override;
  2256. procedure UpdateBitmap;
  2257. property Width: integer read FWidth;
  2258. property Height: integer read FHeight;
  2259. property Palette: HPalette read FPalette;
  2260. end;
  2261. ////////////////////////////////////////////////////////////////////////////////
  2262. constructor TDIB.Create(ABitmap: TBitmap; APixelFormat: TPixelFormat);
  2263. begin
  2264. inherited Create;
  2265. FBitmap := ABitmap;
  2266. FPixelFormat := APixelFormat;
  2267. end;
  2268. ////////////////////////////////////////////////////////////////////////////////
  2269. constructor TDIBReader.Create(ABitmap: TBitmap; APixelFormat: TPixelFormat);
  2270. {$ifdef VER9x}
  2271. var
  2272. InfoHeaderSize : integer;
  2273. ImageSize : longInt;
  2274. {$endif}
  2275. begin
  2276. inherited Create(ABitmap, APixelFormat);
  2277. {$ifndef VER9x}
  2278. SetPixelFormat(FBitmap, FPixelFormat);
  2279. {$else}
  2280. FDC := CreateCompatibleDC(0);
  2281. SelectPalette(FDC, FBitmap.Palette, False);
  2282. // Allocate DIB info structure
  2283. InternalGetDIBSizes(ABitmap.Handle, InfoHeaderSize, ImageSize, APixelFormat);
  2284. GetMem(FInfo, InfoHeaderSize);
  2285. // Get DIB info
  2286. InitializeBitmapInfoHeader(ABitmap.Handle, FInfo^.bmiHeader, APixelFormat);
  2287. // Allocate scan line buffer
  2288. GetMem(FScanLine, ImageSize DIV abs(FInfo^.bmiHeader.biHeight));
  2289. FLastRow := -1;
  2290. {$endif}
  2291. end;
  2292. destructor TDIBReader.Destroy;
  2293. begin
  2294. {$ifdef VER9x}
  2295. DeleteDC(FDC);
  2296. FreeMem(FScanLine);
  2297. FreeMem(FInfo);
  2298. {$endif}
  2299. inherited Destroy;
  2300. end;
  2301. function TDIBReader.GetScanline(Row: integer): pointer;
  2302. begin
  2303. {$ifdef VER9x}
  2304. if (Row < 0) or (Row >= FBitmap.Height) then
  2305. raise EInvalidGraphicOperation.Create(SScanLine);
  2306. GDIFlush;
  2307. Result := FScanLine;
  2308. if (Row = FLastRow) then
  2309. exit;
  2310. FLastRow := Row;
  2311. if (FInfo^.bmiHeader.biHeight > 0) then // bottom-up DIB
  2312. Row := FInfo^.bmiHeader.biHeight - Row - 1;
  2313. GetDIBits(FDC, FBitmap.Handle, Row, 1, FScanLine, FInfo^, DIB_RGB_COLORS);
  2314. {$else}
  2315. Result := FBitmap.ScanLine[Row];
  2316. {$endif}
  2317. end;
  2318. ////////////////////////////////////////////////////////////////////////////////
  2319. constructor TDIBWriter.Create(ABitmap: TBitmap; APixelFormat: TPixelFormat;
  2320. AWidth, AHeight: integer; APalette: HPalette);
  2321. begin
  2322. inherited Create(ABitmap, APixelFormat);
  2323. // DIB writer only supports 8 or 24 bit bitmaps
  2324. if not(APixelFormat in [pf8bit, pf24bit]) then
  2325. Error(sInvalidPixelFormat);
  2326. if (AWidth = 0) or (AHeight = 0) then
  2327. Error(sBadDimension);
  2328. FHeight := AHeight;
  2329. FWidth := AWidth;
  2330. {$ifndef PIXELFORMAT_TOO_SLOW}
  2331. FBitmap.Palette := 0;
  2332. FBitmap.Height := FHeight;
  2333. FBitmap.Width := FWidth;
  2334. SafeSetPixelFormat(FBitmap, FPixelFormat);
  2335. FPalette := CopyPalette(APalette);
  2336. FBitmap.Palette := FPalette;
  2337. {$else}
  2338. FPalette := APalette;
  2339. FDIBInfo := nil;
  2340. FDIBBits := nil;
  2341. {$ifndef CREATEDIBSECTION_SLOW}
  2342. FDIB := 0;
  2343. {$endif}
  2344. {$endif}
  2345. end;
  2346. destructor TDIBWriter.Destroy;
  2347. begin
  2348. UpdateBitmap;
  2349. FreeDIB;
  2350. inherited Destroy;
  2351. end;
  2352. function TDIBWriter.GetScanline(Row: integer): pointer;
  2353. begin
  2354. {$ifdef PIXELFORMAT_TOO_SLOW}
  2355. NeedDIB;
  2356. if (FDIBBits = nil) then
  2357. Error(sNoDIB);
  2358. with FDIBInfo^.bmiHeader do
  2359. begin
  2360. if (Row < 0) or (Row >= Height) then
  2361. raise EInvalidGraphicOperation.Create(SScanLine);
  2362. GDIFlush;
  2363. if biHeight > 0 then // bottom-up DIB
  2364. Row := biHeight - Row - 1;
  2365. Result := PChar(Cardinal(FDIBBits) + Cardinal(Row) * AlignBit(biWidth, biBitCount, 32));
  2366. end;
  2367. {$else}
  2368. Result := FBitmap.ScanLine[Row];
  2369. {$endif}
  2370. end;
  2371. procedure TDIBWriter.CreateDIB;
  2372. {$IFDEF PIXELFORMAT_TOO_SLOW}
  2373. var
  2374. SrcColors : WORD;
  2375. // ScreenDC : HDC;
  2376. // From Delphi 3.02 graphics.pas
  2377. // There is a bug in the ByteSwapColors from Delphi 3.0!
  2378. procedure ByteSwapColors(var Colors; Count: Integer);
  2379. var // convert RGB to BGR and vice-versa. TRGBQuad <-> TPaletteEntry
  2380. SysInfo: TSystemInfo;
  2381. begin
  2382. GetSystemInfo(SysInfo);
  2383. asm
  2384. MOV EDX, Colors
  2385. MOV ECX, Count
  2386. DEC ECX
  2387. JS @@END
  2388. LEA EAX, SysInfo
  2389. CMP [EAX].TSystemInfo.wProcessorLevel, 3
  2390. JE @@386
  2391. @@1: MOV EAX, [EDX+ECX*4]
  2392. BSWAP EAX
  2393. SHR EAX,8
  2394. MOV [EDX+ECX*4],EAX
  2395. DEC ECX
  2396. JNS @@1
  2397. JMP @@END
  2398. @@386:
  2399. PUSH EBX
  2400. @@2: XOR EBX,EBX
  2401. MOV EAX, [EDX+ECX*4]
  2402. MOV BH, AL
  2403. MOV BL, AH
  2404. SHR EAX,16
  2405. SHL EBX,8
  2406. MOV BL, AL
  2407. MOV [EDX+ECX*4],EBX
  2408. DEC ECX
  2409. JNS @@2
  2410. POP EBX
  2411. @@END:
  2412. end;
  2413. end;
  2414. {$ENDIF}
  2415. begin
  2416. {$ifdef PIXELFORMAT_TOO_SLOW}
  2417. FreeDIB;
  2418. if (PixelFormat = pf8bit) then
  2419. // 8 bit: Header and palette
  2420. FDIBInfoSize := SizeOf(TBitmapInfoHeader) + SizeOf(TRGBQuad) * (1 shl 8)
  2421. else
  2422. // 24 bit: Header but no palette
  2423. FDIBInfoSize := SizeOf(TBitmapInfoHeader);
  2424. // Allocate TBitmapInfo structure
  2425. GetMem(FDIBInfo, FDIBInfoSize);
  2426. try
  2427. FDIBInfo^.bmiHeader.biSize := SizeOf(FDIBInfo^.bmiHeader);
  2428. FDIBInfo^.bmiHeader.biWidth := Width;
  2429. FDIBInfo^.bmiHeader.biHeight := Height;
  2430. FDIBInfo^.bmiHeader.biPlanes := 1;
  2431. FDIBInfo^.bmiHeader.biSizeImage := 0;
  2432. FDIBInfo^.bmiHeader.biCompression := BI_RGB;
  2433. if (PixelFormat = pf8bit) then
  2434. begin
  2435. FDIBInfo^.bmiHeader.biBitCount := 8;
  2436. // Find number of colors defined by palette
  2437. if (Palette <> 0) and
  2438. (GetObject(Palette, sizeof(SrcColors), @SrcColors) <> 0) and
  2439. (SrcColors <> 0) then
  2440. begin
  2441. // Copy all colors...
  2442. GetPaletteEntries(Palette, 0, SrcColors, FDIBInfo^.bmiColors[0]);
  2443. // ...and convert BGR to RGB
  2444. ByteSwapColors(FDIBInfo^.bmiColors[0], SrcColors);
  2445. end else
  2446. SrcColors := 0;
  2447. // Finally zero any unused entried
  2448. if (SrcColors < 256) then
  2449. FillChar(pointer(LongInt(@FDIBInfo^.bmiColors)+SizeOf(TRGBQuad)*SrcColors)^,
  2450. 256 - SrcColors, 0);
  2451. FDIBInfo^.bmiHeader.biClrUsed := 256;
  2452. FDIBInfo^.bmiHeader.biClrImportant := SrcColors;
  2453. end else
  2454. begin
  2455. FDIBInfo^.bmiHeader.biBitCount := 24;
  2456. FDIBInfo^.bmiHeader.biClrUsed := 0;
  2457. FDIBInfo^.bmiHeader.biClrImportant := 0;
  2458. end;
  2459. FDIBBitsSize := AlignBit(Width, FDIBInfo^.bmiHeader.biBitCount, 32) * Cardinal(abs(Height));
  2460. {$ifdef CREATEDIBSECTION_SLOW}
  2461. FDIBBits := GlobalAllocPtr(GMEM_MOVEABLE, FDIBBitsSize);
  2462. if (FDIBBits = nil) then
  2463. raise EOutOfMemory.Create(sOutOfMemDIB);
  2464. {$else}
  2465. // ScreenDC := GDICheck(GetDC(0));
  2466. try
  2467. // Allocate DIB section
  2468. // Note: You can ignore warnings about the HDC parameter being 0. The
  2469. // parameter is not used for 24 bit bitmaps
  2470. FDIB := GDICheck(CreateDIBSection(0 {ScreenDC}, FDIBInfo^, DIB_RGB_COLORS,
  2471. FDIBBits,
  2472. {$IFDEF VER9x} nil, {$ELSE} 0, {$ENDIF}
  2473. 0));
  2474. finally
  2475. // ReleaseDC(0, ScreenDC);
  2476. end;
  2477. {$endif}
  2478. except
  2479. FreeDIB;
  2480. raise;
  2481. end;
  2482. {$endif}
  2483. end;
  2484. procedure TDIBWriter.FreeDIB;
  2485. begin
  2486. {$ifdef PIXELFORMAT_TOO_SLOW}
  2487. if (FDIBInfo <> nil) then
  2488. FreeMem(FDIBInfo);
  2489. {$ifdef CREATEDIBSECTION_SLOW}
  2490. if (FDIBBits <> nil) then
  2491. GlobalFreePtr(FDIBBits);
  2492. {$else}
  2493. if (FDIB <> 0) then
  2494. DeleteObject(FDIB);
  2495. FDIB := 0;
  2496. {$endif}
  2497. FDIBInfo := nil;
  2498. FDIBBits := nil;
  2499. {$endif}
  2500. end;
  2501. procedure TDIBWriter.NeedDIB;
  2502. begin
  2503. {$ifdef PIXELFORMAT_TOO_SLOW}
  2504. {$ifdef CREATEDIBSECTION_SLOW}
  2505. if (FDIBBits = nil) then
  2506. {$else}
  2507. if (FDIB = 0) then
  2508. {$endif}
  2509. CreateDIB;
  2510. {$endif}
  2511. end;
  2512. // Convert the DIB created by CreateDIB back to a TBitmap
  2513. procedure TDIBWriter.UpdateBitmap;
  2514. {$ifdef PIXELFORMAT_TOO_SLOW}
  2515. var
  2516. Stream : TMemoryStream;
  2517. FileSize : longInt;
  2518. BitmapFileHeader : TBitmapFileHeader;
  2519. {$endif}
  2520. begin
  2521. {$ifdef PIXELFORMAT_TOO_SLOW}
  2522. {$ifdef CREATEDIBSECTION_SLOW}
  2523. if (FDIBBits = nil) then
  2524. {$else}
  2525. if (FDIB = 0) then
  2526. {$endif}
  2527. exit;
  2528. // Win95 and NT differs in what solution performs best
  2529. {$ifndef CREATEDIBSECTION_SLOW}
  2530. {$ifdef VER10_PLUS}
  2531. if (Win32Platform = VER_PLATFORM_WIN32_NT) then
  2532. begin
  2533. // Assign DIB to bitmap
  2534. FBitmap.Handle := FDIB;
  2535. FDIB := 0;
  2536. FBitmap.Palette := CopyPalette(Palette);
  2537. end else
  2538. {$endif}
  2539. {$endif}
  2540. begin
  2541. // Write DIB to a stream in the BMP file format
  2542. Stream := TMemoryStream.Create;
  2543. try
  2544. // Make room in stream for a TBitmapInfo and pixel data
  2545. FileSize := sizeof(TBitmapFileHeader) + FDIBInfoSize + FDIBBitsSize;
  2546. Stream.SetSize(FileSize);
  2547. // Initialize file header
  2548. FillChar(BitmapFileHeader, sizeof(TBitmapFileHeader), 0);
  2549. with BitmapFileHeader do
  2550. begin
  2551. bfType := $4D42; // 'BM' = Windows BMP signature
  2552. bfSize := FileSize; // File size (not needed)
  2553. bfOffBits := sizeof(TBitmapFileHeader) + FDIBInfoSize; // Offset of pixel data
  2554. end;
  2555. // Save file header
  2556. Stream.Write(BitmapFileHeader, sizeof(TBitmapFileHeader));
  2557. // Save TBitmapInfo structure
  2558. Stream.Write(FDIBInfo^, FDIBInfoSize);
  2559. // Save pixel data
  2560. Stream.Write(FDIBBits^, FDIBBitsSize);
  2561. // Rewind and load bitmap from stream
  2562. Stream.Position := 0;
  2563. FBitmap.LoadFromStream(Stream);
  2564. finally
  2565. Stream.Free;
  2566. end;
  2567. end;
  2568. {$endif}
  2569. end;
  2570. ////////////////////////////////////////////////////////////////////////////////
  2571. //
  2572. // Color Mapping
  2573. //
  2574. ////////////////////////////////////////////////////////////////////////////////
  2575. type
  2576. TColorLookup = class(TObject)
  2577. private
  2578. FColors : integer;
  2579. public
  2580. constructor Create(Palette: hPalette); virtual;
  2581. function Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; virtual; abstract;
  2582. property Colors: integer read FColors;
  2583. end;
  2584. PRGBQuadArray = ^TRGBQuadArray; // From Delphi 3 graphics.pas
  2585. TRGBQuadArray = array[Byte] of TRGBQuad; // From Delphi 3 graphics.pas
  2586. BGRArray = array[0..0] of TRGBTriple;
  2587. PBGRArray = ^BGRArray;
  2588. PalArray = array[byte] of TPaletteEntry;
  2589. PPalArray = ^PalArray;
  2590. // TFastColorLookup implements a simple but reasonably fast generic color
  2591. // mapper. It trades precision for speed by reducing the size of the color
  2592. // space.
  2593. // Using a class instead of inline code results in a speed penalty of
  2594. // approx. 15% but reduces the complexity of the color reduction routines that
  2595. // uses it. If bitmap to GIF conversion speed is really important to you, the
  2596. // implementation can easily be inlined again.
  2597. TInverseLookup = array[0..1 SHL 15-1] of SmallInt;
  2598. PInverseLookup = ^TInverseLookup;
  2599. TFastColorLookup = class(TColorLookup)
  2600. private
  2601. FPaletteEntries : PPalArray;
  2602. FInverseLookup : PInverseLookup;
  2603. public
  2604. constructor Create(Palette: hPalette); override;
  2605. destructor Destroy; override;
  2606. function Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; override;
  2607. end;
  2608. // TSlowColorLookup implements a precise but very slow generic color mapper.
  2609. // It uses the GetNearestPaletteIndex GDI function.
  2610. // Note: Tests has shown TFastColorLookup to be more precise than
  2611. // TSlowColorLookup in many cases. I can't explain why...
  2612. TSlowColorLookup = class(TColorLookup)
  2613. private
  2614. FPaletteEntries : PPalArray;
  2615. FPalette : hPalette;
  2616. public
  2617. constructor Create(Palette: hPalette); override;
  2618. destructor Destroy; override;
  2619. function Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; override;
  2620. end;
  2621. // TNetscapeColorLookup maps colors to the netscape 6*6*6 color cube.
  2622. TNetscapeColorLookup = class(TColorLookup)
  2623. public
  2624. constructor Create(Palette: hPalette); override;
  2625. function Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; override;
  2626. end;
  2627. // TGrayWindowsLookup maps colors to 4 shade palette.
  2628. TGrayWindowsLookup = class(TSlowColorLookup)
  2629. public
  2630. constructor Create(Palette: hPalette); override;
  2631. function Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; override;
  2632. end;
  2633. // TGrayScaleLookup maps colors to a uniform 256 shade palette.
  2634. TGrayScaleLookup = class(TColorLookup)
  2635. public
  2636. constructor Create(Palette: hPalette); override;
  2637. function Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; override;
  2638. end;
  2639. // TMonochromeLookup maps colors to a black/white palette.
  2640. TMonochromeLookup = class(TColorLookup)
  2641. public
  2642. constructor Create(Palette: hPalette); override;
  2643. function Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; override;
  2644. end;
  2645. constructor TColorLookup.Create(Palette: hPalette);
  2646. begin
  2647. inherited Create;
  2648. end;
  2649. constructor TFastColorLookup.Create(Palette: hPalette);
  2650. var
  2651. i : integer;
  2652. InverseIndex : integer;
  2653. begin
  2654. inherited Create(Palette);
  2655. GetMem(FPaletteEntries, sizeof(TPaletteEntry) * 256);
  2656. FColors := GetPaletteEntries(Palette, 0, 256, FPaletteEntries^);
  2657. New(FInverseLookup);
  2658. for i := low(TInverseLookup) to high(TInverseLookup) do
  2659. FInverseLookup^[i] := -1;
  2660. // Premap palette colors
  2661. if (FColors > 0) then
  2662. for i := 0 to FColors-1 do
  2663. with FPaletteEntries^[i] do
  2664. begin
  2665. InverseIndex := (peRed SHR 3) OR ((peGreen AND $F8) SHL 2) OR ((peBlue AND $F8) SHL 7);
  2666. if (FInverseLookup^[InverseIndex] = -1) then
  2667. FInverseLookup^[InverseIndex] := i;
  2668. end;
  2669. end;
  2670. destructor TFastColorLookup.Destroy;
  2671. begin
  2672. if (FPaletteEntries <> nil) then
  2673. FreeMem(FPaletteEntries);
  2674. if (FInverseLookup <> nil) then
  2675. Dispose(FInverseLookup);
  2676. inherited Destroy;
  2677. end;
  2678. // Map color to arbitrary palette
  2679. function TFastColorLookup.Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  2680. var
  2681. i : integer;
  2682. InverseIndex : integer;
  2683. Delta ,
  2684. MinDelta ,
  2685. MinColor : integer;
  2686. begin
  2687. // Reduce color space with 3 bits in each dimension
  2688. InverseIndex := (Red SHR 3) OR ((Green AND $F8) SHL 2) OR ((Blue AND $F8) SHL 7);
  2689. if (FInverseLookup^[InverseIndex] <> -1) then
  2690. Result := char(FInverseLookup^[InverseIndex])
  2691. else
  2692. begin
  2693. // Sequential scan for nearest color to minimize euclidian distance
  2694. MinDelta := 3 * (256 * 256);
  2695. MinColor := 0;
  2696. for i := 0 to FColors-1 do
  2697. with FPaletteEntries[i] do
  2698. begin
  2699. Delta := ABS(peRed - Red) + ABS(peGreen - Green) + ABS(peBlue - Blue);
  2700. if (Delta < MinDelta) then
  2701. begin
  2702. MinDelta := Delta;
  2703. MinColor := i;
  2704. end;
  2705. end;
  2706. Result := char(MinColor);
  2707. FInverseLookup^[InverseIndex] := MinColor;
  2708. end;
  2709. with FPaletteEntries^[ord(Result)] do
  2710. begin
  2711. R := peRed;
  2712. G := peGreen;
  2713. B := peBlue;
  2714. end;
  2715. end;
  2716. constructor TSlowColorLookup.Create(Palette: hPalette);
  2717. begin
  2718. inherited Create(Palette);
  2719. FPalette := Palette;
  2720. FColors := GetPaletteEntries(Palette, 0, 256, nil^);
  2721. if (FColors > 0) then
  2722. begin
  2723. GetMem(FPaletteEntries, sizeof(TPaletteEntry) * FColors);
  2724. FColors := GetPaletteEntries(Palette, 0, 256, FPaletteEntries^);
  2725. end;
  2726. end;
  2727. destructor TSlowColorLookup.Destroy;
  2728. begin
  2729. if (FPaletteEntries <> nil) then
  2730. FreeMem(FPaletteEntries);
  2731. inherited Destroy;
  2732. end;
  2733. // Map color to arbitrary palette
  2734. function TSlowColorLookup.Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  2735. begin
  2736. Result := char(GetNearestPaletteIndex(FPalette, Red OR (Green SHL 8) OR (Blue SHL 16)));
  2737. if (FPaletteEntries <> nil) then
  2738. with FPaletteEntries^[ord(Result)] do
  2739. begin
  2740. R := peRed;
  2741. G := peGreen;
  2742. B := peBlue;
  2743. end;
  2744. end;
  2745. constructor TNetscapeColorLookup.Create(Palette: hPalette);
  2746. begin
  2747. inherited Create(Palette);
  2748. FColors := 6*6*6; // This better be true or something is wrong
  2749. end;
  2750. // Map color to netscape 6*6*6 color cube
  2751. function TNetscapeColorLookup.Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  2752. begin
  2753. R := (Red+3) DIV 51;
  2754. G := (Green+3) DIV 51;
  2755. B := (Blue+3) DIV 51;
  2756. Result := char(B + 6*G + 36*R);
  2757. R := R * 51;
  2758. G := G * 51;
  2759. B := B * 51;
  2760. end;
  2761. constructor TGrayWindowsLookup.Create(Palette: hPalette);
  2762. begin
  2763. inherited Create(Palette);
  2764. FColors := 4;
  2765. end;
  2766. // Convert color to windows grays
  2767. function TGrayWindowsLookup.Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  2768. begin
  2769. Result := inherited Lookup(MulDiv(Red, 77, 256),
  2770. MulDiv(Green, 150, 256), MulDiv(Blue, 29, 256), R, G, B);
  2771. end;
  2772. constructor TGrayScaleLookup.Create(Palette: hPalette);
  2773. begin
  2774. inherited Create(Palette);
  2775. FColors := 256;
  2776. end;
  2777. // Convert color to grayscale
  2778. function TGrayScaleLookup.Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  2779. begin
  2780. Result := char((Blue*29 + Green*150 + Red*77) DIV 256);
  2781. R := ord(Result);
  2782. G := ord(Result);
  2783. B := ord(Result);
  2784. end;
  2785. constructor TMonochromeLookup.Create(Palette: hPalette);
  2786. begin
  2787. inherited Create(Palette);
  2788. FColors := 2;
  2789. end;
  2790. // Convert color to black/white
  2791. function TMonochromeLookup.Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  2792. begin
  2793. if ((Blue*29 + Green*150 + Red*77) > 32512) then
  2794. begin
  2795. Result := #1;
  2796. R := 255;
  2797. G := 255;
  2798. B := 255;
  2799. end else
  2800. begin
  2801. Result := #0;
  2802. R := 0;
  2803. G := 0;
  2804. B := 0;
  2805. end;
  2806. end;
  2807. ////////////////////////////////////////////////////////////////////////////////
  2808. //
  2809. // Dithering engine
  2810. //
  2811. ////////////////////////////////////////////////////////////////////////////////
  2812. type
  2813. TDitherEngine = class
  2814. private
  2815. protected
  2816. FDirection : integer;
  2817. FColumn : integer;
  2818. FLookup : TColorLookup;
  2819. Width : integer;
  2820. public
  2821. constructor Create(AWidth: integer; Lookup: TColorLookup); virtual;
  2822. function Dither(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; virtual;
  2823. procedure NextLine; virtual;
  2824. procedure NextColumn;
  2825. property Direction: integer read FDirection;
  2826. property Column: integer read FColumn;
  2827. end;
  2828. // Note: TErrorTerm does only *need* to be 16 bits wide, but since
  2829. // it is *much* faster to use native machine words (32 bit), we sacrifice
  2830. // some bytes (a lot actually) to improve performance.
  2831. TErrorTerm = Integer;
  2832. TErrors = array[0..0] of TErrorTerm;
  2833. PErrors = ^TErrors;
  2834. TFloydSteinbergDitherer = class(TDitherEngine)
  2835. private
  2836. ErrorsR ,
  2837. ErrorsG ,
  2838. ErrorsB : PErrors;
  2839. ErrorR ,
  2840. ErrorG ,
  2841. ErrorB : PErrors;
  2842. CurrentErrorR , // Current error or pixel value
  2843. CurrentErrorG ,
  2844. CurrentErrorB ,
  2845. BelowErrorR , // Error for pixel below current
  2846. BelowErrorG ,
  2847. BelowErrorB ,
  2848. BelowPrevErrorR , // Error for pixel below previous pixel
  2849. BelowPrevErrorG ,
  2850. BelowPrevErrorB : TErrorTerm;
  2851. public
  2852. constructor Create(AWidth: integer; Lookup: TColorLookup); override;
  2853. destructor Destroy; override;
  2854. function Dither(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; override;
  2855. procedure NextLine; override;
  2856. end;
  2857. T5by3Ditherer = class(TDitherEngine)
  2858. private
  2859. ErrorsR0 ,
  2860. ErrorsG0 ,
  2861. ErrorsB0 ,
  2862. ErrorsR1 ,
  2863. ErrorsG1 ,
  2864. ErrorsB1 ,
  2865. ErrorsR2 ,
  2866. ErrorsG2 ,
  2867. ErrorsB2 : PErrors;
  2868. ErrorR0 ,
  2869. ErrorG0 ,
  2870. ErrorB0 ,
  2871. ErrorR1 ,
  2872. ErrorG1 ,
  2873. ErrorB1 ,
  2874. ErrorR2 ,
  2875. ErrorG2 ,
  2876. ErrorB2 : PErrors;
  2877. FDirection2 : integer;
  2878. protected
  2879. FDivisor : integer;
  2880. procedure Propagate(Errors0, Errors1, Errors2: PErrors; Error: integer); virtual; abstract;
  2881. public
  2882. constructor Create(AWidth: integer; Lookup: TColorLookup); override;
  2883. destructor Destroy; override;
  2884. function Dither(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; override;
  2885. procedure NextLine; override;
  2886. end;
  2887. TStuckiDitherer = class(T5by3Ditherer)
  2888. protected
  2889. procedure Propagate(Errors0, Errors1, Errors2: PErrors; Error: integer); override;
  2890. public
  2891. constructor Create(AWidth: integer; Lookup: TColorLookup); override;
  2892. end;
  2893. TSierraDitherer = class(T5by3Ditherer)
  2894. protected
  2895. procedure Propagate(Errors0, Errors1, Errors2: PErrors; Error: integer); override;
  2896. public
  2897. constructor Create(AWidth: integer; Lookup: TColorLookup); override;
  2898. end;
  2899. TJaJuNiDitherer = class(T5by3Ditherer)
  2900. protected
  2901. procedure Propagate(Errors0, Errors1, Errors2: PErrors; Error: integer); override;
  2902. public
  2903. constructor Create(AWidth: integer; Lookup: TColorLookup); override;
  2904. end;
  2905. TSteveArcheDitherer = class(TDitherEngine)
  2906. private
  2907. ErrorsR0 ,
  2908. ErrorsG0 ,
  2909. ErrorsB0 ,
  2910. ErrorsR1 ,
  2911. ErrorsG1 ,
  2912. ErrorsB1 ,
  2913. ErrorsR2 ,
  2914. ErrorsG2 ,
  2915. ErrorsB2 ,
  2916. ErrorsR3 ,
  2917. ErrorsG3 ,
  2918. ErrorsB3 : PErrors;
  2919. ErrorR0 ,
  2920. ErrorG0 ,
  2921. ErrorB0 ,
  2922. ErrorR1 ,
  2923. ErrorG1 ,
  2924. ErrorB1 ,
  2925. ErrorR2 ,
  2926. ErrorG2 ,
  2927. ErrorB2 ,
  2928. ErrorR3 ,
  2929. ErrorG3 ,
  2930. ErrorB3 : PErrors;
  2931. FDirection2 ,
  2932. FDirection3 : integer;
  2933. public
  2934. constructor Create(AWidth: integer; Lookup: TColorLookup); override;
  2935. destructor Destroy; override;
  2936. function Dither(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; override;
  2937. procedure NextLine; override;
  2938. end;
  2939. TBurkesDitherer = class(TDitherEngine)
  2940. private
  2941. ErrorsR0 ,
  2942. ErrorsG0 ,
  2943. ErrorsB0 ,
  2944. ErrorsR1 ,
  2945. ErrorsG1 ,
  2946. ErrorsB1 : PErrors;
  2947. ErrorR0 ,
  2948. ErrorG0 ,
  2949. ErrorB0 ,
  2950. ErrorR1 ,
  2951. ErrorG1 ,
  2952. ErrorB1 : PErrors;
  2953. FDirection2 : integer;
  2954. public
  2955. constructor Create(AWidth: integer; Lookup: TColorLookup); override;
  2956. destructor Destroy; override;
  2957. function Dither(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; override;
  2958. procedure NextLine; override;
  2959. end;
  2960. ////////////////////////////////////////////////////////////////////////////////
  2961. // TDitherEngine
  2962. constructor TDitherEngine.Create(AWidth: integer; Lookup: TColorLookup);
  2963. begin
  2964. inherited Create;
  2965. FLookup := Lookup;
  2966. Width := AWidth;
  2967. FDirection := 1;
  2968. FColumn := 0;
  2969. end;
  2970. function TDitherEngine.Dither(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  2971. begin
  2972. // Map color to palette
  2973. Result := FLookup.Lookup(Red, Green, Blue, R, G, B);
  2974. NextColumn;
  2975. end;
  2976. procedure TDitherEngine.NextLine;
  2977. begin
  2978. FDirection := -FDirection;
  2979. if (FDirection = 1) then
  2980. FColumn := 0
  2981. else
  2982. FColumn := Width-1;
  2983. end;
  2984. procedure TDitherEngine.NextColumn;
  2985. begin
  2986. inc(FColumn, FDirection);
  2987. end;
  2988. ////////////////////////////////////////////////////////////////////////////////
  2989. // TFloydSteinbergDitherer
  2990. constructor TFloydSteinbergDitherer.Create(AWidth: integer; Lookup: TColorLookup);
  2991. begin
  2992. inherited Create(AWidth, Lookup);
  2993. // The Error arrays has (columns + 2) entries; the extra entry at
  2994. // each end saves us from special-casing the first and last pixels.
  2995. // We can get away with a single array (holding one row's worth of errors)
  2996. // by using it to store the current row's errors at pixel columns not yet
  2997. // processed, but the next row's errors at columns already processed. We
  2998. // need only a few extra variables to hold the errors immediately around the
  2999. // current column. (If we are lucky, those variables are in registers, but
  3000. // even if not, they're probably cheaper to access than array elements are.)
  3001. GetMem(ErrorsR, sizeof(TErrorTerm)*(Width+2));
  3002. GetMem(ErrorsG, sizeof(TErrorTerm)*(Width+2));
  3003. GetMem(ErrorsB, sizeof(TErrorTerm)*(Width+2));
  3004. FillChar(ErrorsR^, sizeof(TErrorTerm)*(Width+2), 0);
  3005. FillChar(ErrorsG^, sizeof(TErrorTerm)*(Width+2), 0);
  3006. FillChar(ErrorsB^, sizeof(TErrorTerm)*(Width+2), 0);
  3007. ErrorR := ErrorsR;
  3008. ErrorG := ErrorsG;
  3009. ErrorB := ErrorsB;
  3010. CurrentErrorR := 0;
  3011. CurrentErrorG := CurrentErrorR;
  3012. CurrentErrorB := CurrentErrorR;
  3013. BelowErrorR := CurrentErrorR;
  3014. BelowErrorG := CurrentErrorR;
  3015. BelowErrorB := CurrentErrorR;
  3016. BelowPrevErrorR := CurrentErrorR;
  3017. BelowPrevErrorG := CurrentErrorR;
  3018. BelowPrevErrorB := CurrentErrorR;
  3019. end;
  3020. destructor TFloydSteinbergDitherer.Destroy;
  3021. begin
  3022. FreeMem(ErrorsR);
  3023. FreeMem(ErrorsG);
  3024. FreeMem(ErrorsB);
  3025. inherited Destroy;
  3026. end;
  3027. {$IFOPT R+}
  3028. {$DEFINE R_PLUS}
  3029. {$RANGECHECKS OFF}
  3030. {$ENDIF}
  3031. function TFloydSteinbergDitherer.Dither(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  3032. var
  3033. BelowNextError : TErrorTerm;
  3034. Delta : TErrorTerm;
  3035. begin
  3036. CurrentErrorR := Red + (CurrentErrorR + ErrorR[0] + 8) DIV 16;
  3037. // CurrentErrorR := Red + (CurrentErrorR + ErrorR[Direction] + 8) DIV 16;
  3038. if (CurrentErrorR < 0) then
  3039. CurrentErrorR := 0
  3040. else if (CurrentErrorR > 255) then
  3041. CurrentErrorR := 255;
  3042. CurrentErrorG := Green + (CurrentErrorG + ErrorG[0] + 8) DIV 16;
  3043. // CurrentErrorG := Green + (CurrentErrorG + ErrorG[Direction] + 8) DIV 16;
  3044. if (CurrentErrorG < 0) then
  3045. CurrentErrorG := 0
  3046. else if (CurrentErrorG > 255) then
  3047. CurrentErrorG := 255;
  3048. CurrentErrorB := Blue + (CurrentErrorB + ErrorB[0] + 8) DIV 16;
  3049. // CurrentErrorB := Blue + (CurrentErrorB + ErrorB[Direction] + 8) DIV 16;
  3050. if (CurrentErrorB < 0) then
  3051. CurrentErrorB := 0
  3052. else if (CurrentErrorB > 255) then
  3053. CurrentErrorB := 255;
  3054. // Map color to palette
  3055. Result := inherited Dither(CurrentErrorR, CurrentErrorG, CurrentErrorB, R, G, B);
  3056. // Propagate Floyd-Steinberg error terms.
  3057. // Errors are accumulated into the error arrays, at a resolution of
  3058. // 1/16th of a pixel count. The error at a given pixel is propagated
  3059. // to its not-yet-processed neighbors using the standard F-S fractions,
  3060. // ... (here) 7/16
  3061. // 3/16 5/16 1/16
  3062. // We work left-to-right on even rows, right-to-left on odd rows.
  3063. // Red component
  3064. CurrentErrorR := CurrentErrorR - R;
  3065. if (CurrentErrorR <> 0) then
  3066. begin
  3067. BelowNextError := CurrentErrorR; // Error * 1
  3068. Delta := CurrentErrorR * 2;
  3069. inc(CurrentErrorR, Delta);
  3070. ErrorR[0] := BelowPrevErrorR + CurrentErrorR; // Error * 3
  3071. inc(CurrentErrorR, Delta);
  3072. BelowPrevErrorR := BelowErrorR + CurrentErrorR; // Error * 5
  3073. BelowErrorR := BelowNextError; // Error * 1
  3074. inc(CurrentErrorR, Delta); // Error * 7
  3075. end;
  3076. // Green component
  3077. CurrentErrorG := CurrentErrorG - G;
  3078. if (CurrentErrorG <> 0) then
  3079. begin
  3080. BelowNextError := CurrentErrorG; // Error * 1
  3081. Delta := CurrentErrorG * 2;
  3082. inc(CurrentErrorG, Delta);
  3083. ErrorG[0] := BelowPrevErrorG + CurrentErrorG; // Error * 3
  3084. inc(CurrentErrorG, Delta);
  3085. BelowPrevErrorG := BelowErrorG + CurrentErrorG; // Error * 5
  3086. BelowErrorG := BelowNextError; // Error * 1
  3087. inc(CurrentErrorG, Delta); // Error * 7
  3088. end;
  3089. // Blue component
  3090. CurrentErrorB := CurrentErrorB - B;
  3091. if (CurrentErrorB <> 0) then
  3092. begin
  3093. BelowNextError := CurrentErrorB; // Error * 1
  3094. Delta := CurrentErrorB * 2;
  3095. inc(CurrentErrorB, Delta);
  3096. ErrorB[0] := BelowPrevErrorB + CurrentErrorB; // Error * 3
  3097. inc(CurrentErrorB, Delta);
  3098. BelowPrevErrorB := BelowErrorB + CurrentErrorB; // Error * 5
  3099. BelowErrorB := BelowNextError; // Error * 1
  3100. inc(CurrentErrorB, Delta); // Error * 7
  3101. end;
  3102. // Move on to next column
  3103. if (Direction = 1) then
  3104. begin
  3105. inc(longInt(ErrorR), sizeof(TErrorTerm));
  3106. inc(longInt(ErrorG), sizeof(TErrorTerm));
  3107. inc(longInt(ErrorB), sizeof(TErrorTerm));
  3108. end else
  3109. begin
  3110. dec(longInt(ErrorR), sizeof(TErrorTerm));
  3111. dec(longInt(ErrorG), sizeof(TErrorTerm));
  3112. dec(longInt(ErrorB), sizeof(TErrorTerm));
  3113. end;
  3114. end;
  3115. {$IFDEF R_PLUS}
  3116. {$RANGECHECKS ON}
  3117. {$UNDEF R_PLUS}
  3118. {$ENDIF}
  3119. {$IFOPT R+}
  3120. {$DEFINE R_PLUS}
  3121. {$RANGECHECKS OFF}
  3122. {$ENDIF}
  3123. procedure TFloydSteinbergDitherer.NextLine;
  3124. begin
  3125. ErrorR[0] := BelowPrevErrorR;
  3126. ErrorG[0] := BelowPrevErrorG;
  3127. ErrorB[0] := BelowPrevErrorB;
  3128. // Note: The optimizer produces better code for this construct:
  3129. // a := 0; b := a; c := a;
  3130. // compared to this construct:
  3131. // a := 0; b := 0; c := 0;
  3132. CurrentErrorR := 0;
  3133. CurrentErrorG := CurrentErrorR;
  3134. CurrentErrorB := CurrentErrorG;
  3135. BelowErrorR := CurrentErrorG;
  3136. BelowErrorG := CurrentErrorG;
  3137. BelowErrorB := CurrentErrorG;
  3138. BelowPrevErrorR := CurrentErrorG;
  3139. BelowPrevErrorG := CurrentErrorG;
  3140. BelowPrevErrorB := CurrentErrorG;
  3141. inherited NextLine;
  3142. if (Direction = 1) then
  3143. begin
  3144. ErrorR := ErrorsR;
  3145. ErrorG := ErrorsG;
  3146. ErrorB := ErrorsB;
  3147. end else
  3148. begin
  3149. ErrorR := @ErrorsR[Width+1];
  3150. ErrorG := @ErrorsG[Width+1];
  3151. ErrorB := @ErrorsB[Width+1];
  3152. end;
  3153. end;
  3154. {$IFDEF R_PLUS}
  3155. {$RANGECHECKS ON}
  3156. {$UNDEF R_PLUS}
  3157. {$ENDIF}
  3158. ////////////////////////////////////////////////////////////////////////////////
  3159. // T5by3Ditherer
  3160. constructor T5by3Ditherer.Create(AWidth: integer; Lookup: TColorLookup);
  3161. begin
  3162. inherited Create(AWidth, Lookup);
  3163. GetMem(ErrorsR0, sizeof(TErrorTerm)*(Width+4));
  3164. GetMem(ErrorsG0, sizeof(TErrorTerm)*(Width+4));
  3165. GetMem(ErrorsB0, sizeof(TErrorTerm)*(Width+4));
  3166. GetMem(ErrorsR1, sizeof(TErrorTerm)*(Width+4));
  3167. GetMem(ErrorsG1, sizeof(TErrorTerm)*(Width+4));
  3168. GetMem(ErrorsB1, sizeof(TErrorTerm)*(Width+4));
  3169. GetMem(ErrorsR2, sizeof(TErrorTerm)*(Width+4));
  3170. GetMem(ErrorsG2, sizeof(TErrorTerm)*(Width+4));
  3171. GetMem(ErrorsB2, sizeof(TErrorTerm)*(Width+4));
  3172. FillChar(ErrorsR0^, sizeof(TErrorTerm)*(Width+4), 0);
  3173. FillChar(ErrorsG0^, sizeof(TErrorTerm)*(Width+4), 0);
  3174. FillChar(ErrorsB0^, sizeof(TErrorTerm)*(Width+4), 0);
  3175. FillChar(ErrorsR1^, sizeof(TErrorTerm)*(Width+4), 0);
  3176. FillChar(ErrorsG1^, sizeof(TErrorTerm)*(Width+4), 0);
  3177. FillChar(ErrorsB1^, sizeof(TErrorTerm)*(Width+4), 0);
  3178. FillChar(ErrorsR2^, sizeof(TErrorTerm)*(Width+4), 0);
  3179. FillChar(ErrorsG2^, sizeof(TErrorTerm)*(Width+4), 0);
  3180. FillChar(ErrorsB2^, sizeof(TErrorTerm)*(Width+4), 0);
  3181. FDivisor := 1;
  3182. FDirection2 := 2 * Direction;
  3183. ErrorR0 := PErrors(longInt(ErrorsR0)+2*sizeof(TErrorTerm));
  3184. ErrorG0 := PErrors(longInt(ErrorsG0)+2*sizeof(TErrorTerm));
  3185. ErrorB0 := PErrors(longInt(ErrorsB0)+2*sizeof(TErrorTerm));
  3186. ErrorR1 := PErrors(longInt(ErrorsR1)+2*sizeof(TErrorTerm));
  3187. ErrorG1 := PErrors(longInt(ErrorsG1)+2*sizeof(TErrorTerm));
  3188. ErrorB1 := PErrors(longInt(ErrorsB1)+2*sizeof(TErrorTerm));
  3189. ErrorR2 := PErrors(longInt(ErrorsR2)+2*sizeof(TErrorTerm));
  3190. ErrorG2 := PErrors(longInt(ErrorsG2)+2*sizeof(TErrorTerm));
  3191. ErrorB2 := PErrors(longInt(ErrorsB2)+2*sizeof(TErrorTerm));
  3192. end;
  3193. destructor T5by3Ditherer.Destroy;
  3194. begin
  3195. FreeMem(ErrorsR0);
  3196. FreeMem(ErrorsG0);
  3197. FreeMem(ErrorsB0);
  3198. FreeMem(ErrorsR1);
  3199. FreeMem(ErrorsG1);
  3200. FreeMem(ErrorsB1);
  3201. FreeMem(ErrorsR2);
  3202. FreeMem(ErrorsG2);
  3203. FreeMem(ErrorsB2);
  3204. inherited Destroy;
  3205. end;
  3206. {$IFOPT R+}
  3207. {$DEFINE R_PLUS}
  3208. {$RANGECHECKS OFF}
  3209. {$ENDIF}
  3210. function T5by3Ditherer.Dither(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  3211. var
  3212. ColorR ,
  3213. ColorG ,
  3214. ColorB : integer; // Error for current pixel
  3215. begin
  3216. // Apply red component error correction
  3217. ColorR := Red + (ErrorR0[0] + FDivisor DIV 2) DIV FDivisor;
  3218. if (ColorR < 0) then
  3219. ColorR := 0
  3220. else if (ColorR > 255) then
  3221. ColorR := 255;
  3222. // Apply green component error correction
  3223. ColorG := Green + (ErrorG0[0] + FDivisor DIV 2) DIV FDivisor;
  3224. if (ColorG < 0) then
  3225. ColorG := 0
  3226. else if (ColorG > 255) then
  3227. ColorG := 255;
  3228. // Apply blue component error correction
  3229. ColorB := Blue + (ErrorB0[0] + FDivisor DIV 2) DIV FDivisor;
  3230. if (ColorB < 0) then
  3231. ColorB := 0
  3232. else if (ColorB > 255) then
  3233. ColorB := 255;
  3234. // Map color to palette
  3235. Result := inherited Dither(ColorR, ColorG, ColorB, R, G, B);
  3236. // Propagate red component error
  3237. Propagate(ErrorR0, ErrorR1, ErrorR2, ColorR - R);
  3238. // Propagate green component error
  3239. Propagate(ErrorG0, ErrorG1, ErrorG2, ColorG - G);
  3240. // Propagate blue component error
  3241. Propagate(ErrorB0, ErrorB1, ErrorB2, ColorB - B);
  3242. // Move on to next column
  3243. if (Direction = 1) then
  3244. begin
  3245. inc(longInt(ErrorR0), sizeof(TErrorTerm));
  3246. inc(longInt(ErrorG0), sizeof(TErrorTerm));
  3247. inc(longInt(ErrorB0), sizeof(TErrorTerm));
  3248. inc(longInt(ErrorR1), sizeof(TErrorTerm));
  3249. inc(longInt(ErrorG1), sizeof(TErrorTerm));
  3250. inc(longInt(ErrorB1), sizeof(TErrorTerm));
  3251. inc(longInt(ErrorR2), sizeof(TErrorTerm));
  3252. inc(longInt(ErrorG2), sizeof(TErrorTerm));
  3253. inc(longInt(ErrorB2), sizeof(TErrorTerm));
  3254. end else
  3255. begin
  3256. dec(longInt(ErrorR0), sizeof(TErrorTerm));
  3257. dec(longInt(ErrorG0), sizeof(TErrorTerm));
  3258. dec(longInt(ErrorB0), sizeof(TErrorTerm));
  3259. dec(longInt(ErrorR1), sizeof(TErrorTerm));
  3260. dec(longInt(ErrorG1), sizeof(TErrorTerm));
  3261. dec(longInt(ErrorB1), sizeof(TErrorTerm));
  3262. dec(longInt(ErrorR2), sizeof(TErrorTerm));
  3263. dec(longInt(ErrorG2), sizeof(TErrorTerm));
  3264. dec(longInt(ErrorB2), sizeof(TErrorTerm));
  3265. end;
  3266. end;
  3267. {$IFDEF R_PLUS}
  3268. {$RANGECHECKS ON}
  3269. {$UNDEF R_PLUS}
  3270. {$ENDIF}
  3271. {$IFOPT R+}
  3272. {$DEFINE R_PLUS}
  3273. {$RANGECHECKS OFF}
  3274. {$ENDIF}
  3275. procedure T5by3Ditherer.NextLine;
  3276. var
  3277. TempErrors : PErrors;
  3278. begin
  3279. FillChar(ErrorsR0^, sizeof(TErrorTerm)*(Width+4), 0);
  3280. FillChar(ErrorsG0^, sizeof(TErrorTerm)*(Width+4), 0);
  3281. FillChar(ErrorsB0^, sizeof(TErrorTerm)*(Width+4), 0);
  3282. // Swap lines
  3283. TempErrors := ErrorsR0;
  3284. ErrorsR0 := ErrorsR1;
  3285. ErrorsR1 := ErrorsR2;
  3286. ErrorsR2 := TempErrors;
  3287. TempErrors := ErrorsG0;
  3288. ErrorsG0 := ErrorsG1;
  3289. ErrorsG1 := ErrorsG2;
  3290. ErrorsG2 := TempErrors;
  3291. TempErrors := ErrorsB0;
  3292. ErrorsB0 := ErrorsB1;
  3293. ErrorsB1 := ErrorsB2;
  3294. ErrorsB2 := TempErrors;
  3295. inherited NextLine;
  3296. FDirection2 := 2 * Direction;
  3297. if (Direction = 1) then
  3298. begin
  3299. // ErrorsR0[1] gives compiler error, so we
  3300. // use PErrors(longInt(ErrorsR0)+sizeof(TErrorTerm)) instead...
  3301. ErrorR0 := PErrors(longInt(ErrorsR0)+2*sizeof(TErrorTerm));
  3302. ErrorG0 := PErrors(longInt(ErrorsG0)+2*sizeof(TErrorTerm));
  3303. ErrorB0 := PErrors(longInt(ErrorsB0)+2*sizeof(TErrorTerm));
  3304. ErrorR1 := PErrors(longInt(ErrorsR1)+2*sizeof(TErrorTerm));
  3305. ErrorG1 := PErrors(longInt(ErrorsG1)+2*sizeof(TErrorTerm));
  3306. ErrorB1 := PErrors(longInt(ErrorsB1)+2*sizeof(TErrorTerm));
  3307. ErrorR2 := PErrors(longInt(ErrorsR2)+2*sizeof(TErrorTerm));
  3308. ErrorG2 := PErrors(longInt(ErrorsG2)+2*sizeof(TErrorTerm));
  3309. ErrorB2 := PErrors(longInt(ErrorsB2)+2*sizeof(TErrorTerm));
  3310. end else
  3311. begin
  3312. ErrorR0 := @ErrorsR0[Width+1];
  3313. ErrorG0 := @ErrorsG0[Width+1];
  3314. ErrorB0 := @ErrorsB0[Width+1];
  3315. ErrorR1 := @ErrorsR1[Width+1];
  3316. ErrorG1 := @ErrorsG1[Width+1];
  3317. ErrorB1 := @ErrorsB1[Width+1];
  3318. ErrorR2 := @ErrorsR2[Width+1];
  3319. ErrorG2 := @ErrorsG2[Width+1];
  3320. ErrorB2 := @ErrorsB2[Width+1];
  3321. end;
  3322. end;
  3323. {$IFDEF R_PLUS}
  3324. {$RANGECHECKS ON}
  3325. {$UNDEF R_PLUS}
  3326. {$ENDIF}
  3327. ////////////////////////////////////////////////////////////////////////////////
  3328. // TStuckiDitherer
  3329. constructor TStuckiDitherer.Create(AWidth: integer; Lookup: TColorLookup);
  3330. begin
  3331. inherited Create(AWidth, Lookup);
  3332. FDivisor := 42;
  3333. end;
  3334. {$IFOPT R+}
  3335. {$DEFINE R_PLUS}
  3336. {$RANGECHECKS OFF}
  3337. {$ENDIF}
  3338. procedure TStuckiDitherer.Propagate(Errors0, Errors1, Errors2: PErrors; Error: integer);
  3339. begin
  3340. if (Error = 0) then
  3341. exit;
  3342. // Propagate Stucki error terms:
  3343. // ... ... (here) 8/42 4/42
  3344. // 2/42 4/42 8/42 4/42 2/42
  3345. // 1/42 2/42 4/42 2/42 1/42
  3346. inc(Errors2[FDirection2], Error); // Error * 1
  3347. inc(Errors2[-FDirection2], Error); // Error * 1
  3348. Error := Error + Error;
  3349. inc(Errors1[FDirection2], Error); // Error * 2
  3350. inc(Errors1[-FDirection2], Error); // Error * 2
  3351. inc(Errors2[Direction], Error); // Error * 2
  3352. inc(Errors2[-Direction], Error); // Error * 2
  3353. Error := Error + Error;
  3354. inc(Errors0[FDirection2], Error); // Error * 4
  3355. inc(Errors1[-Direction], Error); // Error * 4
  3356. inc(Errors1[Direction], Error); // Error * 4
  3357. inc(Errors2[0], Error); // Error * 4
  3358. Error := Error + Error;
  3359. inc(Errors0[Direction], Error); // Error * 8
  3360. inc(Errors1[0], Error); // Error * 8
  3361. end;
  3362. {$IFDEF R_PLUS}
  3363. {$RANGECHECKS ON}
  3364. {$UNDEF R_PLUS}
  3365. {$ENDIF}
  3366. ////////////////////////////////////////////////////////////////////////////////
  3367. // TSierraDitherer
  3368. constructor TSierraDitherer.Create(AWidth: integer; Lookup: TColorLookup);
  3369. begin
  3370. inherited Create(AWidth, Lookup);
  3371. FDivisor := 32;
  3372. end;
  3373. {$IFOPT R+}
  3374. {$DEFINE R_PLUS}
  3375. {$RANGECHECKS OFF}
  3376. {$ENDIF}
  3377. procedure TSierraDitherer.Propagate(Errors0, Errors1, Errors2: PErrors; Error: integer);
  3378. var
  3379. TempError : integer;
  3380. begin
  3381. if (Error = 0) then
  3382. exit;
  3383. // Propagate Sierra error terms:
  3384. // ... ... (here) 5/32 3/32
  3385. // 2/32 4/32 5/32 4/32 2/32
  3386. // ... 2/32 3/32 2/32 ...
  3387. TempError := Error + Error;
  3388. inc(Errors1[FDirection2], TempError); // Error * 2
  3389. inc(Errors1[-FDirection2], TempError);// Error * 2
  3390. inc(Errors2[Direction], TempError); // Error * 2
  3391. inc(Errors2[-Direction], TempError); // Error * 2
  3392. inc(TempError, Error);
  3393. inc(Errors0[FDirection2], TempError); // Error * 3
  3394. inc(Errors2[0], TempError); // Error * 3
  3395. inc(TempError, Error);
  3396. inc(Errors1[-Direction], TempError); // Error * 4
  3397. inc(Errors1[Direction], TempError); // Error * 4
  3398. inc(TempError, Error);
  3399. inc(Errors0[Direction], TempError); // Error * 5
  3400. inc(Errors1[0], TempError); // Error * 5
  3401. end;
  3402. {$IFDEF R_PLUS}
  3403. {$RANGECHECKS ON}
  3404. {$UNDEF R_PLUS}
  3405. {$ENDIF}
  3406. ////////////////////////////////////////////////////////////////////////////////
  3407. // TJaJuNiDitherer
  3408. constructor TJaJuNiDitherer.Create(AWidth: integer; Lookup: TColorLookup);
  3409. begin
  3410. inherited Create(AWidth, Lookup);
  3411. FDivisor := 38;
  3412. end;
  3413. {$IFOPT R+}
  3414. {$DEFINE R_PLUS}
  3415. {$RANGECHECKS OFF}
  3416. {$ENDIF}
  3417. procedure TJaJuNiDitherer.Propagate(Errors0, Errors1, Errors2: PErrors; Error: integer);
  3418. var
  3419. TempError : integer;
  3420. begin
  3421. if (Error = 0) then
  3422. exit;
  3423. // Propagate Jarvis, Judice and Ninke error terms:
  3424. // ... ... (here) 8/38 4/38
  3425. // 2/38 4/38 8/38 4/38 2/38
  3426. // 1/38 2/38 4/38 2/38 1/38
  3427. inc(Errors2[FDirection2], Error); // Error * 1
  3428. inc(Errors2[-FDirection2], Error); // Error * 1
  3429. TempError := Error + Error;
  3430. inc(Error, TempError);
  3431. inc(Errors1[FDirection2], Error); // Error * 3
  3432. inc(Errors1[-FDirection2], Error); // Error * 3
  3433. inc(Errors2[Direction], Error); // Error * 3
  3434. inc(Errors2[-Direction], Error); // Error * 3
  3435. inc(Error, TempError);
  3436. inc(Errors0[FDirection2], Error); // Error * 5
  3437. inc(Errors1[-Direction], Error); // Error * 5
  3438. inc(Errors1[Direction], Error); // Error * 5
  3439. inc(Errors2[0], Error); // Error * 5
  3440. inc(Error, TempError);
  3441. inc(Errors0[Direction], Error); // Error * 7
  3442. inc(Errors1[0], Error); // Error * 7
  3443. end;
  3444. {$IFDEF R_PLUS}
  3445. {$RANGECHECKS ON}
  3446. {$UNDEF R_PLUS}
  3447. {$ENDIF}
  3448. ////////////////////////////////////////////////////////////////////////////////
  3449. // TSteveArcheDitherer
  3450. constructor TSteveArcheDitherer.Create(AWidth: integer; Lookup: TColorLookup);
  3451. begin
  3452. inherited Create(AWidth, Lookup);
  3453. GetMem(ErrorsR0, sizeof(TErrorTerm)*(Width+6));
  3454. GetMem(ErrorsG0, sizeof(TErrorTerm)*(Width+6));
  3455. GetMem(ErrorsB0, sizeof(TErrorTerm)*(Width+6));
  3456. GetMem(ErrorsR1, sizeof(TErrorTerm)*(Width+6));
  3457. GetMem(ErrorsG1, sizeof(TErrorTerm)*(Width+6));
  3458. GetMem(ErrorsB1, sizeof(TErrorTerm)*(Width+6));
  3459. GetMem(ErrorsR2, sizeof(TErrorTerm)*(Width+6));
  3460. GetMem(ErrorsG2, sizeof(TErrorTerm)*(Width+6));
  3461. GetMem(ErrorsB2, sizeof(TErrorTerm)*(Width+6));
  3462. GetMem(ErrorsR3, sizeof(TErrorTerm)*(Width+6));
  3463. GetMem(ErrorsG3, sizeof(TErrorTerm)*(Width+6));
  3464. GetMem(ErrorsB3, sizeof(TErrorTerm)*(Width+6));
  3465. FillChar(ErrorsR0^, sizeof(TErrorTerm)*(Width+6), 0);
  3466. FillChar(ErrorsG0^, sizeof(TErrorTerm)*(Width+6), 0);
  3467. FillChar(ErrorsB0^, sizeof(TErrorTerm)*(Width+6), 0);
  3468. FillChar(ErrorsR1^, sizeof(TErrorTerm)*(Width+6), 0);
  3469. FillChar(ErrorsG1^, sizeof(TErrorTerm)*(Width+6), 0);
  3470. FillChar(ErrorsB1^, sizeof(TErrorTerm)*(Width+6), 0);
  3471. FillChar(ErrorsR2^, sizeof(TErrorTerm)*(Width+6), 0);
  3472. FillChar(ErrorsG2^, sizeof(TErrorTerm)*(Width+6), 0);
  3473. FillChar(ErrorsB2^, sizeof(TErrorTerm)*(Width+6), 0);
  3474. FillChar(ErrorsR3^, sizeof(TErrorTerm)*(Width+6), 0);
  3475. FillChar(ErrorsG3^, sizeof(TErrorTerm)*(Width+6), 0);
  3476. FillChar(ErrorsB3^, sizeof(TErrorTerm)*(Width+6), 0);
  3477. FDirection2 := 2 * Direction;
  3478. FDirection3 := 3 * Direction;
  3479. ErrorR0 := PErrors(longInt(ErrorsR0)+3*sizeof(TErrorTerm));
  3480. ErrorG0 := PErrors(longInt(ErrorsG0)+3*sizeof(TErrorTerm));
  3481. ErrorB0 := PErrors(longInt(ErrorsB0)+3*sizeof(TErrorTerm));
  3482. ErrorR1 := PErrors(longInt(ErrorsR1)+3*sizeof(TErrorTerm));
  3483. ErrorG1 := PErrors(longInt(ErrorsG1)+3*sizeof(TErrorTerm));
  3484. ErrorB1 := PErrors(longInt(ErrorsB1)+3*sizeof(TErrorTerm));
  3485. ErrorR2 := PErrors(longInt(ErrorsR2)+3*sizeof(TErrorTerm));
  3486. ErrorG2 := PErrors(longInt(ErrorsG2)+3*sizeof(TErrorTerm));
  3487. ErrorB2 := PErrors(longInt(ErrorsB2)+3*sizeof(TErrorTerm));
  3488. ErrorR3 := PErrors(longInt(ErrorsR3)+3*sizeof(TErrorTerm));
  3489. ErrorG3 := PErrors(longInt(ErrorsG3)+3*sizeof(TErrorTerm));
  3490. ErrorB3 := PErrors(longInt(ErrorsB3)+3*sizeof(TErrorTerm));
  3491. end;
  3492. destructor TSteveArcheDitherer.Destroy;
  3493. begin
  3494. FreeMem(ErrorsR0);
  3495. FreeMem(ErrorsG0);
  3496. FreeMem(ErrorsB0);
  3497. FreeMem(ErrorsR1);
  3498. FreeMem(ErrorsG1);
  3499. FreeMem(ErrorsB1);
  3500. FreeMem(ErrorsR2);
  3501. FreeMem(ErrorsG2);
  3502. FreeMem(ErrorsB2);
  3503. FreeMem(ErrorsR3);
  3504. FreeMem(ErrorsG3);
  3505. FreeMem(ErrorsB3);
  3506. inherited Destroy;
  3507. end;
  3508. {$IFOPT R+}
  3509. {$DEFINE R_PLUS}
  3510. {$RANGECHECKS OFF}
  3511. {$ENDIF}
  3512. function TSteveArcheDitherer.Dither(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  3513. var
  3514. ColorR ,
  3515. ColorG ,
  3516. ColorB : integer; // Error for current pixel
  3517. // Propagate Stevenson & Arche error terms:
  3518. // ... ... ... (here) ... 32/200 ...
  3519. // 12/200 ... 26/200 ... 30/200 ... 16/200
  3520. // ... 12/200 ... 26/200 ... 12/200 ...
  3521. // 5/200 ... 12/200 ... 12/200 ... 5/200
  3522. procedure Propagate(Errors0, Errors1, Errors2, Errors3: PErrors; Error: integer);
  3523. var
  3524. TempError : integer;
  3525. begin
  3526. if (Error = 0) then
  3527. exit;
  3528. TempError := 5 * Error;
  3529. inc(Errors3[FDirection3], TempError); // Error * 5
  3530. inc(Errors3[-FDirection3], TempError); // Error * 5
  3531. TempError := 12 * Error;
  3532. inc(Errors1[-FDirection3], TempError); // Error * 12
  3533. inc(Errors2[-FDirection2], TempError); // Error * 12
  3534. inc(Errors2[FDirection2], TempError); // Error * 12
  3535. inc(Errors3[-Direction], TempError); // Error * 12
  3536. inc(Errors3[Direction], TempError); // Error * 12
  3537. inc(Errors1[FDirection3], 16 * TempError); // Error * 16
  3538. TempError := 26 * Error;
  3539. inc(Errors1[-Direction], TempError); // Error * 26
  3540. inc(Errors2[0], TempError); // Error * 26
  3541. inc(Errors1[Direction], 30 * Error); // Error * 30
  3542. inc(Errors0[FDirection2], 32 * Error); // Error * 32
  3543. end;
  3544. begin
  3545. // Apply red component error correction
  3546. ColorR := Red + (ErrorR0[0] + 100) DIV 200;
  3547. if (ColorR < 0) then
  3548. ColorR := 0
  3549. else if (ColorR > 255) then
  3550. ColorR := 255;
  3551. // Apply green component error correction
  3552. ColorG := Green + (ErrorG0[0] + 100) DIV 200;
  3553. if (ColorG < 0) then
  3554. ColorG := 0
  3555. else if (ColorG > 255) then
  3556. ColorG := 255;
  3557. // Apply blue component error correction
  3558. ColorB := Blue + (ErrorB0[0] + 100) DIV 200;
  3559. if (ColorB < 0) then
  3560. ColorB := 0
  3561. else if (ColorB > 255) then
  3562. ColorB := 255;
  3563. // Map color to palette
  3564. Result := inherited Dither(ColorR, ColorG, ColorB, R, G, B);
  3565. // Propagate red component error
  3566. Propagate(ErrorR0, ErrorR1, ErrorR2, ErrorR3, ColorR - R);
  3567. // Propagate green component error
  3568. Propagate(ErrorG0, ErrorG1, ErrorG2, ErrorG3, ColorG - G);
  3569. // Propagate blue component error
  3570. Propagate(ErrorB0, ErrorB1, ErrorB2, ErrorB3, ColorB - B);
  3571. // Move on to next column
  3572. if (Direction = 1) then
  3573. begin
  3574. inc(longInt(ErrorR0), sizeof(TErrorTerm));
  3575. inc(longInt(ErrorG0), sizeof(TErrorTerm));
  3576. inc(longInt(ErrorB0), sizeof(TErrorTerm));
  3577. inc(longInt(ErrorR1), sizeof(TErrorTerm));
  3578. inc(longInt(ErrorG1), sizeof(TErrorTerm));
  3579. inc(longInt(ErrorB1), sizeof(TErrorTerm));
  3580. inc(longInt(ErrorR2), sizeof(TErrorTerm));
  3581. inc(longInt(ErrorG2), sizeof(TErrorTerm));
  3582. inc(longInt(ErrorB2), sizeof(TErrorTerm));
  3583. inc(longInt(ErrorR3), sizeof(TErrorTerm));
  3584. inc(longInt(ErrorG3), sizeof(TErrorTerm));
  3585. inc(longInt(ErrorB3), sizeof(TErrorTerm));
  3586. end else
  3587. begin
  3588. dec(longInt(ErrorR0), sizeof(TErrorTerm));
  3589. dec(longInt(ErrorG0), sizeof(TErrorTerm));
  3590. dec(longInt(ErrorB0), sizeof(TErrorTerm));
  3591. dec(longInt(ErrorR1), sizeof(TErrorTerm));
  3592. dec(longInt(ErrorG1), sizeof(TErrorTerm));
  3593. dec(longInt(ErrorB1), sizeof(TErrorTerm));
  3594. dec(longInt(ErrorR2), sizeof(TErrorTerm));
  3595. dec(longInt(ErrorG2), sizeof(TErrorTerm));
  3596. dec(longInt(ErrorB2), sizeof(TErrorTerm));
  3597. dec(longInt(ErrorR3), sizeof(TErrorTerm));
  3598. dec(longInt(ErrorG3), sizeof(TErrorTerm));
  3599. dec(longInt(ErrorB3), sizeof(TErrorTerm));
  3600. end;
  3601. end;
  3602. {$IFDEF R_PLUS}
  3603. {$RANGECHECKS ON}
  3604. {$UNDEF R_PLUS}
  3605. {$ENDIF}
  3606. {$IFOPT R+}
  3607. {$DEFINE R_PLUS}
  3608. {$RANGECHECKS OFF}
  3609. {$ENDIF}
  3610. procedure TSteveArcheDitherer.NextLine;
  3611. var
  3612. TempErrors : PErrors;
  3613. begin
  3614. FillChar(ErrorsR0^, sizeof(TErrorTerm)*(Width+6), 0);
  3615. FillChar(ErrorsG0^, sizeof(TErrorTerm)*(Width+6), 0);
  3616. FillChar(ErrorsB0^, sizeof(TErrorTerm)*(Width+6), 0);
  3617. // Swap lines
  3618. TempErrors := ErrorsR0;
  3619. ErrorsR0 := ErrorsR1;
  3620. ErrorsR1 := ErrorsR2;
  3621. ErrorsR2 := ErrorsR3;
  3622. ErrorsR3 := TempErrors;
  3623. TempErrors := ErrorsG0;
  3624. ErrorsG0 := ErrorsG1;
  3625. ErrorsG1 := ErrorsG2;
  3626. ErrorsG2 := ErrorsG3;
  3627. ErrorsG3 := TempErrors;
  3628. TempErrors := ErrorsB0;
  3629. ErrorsB0 := ErrorsB1;
  3630. ErrorsB1 := ErrorsB2;
  3631. ErrorsB2 := ErrorsB3;
  3632. ErrorsB3 := TempErrors;
  3633. inherited NextLine;
  3634. FDirection2 := 2 * Direction;
  3635. FDirection3 := 3 * Direction;
  3636. if (Direction = 1) then
  3637. begin
  3638. // ErrorsR0[1] gives compiler error, so we
  3639. // use PErrors(longInt(ErrorsR0)+sizeof(TErrorTerm)) instead...
  3640. ErrorR0 := PErrors(longInt(ErrorsR0)+3*sizeof(TErrorTerm));
  3641. ErrorG0 := PErrors(longInt(ErrorsG0)+3*sizeof(TErrorTerm));
  3642. ErrorB0 := PErrors(longInt(ErrorsB0)+3*sizeof(TErrorTerm));
  3643. ErrorR1 := PErrors(longInt(ErrorsR1)+3*sizeof(TErrorTerm));
  3644. ErrorG1 := PErrors(longInt(ErrorsG1)+3*sizeof(TErrorTerm));
  3645. ErrorB1 := PErrors(longInt(ErrorsB1)+3*sizeof(TErrorTerm));
  3646. ErrorR2 := PErrors(longInt(ErrorsR2)+3*sizeof(TErrorTerm));
  3647. ErrorG2 := PErrors(longInt(ErrorsG2)+3*sizeof(TErrorTerm));
  3648. ErrorB2 := PErrors(longInt(ErrorsB2)+3*sizeof(TErrorTerm));
  3649. ErrorR3 := PErrors(longInt(ErrorsR3)+3*sizeof(TErrorTerm));
  3650. ErrorG3 := PErrors(longInt(ErrorsG3)+3*sizeof(TErrorTerm));
  3651. ErrorB3 := PErrors(longInt(ErrorsB3)+3*sizeof(TErrorTerm));
  3652. end else
  3653. begin
  3654. ErrorR0 := @ErrorsR0[Width+2];
  3655. ErrorG0 := @ErrorsG0[Width+2];
  3656. ErrorB0 := @ErrorsB0[Width+2];
  3657. ErrorR1 := @ErrorsR1[Width+2];
  3658. ErrorG1 := @ErrorsG1[Width+2];
  3659. ErrorB1 := @ErrorsB1[Width+2];
  3660. ErrorR2 := @ErrorsR2[Width+2];
  3661. ErrorG2 := @ErrorsG2[Width+2];
  3662. ErrorB2 := @ErrorsB2[Width+2];
  3663. ErrorR3 := @ErrorsR2[Width+2];
  3664. ErrorG3 := @ErrorsG2[Width+2];
  3665. ErrorB3 := @ErrorsB2[Width+2];
  3666. end;
  3667. end;
  3668. {$IFDEF R_PLUS}
  3669. {$RANGECHECKS ON}
  3670. {$UNDEF R_PLUS}
  3671. {$ENDIF}
  3672. ////////////////////////////////////////////////////////////////////////////////
  3673. // TBurkesDitherer
  3674. constructor TBurkesDitherer.Create(AWidth: integer; Lookup: TColorLookup);
  3675. begin
  3676. inherited Create(AWidth, Lookup);
  3677. GetMem(ErrorsR0, sizeof(TErrorTerm)*(Width+4));
  3678. GetMem(ErrorsG0, sizeof(TErrorTerm)*(Width+4));
  3679. GetMem(ErrorsB0, sizeof(TErrorTerm)*(Width+4));
  3680. GetMem(ErrorsR1, sizeof(TErrorTerm)*(Width+4));
  3681. GetMem(ErrorsG1, sizeof(TErrorTerm)*(Width+4));
  3682. GetMem(ErrorsB1, sizeof(TErrorTerm)*(Width+4));
  3683. FillChar(ErrorsR0^, sizeof(TErrorTerm)*(Width+4), 0);
  3684. FillChar(ErrorsG0^, sizeof(TErrorTerm)*(Width+4), 0);
  3685. FillChar(ErrorsB0^, sizeof(TErrorTerm)*(Width+4), 0);
  3686. FillChar(ErrorsR1^, sizeof(TErrorTerm)*(Width+4), 0);
  3687. FillChar(ErrorsG1^, sizeof(TErrorTerm)*(Width+4), 0);
  3688. FillChar(ErrorsB1^, sizeof(TErrorTerm)*(Width+4), 0);
  3689. FDirection2 := 2 * Direction;
  3690. ErrorR0 := PErrors(longInt(ErrorsR0)+2*sizeof(TErrorTerm));
  3691. ErrorG0 := PErrors(longInt(ErrorsG0)+2*sizeof(TErrorTerm));
  3692. ErrorB0 := PErrors(longInt(ErrorsB0)+2*sizeof(TErrorTerm));
  3693. ErrorR1 := PErrors(longInt(ErrorsR1)+2*sizeof(TErrorTerm));
  3694. ErrorG1 := PErrors(longInt(ErrorsG1)+2*sizeof(TErrorTerm));
  3695. ErrorB1 := PErrors(longInt(ErrorsB1)+2*sizeof(TErrorTerm));
  3696. end;
  3697. destructor TBurkesDitherer.Destroy;
  3698. begin
  3699. FreeMem(ErrorsR0);
  3700. FreeMem(ErrorsG0);
  3701. FreeMem(ErrorsB0);
  3702. FreeMem(ErrorsR1);
  3703. FreeMem(ErrorsG1);
  3704. FreeMem(ErrorsB1);
  3705. inherited Destroy;
  3706. end;
  3707. {$IFOPT R+}
  3708. {$DEFINE R_PLUS}
  3709. {$RANGECHECKS OFF}
  3710. {$ENDIF}
  3711. function TBurkesDitherer.Dither(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  3712. var
  3713. ErrorR ,
  3714. ErrorG ,
  3715. ErrorB : integer; // Error for current pixel
  3716. // Propagate Burkes error terms:
  3717. // ... ... (here) 8/32 4/32
  3718. // 2/32 4/32 8/32 4/32 2/32
  3719. procedure Propagate(Errors0, Errors1: PErrors; Error: integer);
  3720. begin
  3721. if (Error = 0) then
  3722. exit;
  3723. inc(Error, Error);
  3724. inc(Errors1[FDirection2], Error); // Error * 2
  3725. inc(Errors1[-FDirection2], Error); // Error * 2
  3726. inc(Error, Error);
  3727. inc(Errors0[FDirection2], Error); // Error * 4
  3728. inc(Errors1[-Direction], Error); // Error * 4
  3729. inc(Errors1[Direction], Error); // Error * 4
  3730. inc(Error, Error);
  3731. inc(Errors0[Direction], Error); // Error * 8
  3732. inc(Errors1[0], Error); // Error * 8
  3733. end;
  3734. begin
  3735. // Apply red component error correction
  3736. ErrorR := Red + (ErrorR0[0] + 16) DIV 32;
  3737. if (ErrorR < 0) then
  3738. ErrorR := 0
  3739. else if (ErrorR > 255) then
  3740. ErrorR := 255;
  3741. // Apply green component error correction
  3742. ErrorG := Green + (ErrorG0[0] + 16) DIV 32;
  3743. if (ErrorG < 0) then
  3744. ErrorG := 0
  3745. else if (ErrorG > 255) then
  3746. ErrorG := 255;
  3747. // Apply blue component error correction
  3748. ErrorB := Blue + (ErrorB0[0] + 16) DIV 32;
  3749. if (ErrorB < 0) then
  3750. ErrorB := 0
  3751. else if (ErrorB > 255) then
  3752. ErrorB := 255;
  3753. // Map color to palette
  3754. Result := inherited Dither(ErrorR, ErrorG, ErrorB, R, G, B);
  3755. // Propagate red component error
  3756. Propagate(ErrorR0, ErrorR1, ErrorR - R);
  3757. // Propagate green component error
  3758. Propagate(ErrorG0, ErrorG1, ErrorG - G);
  3759. // Propagate blue component error
  3760. Propagate(ErrorB0, ErrorB1, ErrorB - B);
  3761. // Move on to next column
  3762. if (Direction = 1) then
  3763. begin
  3764. inc(longInt(ErrorR0), sizeof(TErrorTerm));
  3765. inc(longInt(ErrorG0), sizeof(TErrorTerm));
  3766. inc(longInt(ErrorB0), sizeof(TErrorTerm));
  3767. inc(longInt(ErrorR1), sizeof(TErrorTerm));
  3768. inc(longInt(ErrorG1), sizeof(TErrorTerm));
  3769. inc(longInt(ErrorB1), sizeof(TErrorTerm));
  3770. end else
  3771. begin
  3772. dec(longInt(ErrorR0), sizeof(TErrorTerm));
  3773. dec(longInt(ErrorG0), sizeof(TErrorTerm));
  3774. dec(longInt(ErrorB0), sizeof(TErrorTerm));
  3775. dec(longInt(ErrorR1), sizeof(TErrorTerm));
  3776. dec(longInt(ErrorG1), sizeof(TErrorTerm));
  3777. dec(longInt(ErrorB1), sizeof(TErrorTerm));
  3778. end;
  3779. end;
  3780. {$IFDEF R_PLUS}
  3781. {$RANGECHECKS ON}
  3782. {$UNDEF R_PLUS}
  3783. {$ENDIF}
  3784. {$IFOPT R+}
  3785. {$DEFINE R_PLUS}
  3786. {$RANGECHECKS OFF}
  3787. {$ENDIF}
  3788. procedure TBurkesDitherer.NextLine;
  3789. var
  3790. TempErrors : PErrors;
  3791. begin
  3792. FillChar(ErrorsR0^, sizeof(TErrorTerm)*(Width+4), 0);
  3793. FillChar(ErrorsG0^, sizeof(TErrorTerm)*(Width+4), 0);
  3794. FillChar(ErrorsB0^, sizeof(TErrorTerm)*(Width+4), 0);
  3795. // Swap lines
  3796. TempErrors := ErrorsR0;
  3797. ErrorsR0 := ErrorsR1;
  3798. ErrorsR1 := TempErrors;
  3799. TempErrors := ErrorsG0;
  3800. ErrorsG0 := ErrorsG1;
  3801. ErrorsG1 := TempErrors;
  3802. TempErrors := ErrorsB0;
  3803. ErrorsB0 := ErrorsB1;
  3804. ErrorsB1 := TempErrors;
  3805. inherited NextLine;
  3806. FDirection2 := 2 * Direction;
  3807. if (Direction = 1) then
  3808. begin
  3809. // ErrorsR0[1] gives compiler error, so we
  3810. // use PErrors(longInt(ErrorsR0)+sizeof(TErrorTerm)) instead...
  3811. ErrorR0 := PErrors(longInt(ErrorsR0)+2*sizeof(TErrorTerm));
  3812. ErrorG0 := PErrors(longInt(ErrorsG0)+2*sizeof(TErrorTerm));
  3813. ErrorB0 := PErrors(longInt(ErrorsB0)+2*sizeof(TErrorTerm));
  3814. ErrorR1 := PErrors(longInt(ErrorsR1)+2*sizeof(TErrorTerm));
  3815. ErrorG1 := PErrors(longInt(ErrorsG1)+2*sizeof(TErrorTerm));
  3816. ErrorB1 := PErrors(longInt(ErrorsB1)+2*sizeof(TErrorTerm));
  3817. end else
  3818. begin
  3819. ErrorR0 := @ErrorsR0[Width+1];
  3820. ErrorG0 := @ErrorsG0[Width+1];
  3821. ErrorB0 := @ErrorsB0[Width+1];
  3822. ErrorR1 := @ErrorsR1[Width+1];
  3823. ErrorG1 := @ErrorsG1[Width+1];
  3824. ErrorB1 := @ErrorsB1[Width+1];
  3825. end;
  3826. end;
  3827. {$IFDEF R_PLUS}
  3828. {$RANGECHECKS ON}
  3829. {$UNDEF R_PLUS}
  3830. {$ENDIF}
  3831. ////////////////////////////////////////////////////////////////////////////////
  3832. //
  3833. // Octree Color Quantization Engine
  3834. //
  3835. ////////////////////////////////////////////////////////////////////////////////
  3836. // Adapted from Earl F. Glynn's ColorQuantizationLibrary, March 1998
  3837. ////////////////////////////////////////////////////////////////////////////////
  3838. type
  3839. TOctreeNode = class; // Forward definition so TReducibleNodes can be declared
  3840. TReducibleNodes = array[0..7] of TOctreeNode;
  3841. TOctreeNode = Class(TObject)
  3842. public
  3843. IsLeaf : Boolean;
  3844. PixelCount : integer;
  3845. RedSum : integer;
  3846. GreenSum : integer;
  3847. BlueSum : integer;
  3848. Next : TOctreeNode;
  3849. Child : TReducibleNodes;
  3850. constructor Create(Level: integer; ColorBits: integer; var LeafCount: integer;
  3851. var ReducibleNodes: TReducibleNodes);
  3852. destructor Destroy; override;
  3853. end;
  3854. TColorQuantizer = class(TObject)
  3855. private
  3856. FTree : TOctreeNode;
  3857. FLeafCount : integer;
  3858. FReducibleNodes : TReducibleNodes;
  3859. FMaxColors : integer;
  3860. FColorBits : integer;
  3861. protected
  3862. procedure AddColor(var Node: TOctreeNode; r, g, b: byte; ColorBits: integer;
  3863. Level: integer; var LeafCount: integer; var ReducibleNodes: TReducibleNodes);
  3864. procedure DeleteTree(var Node: TOctreeNode);
  3865. procedure GetPaletteColors(const Node: TOctreeNode;
  3866. var RGBQuadArray: TRGBQuadArray; var Index: integer);
  3867. procedure ReduceTree(ColorBits: integer; var LeafCount: integer;
  3868. var ReducibleNodes: TReducibleNodes);
  3869. public
  3870. constructor Create(MaxColors: integer; ColorBits: integer);
  3871. destructor Destroy; override;
  3872. procedure GetColorTable(var RGBQuadArray: TRGBQuadArray);
  3873. function ProcessImage(const DIB: TDIBReader): boolean;
  3874. property ColorCount: integer read FLeafCount;
  3875. end;
  3876. constructor TOctreeNode.Create(Level: integer; ColorBits: integer;
  3877. var LeafCount: integer; var ReducibleNodes: TReducibleNodes);
  3878. var
  3879. i : integer;
  3880. begin
  3881. PixelCount := 0;
  3882. RedSum := 0;
  3883. GreenSum := 0;
  3884. BlueSum := 0;
  3885. for i := Low(Child) to High(Child) do
  3886. Child[i] := nil;
  3887. IsLeaf := (Level = ColorBits);
  3888. if (IsLeaf) then
  3889. begin
  3890. Next := nil;
  3891. inc(LeafCount);
  3892. end else
  3893. begin
  3894. Next := ReducibleNodes[Level];
  3895. ReducibleNodes[Level] := self;
  3896. end;
  3897. end;
  3898. destructor TOctreeNode.Destroy;
  3899. var
  3900. i : integer;
  3901. begin
  3902. for i := High(Child) downto Low(Child) do
  3903. Child[i].Free;
  3904. end;
  3905. constructor TColorQuantizer.Create(MaxColors: integer; ColorBits: integer);
  3906. var
  3907. i : integer;
  3908. begin
  3909. ASSERT(ColorBits <= 8, 'ColorBits must be 8 or less');
  3910. FTree := nil;
  3911. FLeafCount := 0;
  3912. // Initialize all nodes even though only ColorBits+1 of them are needed
  3913. for i := Low(FReducibleNodes) to High(FReducibleNodes) do
  3914. FReducibleNodes[i] := nil;
  3915. FMaxColors := MaxColors;
  3916. FColorBits := ColorBits;
  3917. end;
  3918. destructor TColorQuantizer.Destroy;
  3919. begin
  3920. if (FTree <> nil) then
  3921. DeleteTree(FTree);
  3922. end;
  3923. procedure TColorQuantizer.GetColorTable(var RGBQuadArray: TRGBQuadArray);
  3924. var
  3925. Index : integer;
  3926. begin
  3927. Index := 0;
  3928. GetPaletteColors(FTree, RGBQuadArray, Index);
  3929. end;
  3930. // Handles passed to ProcessImage should refer to DIB sections, not DDBs.
  3931. // In certain cases, specifically when it's called upon to process 1, 4, or
  3932. // 8-bit per pixel images on systems with palettized display adapters,
  3933. // ProcessImage can produce incorrect results if it's passed a handle to a
  3934. // DDB.
  3935. function TColorQuantizer.ProcessImage(const DIB: TDIBReader): boolean;
  3936. var
  3937. i ,
  3938. j : integer;
  3939. ScanLine : pointer;
  3940. Pixel : PRGBTriple;
  3941. begin
  3942. Result := True;
  3943. for j := 0 to DIB.Bitmap.Height-1 do
  3944. begin
  3945. Scanline := DIB.Scanline[j];
  3946. Pixel := ScanLine;
  3947. for i := 0 to DIB.Bitmap.Width-1 do
  3948. begin
  3949. with Pixel^ do
  3950. AddColor(FTree, rgbtRed, rgbtGreen, rgbtBlue,
  3951. FColorBits, 0, FLeafCount, FReducibleNodes);
  3952. while FLeafCount > FMaxColors do
  3953. ReduceTree(FColorbits, FLeafCount, FReducibleNodes);
  3954. inc(Pixel);
  3955. end;
  3956. end;
  3957. end;
  3958. procedure TColorQuantizer.AddColor(var Node: TOctreeNode; r,g,b: byte;
  3959. ColorBits: integer; Level: integer; var LeafCount: integer;
  3960. var ReducibleNodes: TReducibleNodes);
  3961. const
  3962. Mask: array[0..7] of BYTE = ($80, $40, $20, $10, $08, $04, $02, $01);
  3963. var
  3964. Index : integer;
  3965. Shift : integer;
  3966. begin
  3967. // If the node doesn't exist, create it.
  3968. if (Node = nil) then
  3969. Node := TOctreeNode.Create(Level, ColorBits, LeafCount, ReducibleNodes);
  3970. if (Node.IsLeaf) then
  3971. begin
  3972. inc(Node.PixelCount);
  3973. inc(Node.RedSum, r);
  3974. inc(Node.GreenSum, g);
  3975. inc(Node.BlueSum, b);
  3976. end else
  3977. begin
  3978. // Recurse a level deeper if the node is not a leaf.
  3979. Shift := 7 - Level;
  3980. Index := (((r and mask[Level]) SHR Shift) SHL 2) or
  3981. (((g and mask[Level]) SHR Shift) SHL 1) or
  3982. ((b and mask[Level]) SHR Shift);
  3983. AddColor(Node.Child[Index], r, g, b, ColorBits, Level+1, LeafCount, ReducibleNodes);
  3984. end;
  3985. end;
  3986. procedure TColorQuantizer.DeleteTree(var Node: TOctreeNode);
  3987. var
  3988. i : integer;
  3989. begin
  3990. for i := High(TReducibleNodes) downto Low(TReducibleNodes) do
  3991. if (Node.Child[i] <> nil) then
  3992. DeleteTree(Node.Child[i]);
  3993. Node.Free;
  3994. Node := nil;
  3995. end;
  3996. procedure TColorQuantizer.GetPaletteColors(const Node: TOctreeNode;
  3997. var RGBQuadArray: TRGBQuadArray; var Index: integer);
  3998. var
  3999. i : integer;
  4000. begin
  4001. if (Node.IsLeaf) then
  4002. begin
  4003. with RGBQuadArray[Index] do
  4004. begin
  4005. if (Node.PixelCount <> 0) then
  4006. begin
  4007. rgbRed := BYTE(Node.RedSum DIV Node.PixelCount);
  4008. rgbGreen := BYTE(Node.GreenSum DIV Node.PixelCount);
  4009. rgbBlue := BYTE(Node.BlueSum DIV Node.PixelCount);
  4010. end else
  4011. begin
  4012. rgbRed := 0;
  4013. rgbGreen := 0;
  4014. rgbBlue := 0;
  4015. end;
  4016. rgbReserved := 0;
  4017. end;
  4018. inc(Index);
  4019. end else
  4020. begin
  4021. for i := Low(Node.Child) to High(Node.Child) do
  4022. if (Node.Child[i] <> nil) then
  4023. GetPaletteColors(Node.Child[i], RGBQuadArray, Index);
  4024. end;
  4025. end;
  4026. procedure TColorQuantizer.ReduceTree(ColorBits: integer; var LeafCount: integer;
  4027. var ReducibleNodes: TReducibleNodes);
  4028. var
  4029. RedSum ,
  4030. GreenSum ,
  4031. BlueSum : integer;
  4032. Children : integer;
  4033. i : integer;
  4034. Node : TOctreeNode;
  4035. begin
  4036. // Find the deepest level containing at least one reducible node
  4037. i := Colorbits - 1;
  4038. while (i > 0) and (ReducibleNodes[i] = nil) do
  4039. dec(i);
  4040. // Reduce the node most recently added to the list at level i.
  4041. Node := ReducibleNodes[i];
  4042. ReducibleNodes[i] := Node.Next;
  4043. RedSum := 0;
  4044. GreenSum := 0;
  4045. BlueSum := 0;
  4046. Children := 0;
  4047. for i := Low(ReducibleNodes) to High(ReducibleNodes) do
  4048. if (Node.Child[i] <> nil) then
  4049. begin
  4050. inc(RedSum, Node.Child[i].RedSum);
  4051. inc(GreenSum, Node.Child[i].GreenSum);
  4052. inc(BlueSum, Node.Child[i].BlueSum);
  4053. inc(Node.PixelCount, Node.Child[i].PixelCount);
  4054. Node.Child[i].Free;
  4055. Node.Child[i] := nil;
  4056. inc(Children);
  4057. end;
  4058. Node.IsLeaf := TRUE;
  4059. Node.RedSum := RedSum;
  4060. Node.GreenSum := GreenSum;
  4061. Node.BlueSum := BlueSum;
  4062. dec(LeafCount, Children-1);
  4063. end;
  4064. ////////////////////////////////////////////////////////////////////////////////
  4065. //
  4066. // Octree Color Quantization Wrapper
  4067. //
  4068. ////////////////////////////////////////////////////////////////////////////////
  4069. // Adapted from Earl F. Glynn's PaletteLibrary, March 1998
  4070. ////////////////////////////////////////////////////////////////////////////////
  4071. // Wrapper for internal use - uses TDIBReader for bitmap access
  4072. function doCreateOptimizedPaletteFromSingleBitmap(const DIB: TDIBReader;
  4073. Colors, ColorBits: integer; Windows: boolean): hPalette;
  4074. var
  4075. SystemPalette : HPalette;
  4076. ColorQuantizer : TColorQuantizer;
  4077. i : integer;
  4078. LogicalPalette : TMaxLogPalette;
  4079. RGBQuadArray : TRGBQuadArray;
  4080. Offset : integer;
  4081. begin
  4082. LogicalPalette.palVersion := $0300;
  4083. LogicalPalette.palNumEntries := Colors;
  4084. // 2003.03.06 ->
  4085. {reset palette to black}
  4086. FillChar(LogicalPalette.palPalEntry, SizeOf(LogicalPalette.palPalEntry), 0);
  4087. for i := 0 to 255 do
  4088. LogicalPalette.palPalEntry[i].peFlags := PC_NOCOLLAPSE;
  4089. // 2003.03.06 <-
  4090. if (Windows) then
  4091. begin
  4092. // Get the windows 20 color system palette
  4093. SystemPalette := GetStockObject(DEFAULT_PALETTE);
  4094. GetPaletteEntries(SystemPalette, 0, 10, LogicalPalette.palPalEntry[0]);
  4095. //GetPaletteEntries(SystemPalette, 10, 10, LogicalPalette.palPalEntry[245]); // wrong offset
  4096. GetPaletteEntries(SystemPalette, 10, 10, LogicalPalette.palPalEntry[246]); // 2003.03.06
  4097. Colors := 236;
  4098. Offset := 10;
  4099. LogicalPalette.palNumEntries := 256;
  4100. { Test code
  4101. // 2003.03.06 ->
  4102. // Get the windows 20 color system palette
  4103. SystemPalette := GetStockObject(DEFAULT_PALETTE);
  4104. GetPaletteEntries(SystemPalette, 0, 10, LogicalPalette.palPalEntry[0]);
  4105. GetPaletteEntries(SystemPalette, 10, 10, LogicalPalette.palPalEntry[10]);
  4106. Colors := 236;
  4107. Offset := 20;
  4108. LogicalPalette.palNumEntries := 256;
  4109. // 2003.03.06 <-
  4110. }
  4111. end else
  4112. Offset := 0;
  4113. // Normally for 24-bit images, use ColorBits of 5 or 6. For 8-bit images
  4114. // use ColorBits = 8.
  4115. ColorQuantizer := TColorQuantizer.Create(Colors, ColorBits);
  4116. try
  4117. ColorQuantizer.ProcessImage(DIB);
  4118. ColorQuantizer.GetColorTable(RGBQuadArray);
  4119. finally
  4120. ColorQuantizer.Free;
  4121. end;
  4122. for i := 0 to Colors-1 do
  4123. with LogicalPalette.palPalEntry[i+Offset] do
  4124. begin
  4125. peRed := RGBQuadArray[i].rgbRed;
  4126. peGreen := RGBQuadArray[i].rgbGreen;
  4127. peBlue := RGBQuadArray[i].rgbBlue;
  4128. peFlags := RGBQuadArray[i].rgbReserved;
  4129. end;
  4130. Result := CreatePalette(pLogPalette(@LogicalPalette)^);
  4131. end;
  4132. function CreateOptimizedPaletteFromSingleBitmap(const Bitmap: TBitmap;
  4133. Colors, ColorBits: integer; Windows: boolean): hPalette;
  4134. var
  4135. DIB : TDIBReader;
  4136. begin
  4137. DIB := TDIBReader.Create(Bitmap, pf24bit);
  4138. try
  4139. Result := doCreateOptimizedPaletteFromSingleBitmap(DIB, Colors, ColorBits, Windows);
  4140. finally
  4141. DIB.Free;
  4142. end;
  4143. end;
  4144. function CreateOptimizedPaletteFromManyBitmaps(Bitmaps: TList; Colors, ColorBits: integer;
  4145. Windows: boolean): hPalette;
  4146. var
  4147. SystemPalette : HPalette;
  4148. ColorQuantizer : TColorQuantizer;
  4149. i : integer;
  4150. LogicalPalette : TMaxLogPalette;
  4151. RGBQuadArray : TRGBQuadArray;
  4152. Offset : integer;
  4153. DIB : TDIBReader;
  4154. begin
  4155. if (Bitmaps = nil) or (Bitmaps.Count = 0) then
  4156. Error(sInvalidBitmapList);
  4157. LogicalPalette.palVersion := $0300;
  4158. LogicalPalette.palNumEntries := Colors;
  4159. // 2003.03.06 ->
  4160. {reset palette to black}
  4161. FillChar(LogicalPalette.palPalEntry, SizeOf(LogicalPalette.palPalEntry), 0);
  4162. for i := 0 to 255 do
  4163. LogicalPalette.palPalEntry[i].peFlags := PC_NOCOLLAPSE;
  4164. // 2003.03.06 <-
  4165. if (Windows) then
  4166. begin
  4167. // Get the windows 20 color system palette
  4168. SystemPalette := GetStockObject(DEFAULT_PALETTE);
  4169. GetPaletteEntries(SystemPalette, 0, 10, LogicalPalette.palPalEntry[0]);
  4170. //GetPaletteEntries(SystemPalette, 10, 10, LogicalPalette.palPalEntry[245]); // wrong offset
  4171. GetPaletteEntries(SystemPalette, 10, 10, LogicalPalette.palPalEntry[246]); // 2003.03.06
  4172. Colors := 236;
  4173. Offset := 10;
  4174. LogicalPalette.palNumEntries := 256;
  4175. { Test code
  4176. // 2003.03.06 ->
  4177. // Get the windows 20 color system palette
  4178. SystemPalette := GetStockObject(DEFAULT_PALETTE);
  4179. GetPaletteEntries(SystemPalette, 0, 10, LogicalPalette.palPalEntry[0]);
  4180. GetPaletteEntries(SystemPalette, 10, 10, LogicalPalette.palPalEntry[10]);
  4181. Colors := 236;
  4182. Offset := 20;
  4183. LogicalPalette.palNumEntries := 256;
  4184. // 2003.03.06 <-
  4185. }
  4186. end else
  4187. Offset := 0;
  4188. // Normally for 24-bit images, use ColorBits of 5 or 6. For 8-bit images
  4189. // use ColorBits = 8.
  4190. ColorQuantizer := TColorQuantizer.Create(Colors, ColorBits);
  4191. try
  4192. for i := 0 to Bitmaps.Count-1 do
  4193. begin
  4194. DIB := TDIBReader.Create(TBitmap(Bitmaps[i]), pf24bit);
  4195. try
  4196. ColorQuantizer.ProcessImage(DIB);
  4197. finally
  4198. DIB.Free;
  4199. end;
  4200. end;
  4201. ColorQuantizer.GetColorTable(RGBQuadArray);
  4202. finally
  4203. ColorQuantizer.Free;
  4204. end;
  4205. for i := 0 to Colors-1 do
  4206. with LogicalPalette.palPalEntry[i+Offset] do
  4207. begin
  4208. peRed := RGBQuadArray[i].rgbRed;
  4209. peGreen := RGBQuadArray[i].rgbGreen;
  4210. peBlue := RGBQuadArray[i].rgbBlue;
  4211. peFlags := RGBQuadArray[i].rgbReserved;
  4212. end;
  4213. Result := CreatePalette(pLogPalette(@LogicalPalette)^);
  4214. end;
  4215. ////////////////////////////////////////////////////////////////////////////////
  4216. //
  4217. // Color reduction
  4218. //
  4219. ////////////////////////////////////////////////////////////////////////////////
  4220. {$IFOPT R+}
  4221. {$DEFINE R_PLUS}
  4222. {$RANGECHECKS OFF}
  4223. {$ENDIF}
  4224. //: Reduces the color depth of a bitmap using color quantization and dithering.
  4225. function ReduceColors(Bitmap: TBitmap; ColorReduction: TColorReduction;
  4226. DitherMode: TDitherMode; ReductionBits: integer; CustomPalette: hPalette): TBitmap;
  4227. var
  4228. Palette : hPalette;
  4229. ColorLookup : TColorLookup;
  4230. Ditherer : TDitherEngine;
  4231. Row : Integer;
  4232. DIBResult : TDIBWriter;
  4233. DIBSource : TDIBReader;
  4234. SrcScanLine ,
  4235. Src : PRGBTriple;
  4236. DstScanLine ,
  4237. Dst : PChar;
  4238. BGR : TRGBTriple;
  4239. {$ifdef DEBUG_DITHERPERFORMANCE}
  4240. TimeStart ,
  4241. TimeStop : DWORD;
  4242. {$endif}
  4243. function GrayScalePalette: hPalette;
  4244. var
  4245. i : integer;
  4246. Pal : TMaxLogPalette;
  4247. begin
  4248. Pal.palVersion := $0300;
  4249. Pal.palNumEntries := 256;
  4250. for i := 0 to 255 do
  4251. begin
  4252. with (Pal.palPalEntry[i]) do
  4253. begin
  4254. peRed := i;
  4255. peGreen := i;
  4256. peBlue := i;
  4257. peFlags := PC_NOCOLLAPSE;
  4258. end;
  4259. end;
  4260. Result := CreatePalette(pLogPalette(@Pal)^);
  4261. end;
  4262. function MonochromePalette: hPalette;
  4263. var
  4264. i : integer;
  4265. Pal : TMaxLogPalette;
  4266. const
  4267. Values : array[0..1] of byte
  4268. = (0, 255);
  4269. begin
  4270. Pal.palVersion := $0300;
  4271. Pal.palNumEntries := 2;
  4272. for i := 0 to 1 do
  4273. begin
  4274. with (Pal.palPalEntry[i]) do
  4275. begin
  4276. peRed := Values[i];
  4277. peGreen := Values[i];
  4278. peBlue := Values[i];
  4279. peFlags := PC_NOCOLLAPSE;
  4280. end;
  4281. end;
  4282. Result := CreatePalette(pLogPalette(@Pal)^);
  4283. end;
  4284. function WindowsGrayScalePalette: hPalette;
  4285. var
  4286. i : integer;
  4287. Pal : TMaxLogPalette;
  4288. const
  4289. Values : array[0..3] of byte
  4290. = (0, 128, 192, 255);
  4291. begin
  4292. Pal.palVersion := $0300;
  4293. Pal.palNumEntries := 4;
  4294. for i := 0 to 3 do
  4295. begin
  4296. with (Pal.palPalEntry[i]) do
  4297. begin
  4298. peRed := Values[i];
  4299. peGreen := Values[i];
  4300. peBlue := Values[i];
  4301. peFlags := PC_NOCOLLAPSE;
  4302. end;
  4303. end;
  4304. Result := CreatePalette(pLogPalette(@Pal)^);
  4305. end;
  4306. function WindowsHalftonePalette: hPalette;
  4307. var
  4308. DC : HDC;
  4309. begin
  4310. DC := GDICheck(GetDC(0));
  4311. try
  4312. Result := CreateHalfTonePalette(DC);
  4313. finally
  4314. ReleaseDC(0, DC);
  4315. end;
  4316. end;
  4317. begin
  4318. {$ifdef DEBUG_DITHERPERFORMANCE}
  4319. timeBeginPeriod(5);
  4320. TimeStart := timeGetTime;
  4321. {$endif}
  4322. Result := TBitmap.Create;
  4323. try
  4324. if (ColorReduction = rmNone) then
  4325. begin
  4326. Result.Assign(Bitmap);
  4327. {$ifndef VER9x}
  4328. SetPixelFormat(Result, pf24bit);
  4329. {$endif}
  4330. exit;
  4331. end;
  4332. {$IFNDEF VER9x}
  4333. if (Bitmap.Width*Bitmap.Height > BitmapAllocationThreshold) then
  4334. SetPixelFormat(Result, pf1bit); // To reduce resource consumption of resize
  4335. {$ENDIF}
  4336. ColorLookup := nil;
  4337. Ditherer := nil;
  4338. DIBResult := nil;
  4339. DIBSource := nil;
  4340. Palette := 0;
  4341. try // Protect above resources
  4342. // Dithering and color mapper only supports 24 bit bitmaps,
  4343. // so we have convert the source bitmap to the appropiate format.
  4344. DIBSource := TDIBReader.Create(Bitmap, pf24bit);
  4345. // Create a palette based on current options
  4346. case (ColorReduction) of
  4347. rmQuantize:
  4348. Palette := doCreateOptimizedPaletteFromSingleBitmap(DIBSource, 1 SHL ReductionBits, 8, False);
  4349. rmQuantizeWindows:
  4350. Palette := CreateOptimizedPaletteFromSingleBitmap(Bitmap, 256, 8, True);
  4351. rmNetscape:
  4352. Palette := WebPalette;
  4353. rmGrayScale:
  4354. Palette := GrayScalePalette;
  4355. rmMonochrome:
  4356. Palette := MonochromePalette;
  4357. rmWindowsGray:
  4358. Palette := WindowsGrayScalePalette;
  4359. rmWindows20:
  4360. Palette := GetStockObject(DEFAULT_PALETTE);
  4361. rmWindows256:
  4362. Palette := WindowsHalftonePalette;
  4363. rmPalette:
  4364. Palette := CopyPalette(CustomPalette);
  4365. else
  4366. exit;
  4367. end;
  4368. { TODO -oanme -cImprovement : Gray scale conversion should be done prior to dithering/mapping. Otherwise corrected values will be converted multiple times. }
  4369. // Create a color mapper based on current options
  4370. case (ColorReduction) of
  4371. // For some strange reason my fast and dirty color lookup
  4372. // is more precise that Windows GetNearestPaletteIndex...
  4373. // rmWindows20:
  4374. // ColorLookup := TSlowColorLookup.Create(Palette);
  4375. // rmWindowsGray:
  4376. // ColorLookup := TGrayWindowsLookup.Create(Palette);
  4377. rmQuantize:
  4378. // ColorLookup := TFastColorLookup.Create(Palette);
  4379. ColorLookup := TSlowColorLookup.Create(Palette); // 2003-03-06
  4380. rmNetscape:
  4381. ColorLookup := TNetscapeColorLookup.Create(Palette);
  4382. rmGrayScale:
  4383. ColorLookup := TGrayScaleLookup.Create(Palette);
  4384. rmMonochrome:
  4385. ColorLookup := TMonochromeLookup.Create(Palette);
  4386. else
  4387. // ColorLookup := TFastColorLookup.Create(Palette);
  4388. ColorLookup := TSlowColorLookup.Create(Palette); // 2003-03-06
  4389. end;
  4390. // Nothing to do if palette doesn't contain any colors
  4391. if (ColorLookup.Colors = 0) then
  4392. exit;
  4393. // Create a ditherer based on current options
  4394. case (DitherMode) of
  4395. dmNearest:
  4396. Ditherer := TDitherEngine.Create(Bitmap.Width, ColorLookup);
  4397. dmFloydSteinberg:
  4398. Ditherer := TFloydSteinbergDitherer.Create(Bitmap.Width, ColorLookup);
  4399. dmStucki:
  4400. Ditherer := TStuckiDitherer.Create(Bitmap.Width, ColorLookup);
  4401. dmSierra:
  4402. Ditherer := TSierraDitherer.Create(Bitmap.Width, ColorLookup);
  4403. dmJaJuNI:
  4404. Ditherer := TJaJuNIDitherer.Create(Bitmap.Width, ColorLookup);
  4405. dmSteveArche:
  4406. Ditherer := TSteveArcheDitherer.Create(Bitmap.Width, ColorLookup);
  4407. dmBurkes:
  4408. Ditherer := TBurkesDitherer.Create(Bitmap.Width, ColorLookup);
  4409. else
  4410. exit;
  4411. end;
  4412. // The processed bitmap is returned in pf8bit format
  4413. DIBResult := TDIBWriter.Create(Result, pf8bit, Bitmap.Width, Bitmap.Height,
  4414. Palette);
  4415. // Process the image
  4416. Row := 0;
  4417. while (Row < Bitmap.Height) do
  4418. begin
  4419. SrcScanline := DIBSource.ScanLine[Row];
  4420. DstScanline := DIBResult.ScanLine[Row];
  4421. Src := pointer(longInt(SrcScanLine) + Ditherer.Column*sizeof(TRGBTriple));
  4422. Dst := pointer(longInt(DstScanLine) + Ditherer.Column);
  4423. while (Ditherer.Column < Ditherer.Width) and (Ditherer.Column >= 0) do
  4424. begin
  4425. BGR := Src^;
  4426. // Dither and map a single pixel
  4427. Dst^ := Ditherer.Dither(BGR.rgbtRed, BGR.rgbtGreen, BGR.rgbtBlue,
  4428. BGR.rgbtRed, BGR.rgbtGreen, BGR.rgbtBlue);
  4429. inc(Src, Ditherer.Direction);
  4430. inc(Dst, Ditherer.Direction);
  4431. end;
  4432. Inc(Row);
  4433. Ditherer.NextLine;
  4434. end;
  4435. finally
  4436. if (ColorLookup <> nil) then
  4437. ColorLookup.Free;
  4438. if (Ditherer <> nil) then
  4439. Ditherer.Free;
  4440. if (DIBResult <> nil) then
  4441. DIBResult.Free;
  4442. if (DIBSource <> nil) then
  4443. DIBSource.Free;
  4444. // Must delete palette after TDIBWriter since TDIBWriter uses palette
  4445. if (Palette <> 0) then
  4446. DeleteObject(Palette);
  4447. end;
  4448. except
  4449. Result.Free;
  4450. raise;
  4451. end;
  4452. {$ifdef DEBUG_DITHERPERFORMANCE}
  4453. TimeStop := timeGetTime;
  4454. ShowMessage(format('Dithered %d pixels in %d mS, Rate %d pixels/mS (%d pixels/S)',
  4455. [Bitmap.Height*Bitmap.Width, TimeStop-TimeStart,
  4456. MulDiv(Bitmap.Height, Bitmap.Width, TimeStop-TimeStart+1),
  4457. MulDiv(Bitmap.Height, Bitmap.Width * 1000, TimeStop-TimeStart+1)]));
  4458. timeEndPeriod(5);
  4459. {$endif}
  4460. end;
  4461. {$IFDEF R_PLUS}
  4462. {$RANGECHECKS ON}
  4463. {$UNDEF R_PLUS}
  4464. {$ENDIF}
  4465. ////////////////////////////////////////////////////////////////////////////////
  4466. //
  4467. // TGIFColorMap
  4468. //
  4469. ////////////////////////////////////////////////////////////////////////////////
  4470. const
  4471. InitColorMapSize = 16;
  4472. DeltaColorMapSize = 32;
  4473. //: Creates an instance of a TGIFColorMap object.
  4474. constructor TGIFColorMap.Create;
  4475. begin
  4476. inherited Create;
  4477. FColorMap := nil;
  4478. FCapacity := 0;
  4479. FCount := 0;
  4480. FOptimized := False;
  4481. end;
  4482. //: Destroys an instance of a TGIFColorMap object.
  4483. destructor TGIFColorMap.Destroy;
  4484. begin
  4485. Clear;
  4486. Changed;
  4487. inherited Destroy;
  4488. end;
  4489. //: Empties the color map.
  4490. procedure TGIFColorMap.Clear;
  4491. begin
  4492. if (FColorMap <> nil) then
  4493. FreeMem(FColorMap);
  4494. FColorMap := nil;
  4495. FCapacity := 0;
  4496. FCount := 0;
  4497. FOptimized := False;
  4498. end;
  4499. //: Converts a Windows color value to a RGB value.
  4500. class function TGIFColorMap.Color2RGB(Color: TColor): TGIFColor;
  4501. begin
  4502. Result.Blue := (Color shr 16) and $FF;
  4503. Result.Green := (Color shr 8) and $FF;
  4504. Result.Red := Color and $FF;
  4505. end;
  4506. //: Converts a RGB value to a Windows color value.
  4507. class function TGIFColorMap.RGB2Color(Color: TGIFColor): TColor;
  4508. begin
  4509. Result := (Color.Blue SHL 16) OR (Color.Green SHL 8) OR Color.Red;
  4510. end;
  4511. //: Saves the color map to a stream.
  4512. procedure TGIFColorMap.SaveToStream(Stream: TStream);
  4513. var
  4514. Dummies : integer;
  4515. Dummy : TGIFColor;
  4516. begin
  4517. if (FCount = 0) then
  4518. exit;
  4519. Stream.WriteBuffer(FColorMap^, FCount*sizeof(TGIFColor));
  4520. Dummies := (1 SHL BitsPerPixel)-FCount;
  4521. Dummy.Red := 0;
  4522. Dummy.Green := 0;
  4523. Dummy.Blue := 0;
  4524. while (Dummies > 0) do
  4525. begin
  4526. Stream.WriteBuffer(Dummy, sizeof(TGIFColor));
  4527. dec(Dummies);
  4528. end;
  4529. end;
  4530. //: Loads the color map from a stream.
  4531. procedure TGIFColorMap.LoadFromStream(Stream: TStream; Count: integer);
  4532. begin
  4533. Clear;
  4534. SetCapacity(Count);
  4535. ReadCheck(Stream, FColorMap^, Count*sizeof(TGIFColor));
  4536. FCount := Count;
  4537. end;
  4538. //: Returns the position of a color in the color map.
  4539. function TGIFColorMap.IndexOf(Color: TColor): integer;
  4540. var
  4541. RGB : TGIFColor;
  4542. begin
  4543. RGB := Color2RGB(Color);
  4544. if (FOptimized) then
  4545. begin
  4546. // Optimized palette has most frequently occuring entries first
  4547. Result := 0;
  4548. // Reverse search to (hopefully) check latest colors first
  4549. while (Result < FCount) do
  4550. with (FColorMap^[Result]) do
  4551. begin
  4552. if (RGB.Red = Red) and (RGB.Green = Green) and (RGB.Blue = Blue) then
  4553. exit;
  4554. Inc(Result);
  4555. end;
  4556. Result := -1;
  4557. end else
  4558. begin
  4559. Result := FCount-1;
  4560. // Reverse search to (hopefully) check latest colors first
  4561. while (Result >= 0) do
  4562. with (FColorMap^[Result]) do
  4563. begin
  4564. if (RGB.Red = Red) and (RGB.Green = Green) and (RGB.Blue = Blue) then
  4565. exit;
  4566. Dec(Result);
  4567. end;
  4568. end;
  4569. end;
  4570. procedure TGIFColorMap.SetCapacity(Size: integer);
  4571. begin
  4572. if (Size >= FCapacity) then
  4573. begin
  4574. if (Size <= InitColorMapSize) then
  4575. FCapacity := InitColorMapSize
  4576. else
  4577. FCapacity := (Size + DeltaColorMapSize - 1) DIV DeltaColorMapSize * DeltaColorMapSize;
  4578. if (FCapacity > GIFMaxColors) then
  4579. FCapacity := GIFMaxColors;
  4580. ReallocMem(FColorMap, FCapacity * sizeof(TGIFColor));
  4581. end;
  4582. end;
  4583. //: Imports a Windows palette into the color map.
  4584. procedure TGIFColorMap.ImportPalette(Palette: HPalette);
  4585. type
  4586. PalArray = array[byte] of TPaletteEntry;
  4587. var
  4588. Pal : PalArray;
  4589. NewCount : integer;
  4590. i : integer;
  4591. begin
  4592. Clear;
  4593. NewCount := GetPaletteEntries(Palette, 0, 256, pal);
  4594. if (NewCount = 0) then
  4595. exit;
  4596. SetCapacity(NewCount);
  4597. for i := 0 to NewCount-1 do
  4598. with FColorMap[i], Pal[i] do
  4599. begin
  4600. Red := peRed;
  4601. Green := peGreen;
  4602. Blue := peBlue;
  4603. end;
  4604. FCount := NewCount;
  4605. Changed;
  4606. end;
  4607. //: Imports a color map structure into the color map.
  4608. procedure TGIFColorMap.ImportColorMap(Map: TColorMap; Count: integer);
  4609. begin
  4610. Clear;
  4611. if (Count = 0) then
  4612. exit;
  4613. SetCapacity(Count);
  4614. FCount := Count;
  4615. System.Move(Map, FColorMap^, FCount * sizeof(TGIFColor));
  4616. Changed;
  4617. end;
  4618. //: Imports a Windows palette structure into the color map.
  4619. procedure TGIFColorMap.ImportColorTable(Pal: pointer; Count: integer);
  4620. var
  4621. i : integer;
  4622. begin
  4623. Clear;
  4624. if (Count = 0) then
  4625. exit;
  4626. SetCapacity(Count);
  4627. for i := 0 to Count-1 do
  4628. with FColorMap[i], PRGBQuadArray(Pal)[i] do
  4629. begin
  4630. Red := rgbRed;
  4631. Green := rgbGreen;
  4632. Blue := rgbBlue;
  4633. end;
  4634. FCount := Count;
  4635. Changed;
  4636. end;
  4637. //: Imports the color table of a DIB into the color map.
  4638. procedure TGIFColorMap.ImportDIBColors(Handle: HDC);
  4639. var
  4640. Pal : Pointer;
  4641. NewCount : integer;
  4642. begin
  4643. Clear;
  4644. GetMem(Pal, sizeof(TRGBQuad) * 256);
  4645. try
  4646. NewCount := GetDIBColorTable(Handle, 0, 256, Pal^);
  4647. ImportColorTable(Pal, NewCount);
  4648. finally
  4649. FreeMem(Pal);
  4650. end;
  4651. Changed;
  4652. end;
  4653. //: Creates a Windows palette from the color map.
  4654. function TGIFColorMap.ExportPalette: HPalette;
  4655. var
  4656. Pal : TMaxLogPalette;
  4657. i : Integer;
  4658. begin
  4659. if (Count = 0) then
  4660. begin
  4661. Result := 0;
  4662. exit;
  4663. end;
  4664. Pal.palVersion := $300;
  4665. Pal.palNumEntries := Count;
  4666. for i := 0 to Count-1 do
  4667. with FColorMap[i], Pal.palPalEntry[i] do
  4668. begin
  4669. peRed := Red;
  4670. peGreen := Green;
  4671. peBlue := Blue;
  4672. peFlags := PC_NOCOLLAPSE; { TODO -oanme -cImprovement : Verify that PC_NOCOLLAPSE is the correct value to use. }
  4673. end;
  4674. Result := CreatePalette(PLogPalette(@Pal)^);
  4675. end;
  4676. //: Adds a color to the color map.
  4677. function TGIFColorMap.Add(Color: TColor): integer;
  4678. begin
  4679. if (FCount >= GIFMaxColors) then
  4680. // Color map full
  4681. Error(sTooManyColors);
  4682. Result := FCount;
  4683. if (Result >= FCapacity) then
  4684. SetCapacity(FCount+1);
  4685. FColorMap^[FCount] := Color2RGB(Color);
  4686. inc(FCount);
  4687. FOptimized := False;
  4688. Changed;
  4689. end;
  4690. function TGIFColorMap.AddUnique(Color: TColor): integer;
  4691. begin
  4692. // Look up color before add (same as IndexOf)
  4693. Result := IndexOf(Color);
  4694. if (Result >= 0) then
  4695. // Color already in map
  4696. exit;
  4697. Result := Add(Color);
  4698. end;
  4699. //: Removes a color from the color map.
  4700. procedure TGIFColorMap.Delete(Index: integer);
  4701. begin
  4702. if (Index < 0) or (Index >= FCount) then
  4703. // Color index out of range
  4704. Error(sBadColorIndex);
  4705. dec(FCount);
  4706. if (Index < FCount) then
  4707. System.Move(FColorMap^[Index + 1], FColorMap^[Index], (FCount - Index)* sizeof(TGIFColor));
  4708. FOptimized := False;
  4709. Changed;
  4710. end;
  4711. function TGIFColorMap.GetColor(Index: integer): TColor;
  4712. begin
  4713. if (Index < 0) or (Index >= FCount) then
  4714. begin
  4715. // Color index out of range
  4716. Warning(gsWarning, sBadColorIndex);
  4717. // Raise an exception if the color map is empty
  4718. if (FCount = 0) then
  4719. Error(sEmptyColorMap);
  4720. // Default to color index 0
  4721. Index := 0;
  4722. end;
  4723. Result := RGB2Color(FColorMap^[Index]);
  4724. end;
  4725. procedure TGIFColorMap.SetColor(Index: integer; Value: TColor);
  4726. begin
  4727. if (Index < 0) or (Index >= FCount) then
  4728. // Color index out of range
  4729. Error(sBadColorIndex);
  4730. FColorMap^[Index] := Color2RGB(Value);
  4731. Changed;
  4732. end;
  4733. function TGIFColorMap.DoOptimize: boolean;
  4734. var
  4735. Usage : TColormapHistogram;
  4736. TempMap : array[0..255] of TGIFColor;
  4737. ReverseMap : TColormapReverse;
  4738. i : integer;
  4739. LastFound : boolean;
  4740. NewCount : integer;
  4741. T : TUsageCount;
  4742. Pivot : integer;
  4743. procedure QuickSort(iLo, iHi: Integer);
  4744. var
  4745. Lo, Hi: Integer;
  4746. begin
  4747. repeat
  4748. Lo := iLo;
  4749. Hi := iHi;
  4750. Pivot := Usage[(iLo + iHi) SHR 1].Count;
  4751. repeat
  4752. while (Usage[Lo].Count - Pivot > 0) do inc(Lo);
  4753. while (Usage[Hi].Count - Pivot < 0) do dec(Hi);
  4754. if (Lo <= Hi) then
  4755. begin
  4756. T := Usage[Lo];
  4757. Usage[Lo] := Usage[Hi];
  4758. Usage[Hi] := T;
  4759. inc(Lo);
  4760. dec(Hi);
  4761. end;
  4762. until (Lo > Hi);
  4763. if (iLo < Hi) then
  4764. QuickSort(iLo, Hi);
  4765. iLo := Lo;
  4766. until (Lo >= iHi);
  4767. end;
  4768. begin
  4769. if (FCount <= 1) then
  4770. begin
  4771. Result := False;
  4772. exit;
  4773. end;
  4774. FOptimized := True;
  4775. Result := True;
  4776. BuildHistogram(Usage);
  4777. (*
  4778. ** Sort according to usage count
  4779. *)
  4780. QuickSort(0, FCount-1);
  4781. (*
  4782. ** Test for table already sorted
  4783. *)
  4784. for i := 0 to FCount-1 do
  4785. if (Usage[i].Index <> i) then
  4786. break;
  4787. if (i = FCount) then
  4788. exit;
  4789. (*
  4790. ** Build old to new map
  4791. *)
  4792. for i := 0 to FCount-1 do
  4793. ReverseMap[Usage[i].Index] := i;
  4794. MapImages(ReverseMap);
  4795. (*
  4796. ** Reorder colormap
  4797. *)
  4798. LastFound := False;
  4799. NewCount := FCount;
  4800. Move(FColorMap^, TempMap, FCount * sizeof(TGIFColor));
  4801. for i := 0 to FCount-1 do
  4802. begin
  4803. FColorMap^[ReverseMap[i]] := TempMap[i];
  4804. // Find last used color index
  4805. if (Usage[i].Count = 0) and not(LastFound) then
  4806. begin
  4807. LastFound := True;
  4808. NewCount := i;
  4809. end;
  4810. end;
  4811. FCount := NewCount;
  4812. Changed;
  4813. end;
  4814. function TGIFColorMap.GetBitsPerPixel: integer;
  4815. begin
  4816. Result := Colors2bpp(FCount);
  4817. end;
  4818. //: Copies one color map to another.
  4819. procedure TGIFColorMap.Assign(Source: TPersistent);
  4820. begin
  4821. if (Source is TGIFColorMap) then
  4822. begin
  4823. Clear;
  4824. FCapacity := TGIFColorMap(Source).FCapacity;
  4825. FCount := TGIFColorMap(Source).FCount;
  4826. FOptimized := TGIFColorMap(Source).FOptimized;
  4827. FColorMap := AllocMem(FCapacity * sizeof(TGIFColor));
  4828. System.Move(TGIFColorMap(Source).FColorMap^, FColorMap^, FCount * sizeof(TGIFColor));
  4829. Changed;
  4830. end else
  4831. inherited Assign(Source);
  4832. end;
  4833. ////////////////////////////////////////////////////////////////////////////////
  4834. //
  4835. // TGIFItem
  4836. //
  4837. ////////////////////////////////////////////////////////////////////////////////
  4838. constructor TGIFItem.Create(GIFImage: TGIFImage);
  4839. begin
  4840. inherited Create;
  4841. FGIFImage := GIFImage;
  4842. end;
  4843. procedure TGIFItem.Warning(Severity: TGIFSeverity; Message: string);
  4844. begin
  4845. FGIFImage.Warning(self, Severity, Message);
  4846. end;
  4847. function TGIFItem.GetVersion: TGIFVersion;
  4848. begin
  4849. Result := gv87a;
  4850. end;
  4851. procedure TGIFItem.LoadFromFile(const Filename: string);
  4852. var
  4853. Stream: TStream;
  4854. begin
  4855. Stream := TFileStream.Create(Filename, fmOpenRead OR fmShareDenyWrite);
  4856. try
  4857. LoadFromStream(Stream);
  4858. finally
  4859. Stream.Free;
  4860. end;
  4861. end;
  4862. procedure TGIFItem.SaveToFile(const Filename: string);
  4863. var
  4864. Stream: TStream;
  4865. begin
  4866. Stream := TFileStream.Create(Filename, fmCreate);
  4867. try
  4868. SaveToStream(Stream);
  4869. finally
  4870. Stream.Free;
  4871. end;
  4872. end;
  4873. ////////////////////////////////////////////////////////////////////////////////
  4874. //
  4875. // TGIFList
  4876. //
  4877. ////////////////////////////////////////////////////////////////////////////////
  4878. constructor TGIFList.Create(Image: TGIFImage);
  4879. begin
  4880. inherited Create;
  4881. FImage := Image;
  4882. FItems := TList.Create;
  4883. end;
  4884. destructor TGIFList.Destroy;
  4885. begin
  4886. Clear;
  4887. FItems.Free;
  4888. inherited Destroy;
  4889. end;
  4890. function TGIFList.GetItem(Index: Integer): TGIFItem;
  4891. begin
  4892. Result := TGIFItem(FItems[Index]);
  4893. end;
  4894. procedure TGIFList.SetItem(Index: Integer; Item: TGIFItem);
  4895. begin
  4896. FItems[Index] := Item;
  4897. end;
  4898. function TGIFList.GetCount: Integer;
  4899. begin
  4900. Result := FItems.Count;
  4901. end;
  4902. function TGIFList.Add(Item: TGIFItem): Integer;
  4903. begin
  4904. Result := FItems.Add(Item);
  4905. end;
  4906. procedure TGIFList.Clear;
  4907. begin
  4908. while (FItems.Count > 0) do
  4909. Delete(0);
  4910. end;
  4911. procedure TGIFList.Delete(Index: Integer);
  4912. var
  4913. Item : TGIFItem;
  4914. begin
  4915. Item := TGIFItem(FItems[Index]);
  4916. // Delete before item is destroyed to avoid recursion
  4917. FItems.Delete(Index);
  4918. Item.Free;
  4919. end;
  4920. procedure TGIFList.Exchange(Index1, Index2: Integer);
  4921. begin
  4922. FItems.Exchange(Index1, Index2);
  4923. end;
  4924. function TGIFList.First: TGIFItem;
  4925. begin
  4926. Result := TGIFItem(FItems.First);
  4927. end;
  4928. function TGIFList.IndexOf(Item: TGIFItem): Integer;
  4929. begin
  4930. Result := FItems.IndexOf(Item);
  4931. end;
  4932. procedure TGIFList.Insert(Index: Integer; Item: TGIFItem);
  4933. begin
  4934. FItems.Insert(Index, Item);
  4935. end;
  4936. function TGIFList.Last: TGIFItem;
  4937. begin
  4938. Result := TGIFItem(FItems.Last);
  4939. end;
  4940. procedure TGIFList.Move(CurIndex, NewIndex: Integer);
  4941. begin
  4942. FItems.Move(CurIndex, NewIndex);
  4943. end;
  4944. function TGIFList.Remove(Item: TGIFItem): Integer;
  4945. begin
  4946. // Note: TGIFList.Remove must not destroy item
  4947. Result := FItems.Remove(Item);
  4948. end;
  4949. procedure TGIFList.SaveToStream(Stream: TStream);
  4950. var
  4951. i : integer;
  4952. begin
  4953. for i := 0 to FItems.Count-1 do
  4954. TGIFItem(FItems[i]).SaveToStream(Stream);
  4955. end;
  4956. procedure TGIFList.Warning(Severity: TGIFSeverity; Message: string);
  4957. begin
  4958. Image.Warning(self, Severity, Message);
  4959. end;
  4960. ////////////////////////////////////////////////////////////////////////////////
  4961. //
  4962. // TGIFGlobalColorMap
  4963. //
  4964. ////////////////////////////////////////////////////////////////////////////////
  4965. type
  4966. TGIFGlobalColorMap = class(TGIFColorMap)
  4967. private
  4968. FHeader : TGIFHeader;
  4969. protected
  4970. procedure Warning(Severity: TGIFSeverity; Message: string); override;
  4971. procedure BuildHistogram(var Histogram: TColormapHistogram); override;
  4972. procedure MapImages(var Map: TColormapReverse); override;
  4973. public
  4974. constructor Create(HeaderItem: TGIFHeader);
  4975. function Optimize: boolean; override;
  4976. procedure Changed; override;
  4977. end;
  4978. constructor TGIFGlobalColorMap.Create(HeaderItem: TGIFHeader);
  4979. begin
  4980. Inherited Create;
  4981. FHeader := HeaderItem;
  4982. end;
  4983. procedure TGIFGlobalColorMap.Warning(Severity: TGIFSeverity; Message: string);
  4984. begin
  4985. FHeader.Image.Warning(self, Severity, Message);
  4986. end;
  4987. procedure TGIFGlobalColorMap.BuildHistogram(var Histogram: TColormapHistogram);
  4988. var
  4989. Pixel ,
  4990. LastPixel : PChar;
  4991. i : integer;
  4992. begin
  4993. (*
  4994. ** Init histogram
  4995. *)
  4996. for i := 0 to Count-1 do
  4997. begin
  4998. Histogram[i].Index := i;
  4999. Histogram[i].Count := 0;
  5000. end;
  5001. for i := 0 to FHeader.Image.Images.Count-1 do
  5002. if (FHeader.Image.Images[i].ActiveColorMap = self) then
  5003. begin
  5004. Pixel := FHeader.Image.Images[i].Data;
  5005. LastPixel := Pixel + FHeader.Image.Images[i].Width * FHeader.Image.Images[i].Height;
  5006. (*
  5007. ** Sum up usage count for each color
  5008. *)
  5009. while (Pixel < LastPixel) do
  5010. begin
  5011. inc(Histogram[ord(Pixel^)].Count);
  5012. inc(Pixel);
  5013. end;
  5014. end;
  5015. end;
  5016. procedure TGIFGlobalColorMap.MapImages(var Map: TColormapReverse);
  5017. var
  5018. Pixel ,
  5019. LastPixel : PChar;
  5020. i : integer;
  5021. begin
  5022. for i := 0 to FHeader.Image.Images.Count-1 do
  5023. if (FHeader.Image.Images[i].ActiveColorMap = self) then
  5024. begin
  5025. Pixel := FHeader.Image.Images[i].Data;
  5026. LastPixel := Pixel + FHeader.Image.Images[i].Width * FHeader.Image.Images[i].Height;
  5027. (*
  5028. ** Reorder all pixel to new map
  5029. *)
  5030. while (Pixel < LastPixel) do
  5031. begin
  5032. Pixel^ := chr(Map[ord(Pixel^)]);
  5033. inc(Pixel);
  5034. end;
  5035. (*
  5036. ** Reorder transparent colors
  5037. *)
  5038. if (FHeader.Image.Images[i].Transparent) then
  5039. FHeader.Image.Images[i].GraphicControlExtension.TransparentColorIndex :=
  5040. Map[FHeader.Image.Images[i].GraphicControlExtension.TransparentColorIndex];
  5041. end;
  5042. end;
  5043. function TGIFGlobalColorMap.Optimize: boolean;
  5044. begin
  5045. { Optimize with first image, Remove unused colors if only one image }
  5046. if (FHeader.Image.Images.Count > 0) then
  5047. Result := DoOptimize
  5048. else
  5049. Result := False;
  5050. end;
  5051. procedure TGIFGlobalColorMap.Changed;
  5052. begin
  5053. FHeader.Image.Palette := 0;
  5054. end;
  5055. ////////////////////////////////////////////////////////////////////////////////
  5056. //
  5057. // TGIFHeader
  5058. //
  5059. ////////////////////////////////////////////////////////////////////////////////
  5060. constructor TGIFHeader.Create(GIFImage: TGIFImage);
  5061. begin
  5062. inherited Create(GIFImage);
  5063. FColorMap := TGIFGlobalColorMap.Create(self);
  5064. Clear;
  5065. end;
  5066. destructor TGIFHeader.Destroy;
  5067. begin
  5068. FColorMap.Free;
  5069. inherited Destroy;
  5070. end;
  5071. procedure TGIFHeader.Clear;
  5072. begin
  5073. FColorMap.Clear;
  5074. FLogicalScreenDescriptor.ScreenWidth := 0;
  5075. FLogicalScreenDescriptor.ScreenHeight := 0;
  5076. FLogicalScreenDescriptor.PackedFields := 0;
  5077. FLogicalScreenDescriptor.BackgroundColorIndex := 0;
  5078. FLogicalScreenDescriptor.AspectRatio := 0;
  5079. end;
  5080. procedure TGIFHeader.Assign(Source: TPersistent);
  5081. begin
  5082. if (Source is TGIFHeader) then
  5083. begin
  5084. ColorMap.Assign(TGIFHeader(Source).ColorMap);
  5085. FLogicalScreenDescriptor := TGIFHeader(Source).FLogicalScreenDescriptor;
  5086. end else
  5087. if (Source is TGIFColorMap) then
  5088. begin
  5089. Clear;
  5090. ColorMap.Assign(TGIFColorMap(Source));
  5091. end else
  5092. inherited Assign(Source);
  5093. end;
  5094. type
  5095. TGIFHeaderRec = packed record
  5096. Signature: array[0..2] of char; { contains 'GIF' }
  5097. Version: TGIFVersionRec; { '87a' or '89a' }
  5098. end;
  5099. const
  5100. { logical screen descriptor packed field masks }
  5101. lsdGlobalColorTable = $80; { set if global color table follows L.S.D. }
  5102. lsdColorResolution = $70; { Color resolution - 3 bits }
  5103. lsdSort = $08; { set if global color table is sorted - 1 bit }
  5104. lsdColorTableSize = $07; { size of global color table - 3 bits }
  5105. { Actual size = 2^value+1 - value is 3 bits }
  5106. procedure TGIFHeader.Prepare;
  5107. var
  5108. pack : BYTE;
  5109. begin
  5110. Pack := $00;
  5111. if (ColorMap.Count > 0) then
  5112. begin
  5113. Pack := lsdGlobalColorTable;
  5114. if (ColorMap.Optimized) then
  5115. Pack := Pack OR lsdSort;
  5116. end;
  5117. // Note: The SHL below was SHL 5 in the original source, but that looks wrong
  5118. Pack := Pack OR ((Image.ColorResolution SHL 4) AND lsdColorResolution);
  5119. Pack := Pack OR ((Image.BitsPerPixel-1) AND lsdColorTableSize);
  5120. FLogicalScreenDescriptor.PackedFields := Pack;
  5121. end;
  5122. procedure TGIFHeader.SaveToStream(Stream: TStream);
  5123. var
  5124. GifHeader : TGIFHeaderRec;
  5125. v : TGIFVersion;
  5126. begin
  5127. v := Image.Version;
  5128. if (v = gvUnknown) then
  5129. Error(sBadVersion);
  5130. GifHeader.Signature := 'GIF';
  5131. GifHeader.Version := GIFVersions[v];
  5132. Prepare;
  5133. Stream.Write(GifHeader, sizeof(GifHeader));
  5134. Stream.Write(FLogicalScreenDescriptor, sizeof(FLogicalScreenDescriptor));
  5135. if (FLogicalScreenDescriptor.PackedFields AND lsdGlobalColorTable = lsdGlobalColorTable) then
  5136. ColorMap.SaveToStream(Stream);
  5137. end;
  5138. procedure TGIFHeader.LoadFromStream(Stream: TStream);
  5139. var
  5140. GifHeader : TGIFHeaderRec;
  5141. ColorCount : integer;
  5142. Position : integer;
  5143. begin
  5144. Position := Stream.Position;
  5145. ReadCheck(Stream, GifHeader, sizeof(GifHeader));
  5146. if (uppercase(GifHeader.Signature) <> 'GIF') then
  5147. begin
  5148. // Attempt recovery in case we are reading a GIF stored in a form by rxLib
  5149. Stream.Position := Position;
  5150. // Seek past size stored in stream
  5151. Stream.Seek(sizeof(longInt), soFromCurrent);
  5152. // Attempt to read signature again
  5153. ReadCheck(Stream, GifHeader, sizeof(GifHeader));
  5154. if (uppercase(GifHeader.Signature) <> 'GIF') then
  5155. Error(sBadSignature);
  5156. end;
  5157. ReadCheck(Stream, FLogicalScreenDescriptor, sizeof(FLogicalScreenDescriptor));
  5158. if (FLogicalScreenDescriptor.PackedFields AND lsdGlobalColorTable = lsdGlobalColorTable) then
  5159. begin
  5160. ColorCount := 2 SHL (FLogicalScreenDescriptor.PackedFields AND lsdColorTableSize);
  5161. if (ColorCount < 2) or (ColorCount > 256) then
  5162. Error(sScreenBadColorSize);
  5163. ColorMap.LoadFromStream(Stream, ColorCount)
  5164. end else
  5165. ColorMap.Clear;
  5166. end;
  5167. function TGIFHeader.GetVersion: TGIFVersion;
  5168. begin
  5169. if (FColorMap.Optimized) or (AspectRatio <> 0) then
  5170. Result := gv89a
  5171. else
  5172. Result := inherited GetVersion;
  5173. end;
  5174. function TGIFHeader.GetBackgroundColor: TColor;
  5175. begin
  5176. Result := FColorMap[BackgroundColorIndex];
  5177. end;
  5178. procedure TGIFHeader.SetBackgroundColor(Color: TColor);
  5179. begin
  5180. BackgroundColorIndex := FColorMap.AddUnique(Color);
  5181. end;
  5182. procedure TGIFHeader.SetBackgroundColorIndex(Index: BYTE);
  5183. begin
  5184. if ((Index >= FColorMap.Count) and (FColorMap.Count > 0)) then
  5185. begin
  5186. Warning(gsWarning, sBadColorIndex);
  5187. Index := 0;
  5188. end;
  5189. FLogicalScreenDescriptor.BackgroundColorIndex := Index;
  5190. end;
  5191. function TGIFHeader.GetBitsPerPixel: integer;
  5192. begin
  5193. Result := FColorMap.BitsPerPixel;
  5194. end;
  5195. function TGIFHeader.GetColorResolution: integer;
  5196. begin
  5197. Result := FColorMap.BitsPerPixel-1;
  5198. end;
  5199. ////////////////////////////////////////////////////////////////////////////////
  5200. //
  5201. // TGIFLocalColorMap
  5202. //
  5203. ////////////////////////////////////////////////////////////////////////////////
  5204. type
  5205. TGIFLocalColorMap = class(TGIFColorMap)
  5206. private
  5207. FSubImage : TGIFSubImage;
  5208. protected
  5209. procedure Warning(Severity: TGIFSeverity; Message: string); override;
  5210. procedure BuildHistogram(var Histogram: TColormapHistogram); override;
  5211. procedure MapImages(var Map: TColormapReverse); override;
  5212. public
  5213. constructor Create(SubImage: TGIFSubImage);
  5214. function Optimize: boolean; override;
  5215. procedure Changed; override;
  5216. end;
  5217. constructor TGIFLocalColorMap.Create(SubImage: TGIFSubImage);
  5218. begin
  5219. Inherited Create;
  5220. FSubImage := SubImage;
  5221. end;
  5222. procedure TGIFLocalColorMap.Warning(Severity: TGIFSeverity; Message: string);
  5223. begin
  5224. FSubImage.Image.Warning(self, Severity, Message);
  5225. end;
  5226. procedure TGIFLocalColorMap.BuildHistogram(var Histogram: TColormapHistogram);
  5227. var
  5228. Pixel ,
  5229. LastPixel : PChar;
  5230. i : integer;
  5231. begin
  5232. Pixel := FSubImage.Data;
  5233. LastPixel := Pixel + FSubImage.Width * FSubImage.Height;
  5234. (*
  5235. ** Init histogram
  5236. *)
  5237. for i := 0 to Count-1 do
  5238. begin
  5239. Histogram[i].Index := i;
  5240. Histogram[i].Count := 0;
  5241. end;
  5242. (*
  5243. ** Sum up usage count for each color
  5244. *)
  5245. while (Pixel < LastPixel) do
  5246. begin
  5247. inc(Histogram[ord(Pixel^)].Count);
  5248. inc(Pixel);
  5249. end;
  5250. end;
  5251. procedure TGIFLocalColorMap.MapImages(var Map: TColormapReverse);
  5252. var
  5253. Pixel ,
  5254. LastPixel : PChar;
  5255. begin
  5256. Pixel := FSubImage.Data;
  5257. LastPixel := Pixel + FSubImage.Width * FSubImage.Height;
  5258. (*
  5259. ** Reorder all pixel to new map
  5260. *)
  5261. while (Pixel < LastPixel) do
  5262. begin
  5263. Pixel^ := chr(Map[ord(Pixel^)]);
  5264. inc(Pixel);
  5265. end;
  5266. (*
  5267. ** Reorder transparent colors
  5268. *)
  5269. if (FSubImage.Transparent) then
  5270. FSubImage.GraphicControlExtension.TransparentColorIndex :=
  5271. Map[FSubImage.GraphicControlExtension.TransparentColorIndex];
  5272. end;
  5273. function TGIFLocalColorMap.Optimize: boolean;
  5274. begin
  5275. Result := DoOptimize;
  5276. end;
  5277. procedure TGIFLocalColorMap.Changed;
  5278. begin
  5279. FSubImage.Palette := 0;
  5280. end;
  5281. ////////////////////////////////////////////////////////////////////////////////
  5282. //
  5283. // LZW Decoder
  5284. //
  5285. ////////////////////////////////////////////////////////////////////////////////
  5286. const
  5287. GIFCodeBits = 12; // Max number of bits per GIF token code
  5288. GIFCodeMax = (1 SHL GIFCodeBits)-1;// Max GIF token code
  5289. // 12 bits = 4095
  5290. StackSize = (2 SHL GIFCodeBits); // Size of decompression stack
  5291. TableSize = (1 SHL GIFCodeBits); // Size of decompression table
  5292. procedure TGIFSubImage.Decompress(Stream: TStream);
  5293. var
  5294. table0 : array[0..TableSize-1] of integer;
  5295. table1 : array[0..TableSize-1] of integer;
  5296. firstcode, oldcode : integer;
  5297. buf : array[0..257] of BYTE;
  5298. Dest : PChar;
  5299. v ,
  5300. xpos, ypos, pass : integer;
  5301. stack : array[0..StackSize-1] of integer;
  5302. Source : ^integer;
  5303. BitsPerCode : integer; // number of CodeTableBits/code
  5304. InitialBitsPerCode : BYTE;
  5305. MaxCode : integer; // maximum code, given BitsPerCode
  5306. MaxCodeSize : integer;
  5307. ClearCode : integer; // Special code to signal "Clear table"
  5308. EOFCode : integer; // Special code to signal EOF
  5309. step : integer;
  5310. i : integer;
  5311. StartBit , // Index of bit buffer start
  5312. LastBit , // Index of last bit in buffer
  5313. LastByte : integer; // Index of last byte in buffer
  5314. get_done ,
  5315. return_clear ,
  5316. ZeroBlock : boolean;
  5317. ClearValue : BYTE;
  5318. {$ifdef DEBUG_DECOMPRESSPERFORMANCE}
  5319. TimeStartDecompress ,
  5320. TimeStopDecompress : DWORD;
  5321. {$endif}
  5322. function nextCode(BitsPerCode: integer): integer;
  5323. const
  5324. masks: array[0..15] of integer =
  5325. ($0000, $0001, $0003, $0007,
  5326. $000f, $001f, $003f, $007f,
  5327. $00ff, $01ff, $03ff, $07ff,
  5328. $0fff, $1fff, $3fff, $7fff);
  5329. var
  5330. StartIndex, EndIndex : integer;
  5331. ret : integer;
  5332. EndBit : integer;
  5333. count : BYTE;
  5334. begin
  5335. if (return_clear) then
  5336. begin
  5337. return_clear := False;
  5338. Result := ClearCode;
  5339. exit;
  5340. end;
  5341. EndBit := StartBit + BitsPerCode;
  5342. if (EndBit >= LastBit) then
  5343. begin
  5344. if (get_done) then
  5345. begin
  5346. if (StartBit >= LastBit) then
  5347. Warning(gsWarning, sDecodeTooFewBits);
  5348. Result := -1;
  5349. exit;
  5350. end;
  5351. buf[0] := buf[LastByte-2];
  5352. buf[1] := buf[LastByte-1];
  5353. if (Stream.Read(count, 1) <> 1) then
  5354. begin
  5355. Result := -1;
  5356. exit;
  5357. end;
  5358. if (count = 0) then
  5359. begin
  5360. ZeroBlock := True;
  5361. get_done := TRUE;
  5362. end else
  5363. begin
  5364. // Handle premature end of file
  5365. if (Stream.Size - Stream.Position < Count) then
  5366. begin
  5367. Warning(gsWarning, sOutOfData);
  5368. // Not enough data left - Just read as much as we can get
  5369. Count := Stream.Size - Stream.Position;
  5370. end;
  5371. if (Count <> 0) then
  5372. ReadCheck(Stream, Buf[2], Count);
  5373. end;
  5374. LastByte := 2 + count;
  5375. StartBit := (StartBit - LastBit) + 16;
  5376. LastBit := LastByte * 8;
  5377. EndBit := StartBit + BitsPerCode;
  5378. end;
  5379. EndIndex := EndBit DIV 8;
  5380. StartIndex := StartBit DIV 8;
  5381. ASSERT(StartIndex <= high(buf), 'StartIndex too large');
  5382. if (StartIndex = EndIndex) then
  5383. ret := buf[StartIndex]
  5384. else
  5385. if (StartIndex + 1 = EndIndex) then
  5386. ret := buf[StartIndex] OR (buf[StartIndex+1] SHL 8)
  5387. else
  5388. ret := buf[StartIndex] OR (buf[StartIndex+1] SHL 8) OR (buf[StartIndex+2] SHL 16);
  5389. ret := (ret SHR (StartBit AND $0007)) AND masks[BitsPerCode];
  5390. Inc(StartBit, BitsPerCode);
  5391. Result := ret;
  5392. end;
  5393. function NextLZW: integer;
  5394. var
  5395. code, incode : integer;
  5396. i : integer;
  5397. b : BYTE;
  5398. begin
  5399. code := nextCode(BitsPerCode);
  5400. while (code >= 0) do
  5401. begin
  5402. if (code = ClearCode) then
  5403. begin
  5404. ASSERT(ClearCode < TableSize, 'ClearCode too large');
  5405. for i := 0 to ClearCode-1 do
  5406. begin
  5407. table0[i] := 0;
  5408. table1[i] := i;
  5409. end;
  5410. for i := ClearCode to TableSize-1 do
  5411. begin
  5412. table0[i] := 0;
  5413. table1[i] := 0;
  5414. end;
  5415. BitsPerCode := InitialBitsPerCode+1;
  5416. MaxCodeSize := 2 * ClearCode;
  5417. MaxCode := ClearCode + 2;
  5418. Source := @stack;
  5419. repeat
  5420. firstcode := nextCode(BitsPerCode);
  5421. oldcode := firstcode;
  5422. until (firstcode <> ClearCode);
  5423. Result := firstcode;
  5424. exit;
  5425. end;
  5426. if (code = EOFCode) then
  5427. begin
  5428. Result := -2;
  5429. if (ZeroBlock) then
  5430. exit;
  5431. // Eat rest of data blocks
  5432. if (Stream.Read(b, 1) <> 1) then
  5433. exit;
  5434. while (b <> 0) do
  5435. begin
  5436. Stream.Seek(b, soFromCurrent);
  5437. if (Stream.Read(b, 1) <> 1) then
  5438. exit;
  5439. end;
  5440. exit;
  5441. end;
  5442. incode := code;
  5443. if (code >= MaxCode) then
  5444. begin
  5445. Source^ := firstcode;
  5446. Inc(Source);
  5447. code := oldcode;
  5448. end;
  5449. ASSERT(Code < TableSize, 'Code too large');
  5450. while (code >= ClearCode) do
  5451. begin
  5452. Source^ := table1[code];
  5453. Inc(Source);
  5454. if (code = table0[code]) then
  5455. Error(sDecodeCircular);
  5456. code := table0[code];
  5457. ASSERT(Code < TableSize, 'Code too large');
  5458. end;
  5459. firstcode := table1[code];
  5460. Source^ := firstcode;
  5461. Inc(Source);
  5462. code := MaxCode;
  5463. if (code <= GIFCodeMax) then
  5464. begin
  5465. table0[code] := oldcode;
  5466. table1[code] := firstcode;
  5467. Inc(MaxCode);
  5468. if ((MaxCode >= MaxCodeSize) and (MaxCodeSize <= GIFCodeMax)) then
  5469. begin
  5470. MaxCodeSize := MaxCodeSize * 2;
  5471. Inc(BitsPerCode);
  5472. end;
  5473. end;
  5474. oldcode := incode;
  5475. if (longInt(Source) > longInt(@stack)) then
  5476. begin
  5477. Dec(Source);
  5478. Result := Source^;
  5479. exit;
  5480. end
  5481. end;
  5482. Result := code;
  5483. end;
  5484. function readLZW: integer;
  5485. begin
  5486. if (longInt(Source) > longInt(@stack)) then
  5487. begin
  5488. Dec(Source);
  5489. Result := Source^;
  5490. end else
  5491. Result := NextLZW;
  5492. end;
  5493. begin
  5494. NewImage;
  5495. // Clear image data in case decompress doesn't complete
  5496. if (Transparent) then
  5497. // Clear to transparent color
  5498. ClearValue := GraphicControlExtension.GetTransparentColorIndex
  5499. else
  5500. // Clear to first color
  5501. ClearValue := 0;
  5502. FillChar(FData^, FDataSize, ClearValue);
  5503. {$ifdef DEBUG_DECOMPRESSPERFORMANCE}
  5504. TimeStartDecompress := timeGetTime;
  5505. {$endif}
  5506. (*
  5507. ** Read initial code size in bits from stream
  5508. *)
  5509. if (Stream.Read(InitialBitsPerCode, 1) <> 1) then
  5510. exit;
  5511. (*
  5512. ** Initialize the Compression routines
  5513. *)
  5514. BitsPerCode := InitialBitsPerCode + 1;
  5515. ClearCode := 1 SHL InitialBitsPerCode;
  5516. EOFCode := ClearCode + 1;
  5517. MaxCodeSize := 2 * ClearCode;
  5518. MaxCode := ClearCode + 2;
  5519. StartBit := 0;
  5520. LastBit := 0;
  5521. LastByte := 2;
  5522. ZeroBlock := False;
  5523. get_done := False;
  5524. return_clear := TRUE;
  5525. Source := @stack;
  5526. try
  5527. if (Interlaced) then
  5528. begin
  5529. ypos := 0;
  5530. pass := 0;
  5531. step := 8;
  5532. for i := 0 to Height-1 do
  5533. begin
  5534. Dest := FData + Width * ypos;
  5535. for xpos := 0 to width-1 do
  5536. begin
  5537. v := readLZW;
  5538. if (v < 0) then
  5539. exit;
  5540. Dest^ := char(v);
  5541. Inc(Dest);
  5542. end;
  5543. Inc(ypos, step);
  5544. if (ypos >= height) then
  5545. repeat
  5546. if (pass > 0) then
  5547. step := step DIV 2;
  5548. Inc(pass);
  5549. ypos := step DIV 2;
  5550. until (ypos < height);
  5551. end;
  5552. end else
  5553. begin
  5554. Dest := FData;
  5555. for ypos := 0 to (height * width)-1 do
  5556. begin
  5557. v := readLZW;
  5558. if (v < 0) then
  5559. exit;
  5560. Dest^ := char(v);
  5561. Inc(Dest);
  5562. end;
  5563. end;
  5564. finally
  5565. if (readLZW >= 0) then
  5566. ;
  5567. // raise GIFException.Create('Too much input data, ignoring extra...');
  5568. end;
  5569. {$ifdef DEBUG_DECOMPRESSPERFORMANCE}
  5570. TimeStopDecompress := timeGetTime;
  5571. ShowMessage(format('Decompressed %d pixels in %d mS, Rate %d pixels/mS',
  5572. [Height*Width, TimeStopDecompress-TimeStartDecompress,
  5573. (Height*Width) DIV (TimeStopDecompress-TimeStartDecompress+1)]));
  5574. {$endif}
  5575. end;
  5576. ////////////////////////////////////////////////////////////////////////////////
  5577. //
  5578. // LZW Encoder stuff
  5579. //
  5580. ////////////////////////////////////////////////////////////////////////////////
  5581. ////////////////////////////////////////////////////////////////////////////////
  5582. // LZW Encoder THashTable
  5583. ////////////////////////////////////////////////////////////////////////////////
  5584. const
  5585. HashKeyBits = 13; // Max number of bits per Hash Key
  5586. HashSize = 8009; // Size of hash table
  5587. // Must be prime
  5588. // Must be > than HashMaxCode
  5589. // Must be < than HashMaxKey
  5590. HashKeyMax = (1 SHL HashKeyBits)-1;// Max hash key value
  5591. // 13 bits = 8191
  5592. HashKeyMask = HashKeyMax; // $1FFF
  5593. GIFCodeMask = GIFCodeMax; // $0FFF
  5594. HashEmpty = $000FFFFF; // 20 bits
  5595. type
  5596. // A Hash Key is 20 bits wide.
  5597. // - The lower 8 bits are the postfix character (the new pixel).
  5598. // - The upper 12 bits are the prefix code (the GIF token).
  5599. // A KeyInt must be able to represent the integer values -1..(2^20)-1
  5600. KeyInt = longInt; // 32 bits
  5601. CodeInt = SmallInt; // 16 bits
  5602. THashArray = array[0..HashSize-1] of KeyInt;
  5603. PHashArray = ^THashArray;
  5604. THashTable = class
  5605. {$ifdef DEBUG_HASHPERFORMANCE}
  5606. CountLookupFound : longInt;
  5607. CountMissFound : longInt;
  5608. CountLookupNotFound : longInt;
  5609. CountMissNotFound : longInt;
  5610. {$endif}
  5611. HashTable: PHashArray;
  5612. public
  5613. constructor Create;
  5614. destructor Destroy; override;
  5615. procedure Clear;
  5616. procedure Insert(Key: KeyInt; Code: CodeInt);
  5617. function Lookup(Key: KeyInt): CodeInt;
  5618. end;
  5619. function HashKey(Key: KeyInt): CodeInt;
  5620. begin
  5621. Result := ((Key SHR (GIFCodeBits-8)) XOR Key) MOD HashSize;
  5622. end;
  5623. function NextHashKey(HKey: CodeInt): CodeInt;
  5624. var
  5625. disp : CodeInt;
  5626. begin
  5627. (*
  5628. ** secondary hash (after G. Knott)
  5629. *)
  5630. disp := HashSize - HKey;
  5631. if (HKey = 0) then
  5632. disp := 1;
  5633. // disp := 13; // disp should be prime relative to HashSize, but
  5634. // it doesn't seem to matter here...
  5635. dec(HKey, disp);
  5636. if (HKey < 0) then
  5637. inc(HKey, HashSize);
  5638. Result := HKey;
  5639. end;
  5640. constructor THashTable.Create;
  5641. begin
  5642. ASSERT(longInt($FFFFFFFF) = -1, 'TGIFImage implementation assumes $FFFFFFFF = -1');
  5643. inherited Create;
  5644. GetMem(HashTable, sizeof(THashArray));
  5645. Clear;
  5646. {$ifdef DEBUG_HASHPERFORMANCE}
  5647. CountLookupFound := 0;
  5648. CountMissFound := 0;
  5649. CountLookupNotFound := 0;
  5650. CountMissNotFound := 0;
  5651. {$endif}
  5652. end;
  5653. destructor THashTable.Destroy;
  5654. begin
  5655. {$ifdef DEBUG_HASHPERFORMANCE}
  5656. ShowMessage(
  5657. Format('Found: %d HitRate: %.2f',
  5658. [CountLookupFound, (CountLookupFound+1)/(CountMissFound+1)])+#13+
  5659. Format('Not found: %d HitRate: %.2f',
  5660. [CountLookupNotFound, (CountLookupNotFound+1)/(CountMissNotFound+1)]));
  5661. {$endif}
  5662. FreeMem(HashTable);
  5663. inherited Destroy;
  5664. end;
  5665. // Clear hash table and fill with empty slots (doh!)
  5666. procedure THashTable.Clear;
  5667. {$ifdef DEBUG_HASHFILLFACTOR}
  5668. var
  5669. i ,
  5670. Count : longInt;
  5671. {$endif}
  5672. begin
  5673. {$ifdef DEBUG_HASHFILLFACTOR}
  5674. Count := 0;
  5675. for i := 0 to HashSize-1 do
  5676. if (HashTable[i] SHR GIFCodeBits <> HashEmpty) then
  5677. inc(Count);
  5678. ShowMessage(format('Size: %d, Filled: %d, Rate %.4f',
  5679. [HashSize, Count, Count/HashSize]));
  5680. {$endif}
  5681. FillChar(HashTable^, sizeof(THashArray), $FF);
  5682. end;
  5683. // Insert new key/value pair into hash table
  5684. procedure THashTable.Insert(Key: KeyInt; Code: CodeInt);
  5685. var
  5686. HKey : CodeInt;
  5687. begin
  5688. // Create hash key from prefix string
  5689. HKey := HashKey(Key);
  5690. // Scan for empty slot
  5691. // while (HashTable[HKey] SHR GIFCodeBits <> HashEmpty) do { Unoptimized }
  5692. while (HashTable[HKey] AND (HashEmpty SHL GIFCodeBits) <> (HashEmpty SHL GIFCodeBits)) do { Optimized }
  5693. HKey := NextHashKey(HKey);
  5694. // Fill slot with key/value pair
  5695. HashTable[HKey] := (Key SHL GIFCodeBits) OR (Code AND GIFCodeMask);
  5696. end;
  5697. // Search for key in hash table.
  5698. // Returns value if found or -1 if not
  5699. function THashTable.Lookup(Key: KeyInt): CodeInt;
  5700. var
  5701. HKey : CodeInt;
  5702. HTKey : KeyInt;
  5703. {$ifdef DEBUG_HASHPERFORMANCE}
  5704. n : LongInt;
  5705. {$endif}
  5706. begin
  5707. // Create hash key from prefix string
  5708. HKey := HashKey(Key);
  5709. {$ifdef DEBUG_HASHPERFORMANCE}
  5710. n := 0;
  5711. {$endif}
  5712. // Scan table for key
  5713. // HTKey := HashTable[HKey] SHR GIFCodeBits; { Unoptimized }
  5714. Key := Key SHL GIFCodeBits; { Optimized }
  5715. HTKey := HashTable[HKey] AND (HashEmpty SHL GIFCodeBits); { Optimized }
  5716. // while (HTKey <> HashEmpty) do { Unoptimized }
  5717. while (HTKey <> HashEmpty SHL GIFCodeBits) do { Optimized }
  5718. begin
  5719. if (Key = HTKey) then
  5720. begin
  5721. // Extract and return value
  5722. Result := HashTable[HKey] AND GIFCodeMask;
  5723. {$ifdef DEBUG_HASHPERFORMANCE}
  5724. inc(CountLookupFound);
  5725. inc(CountMissFound, n);
  5726. {$endif}
  5727. exit;
  5728. end;
  5729. {$ifdef DEBUG_HASHPERFORMANCE}
  5730. inc(n);
  5731. {$endif}
  5732. // Try next slot
  5733. HKey := NextHashKey(HKey);
  5734. // HTKey := HashTable[HKey] SHR GIFCodeBits; { Unoptimized }
  5735. HTKey := HashTable[HKey] AND (HashEmpty SHL GIFCodeBits); { Optimized }
  5736. end;
  5737. // Found empty slot - key doesn't exist
  5738. Result := -1;
  5739. {$ifdef DEBUG_HASHPERFORMANCE}
  5740. inc(CountLookupNotFound);
  5741. inc(CountMissNotFound, n);
  5742. {$endif}
  5743. end;
  5744. ////////////////////////////////////////////////////////////////////////////////
  5745. // TGIFStream - Abstract GIF block stream
  5746. //
  5747. // Descendants from TGIFStream either reads or writes data in blocks
  5748. // of up to 255 bytes. These blocks are organized as a leading byte
  5749. // containing the number of bytes in the block (exclusing the count
  5750. // byte itself), followed by the data (up to 254 bytes of data).
  5751. ////////////////////////////////////////////////////////////////////////////////
  5752. type
  5753. TGIFStream = class(TStream)
  5754. private
  5755. FOnWarning : TGIFWarning;
  5756. FStream : TStream;
  5757. FOnProgress : TNotifyEvent;
  5758. FBuffer : array [BYTE] of Char;
  5759. FBufferCount : integer;
  5760. protected
  5761. constructor Create(Stream: TStream);
  5762. function Read(var Buffer; Count: Longint): Longint; override;
  5763. function Write(const Buffer; Count: Longint): Longint; override;
  5764. function Seek(Offset: Longint; Origin: Word): Longint; override;
  5765. procedure Progress(Sender: TObject); dynamic;
  5766. property OnProgress: TNotifyEvent read FOnProgress write FOnProgress;
  5767. public
  5768. property Warning: TGIFWarning read FOnWarning write FOnWarning;
  5769. end;
  5770. constructor TGIFStream.Create(Stream: TStream);
  5771. begin
  5772. inherited Create;
  5773. FStream := Stream;
  5774. FBufferCount := 1; // Reserve first byte of buffer for length
  5775. end;
  5776. procedure TGIFStream.Progress(Sender: TObject);
  5777. begin
  5778. if Assigned(FOnProgress) then
  5779. FOnProgress(Sender);
  5780. end;
  5781. function TGIFStream.Write(const Buffer; Count: Longint): Longint;
  5782. begin
  5783. raise Exception.Create(sInvalidStream);
  5784. end;
  5785. function TGIFStream.Read(var Buffer; Count: Longint): Longint;
  5786. begin
  5787. raise Exception.Create(sInvalidStream);
  5788. end;
  5789. function TGIFStream.Seek(Offset: Longint; Origin: Word): Longint;
  5790. begin
  5791. raise Exception.Create(sInvalidStream);
  5792. end;
  5793. ////////////////////////////////////////////////////////////////////////////////
  5794. // TGIFReader - GIF block reader
  5795. ////////////////////////////////////////////////////////////////////////////////
  5796. type
  5797. TGIFReader = class(TGIFStream)
  5798. public
  5799. constructor Create(Stream: TStream);
  5800. function Read(var Buffer; Count: Longint): Longint; override;
  5801. end;
  5802. constructor TGIFReader.Create(Stream: TStream);
  5803. begin
  5804. inherited Create(Stream);
  5805. FBufferCount := 0;
  5806. end;
  5807. function TGIFReader.Read(var Buffer; Count: Longint): Longint;
  5808. var
  5809. n : integer;
  5810. Dst : PChar;
  5811. size : BYTE;
  5812. begin
  5813. Dst := @Buffer;
  5814. Result := 0;
  5815. while (Count > 0) do
  5816. begin
  5817. // Get data from buffer
  5818. while (FBufferCount > 0) and (Count > 0) do
  5819. begin
  5820. if (FBufferCount > Count) then
  5821. n := Count
  5822. else
  5823. n := FBufferCount;
  5824. Move(FBuffer, Dst^, n);
  5825. dec(FBufferCount, n);
  5826. dec(Count, n);
  5827. inc(Result, n);
  5828. inc(Dst, n);
  5829. end;
  5830. // Refill buffer when it becomes empty
  5831. if (FBufferCount <= 0) then
  5832. begin
  5833. FStream.Read(size, 1);
  5834. { TODO -oanme -cImprovement : Should be handled as a warning instead of an error. }
  5835. if (size >= 255) then
  5836. Error('GIF block too large');
  5837. FBufferCount := size;
  5838. if (FBufferCount > 0) then
  5839. begin
  5840. n := FStream.Read(FBuffer, size);
  5841. if (n = FBufferCount) then
  5842. begin
  5843. Warning(self, gsWarning, sOutOfData);
  5844. break;
  5845. end;
  5846. end else
  5847. break;
  5848. end;
  5849. end;
  5850. end;
  5851. ////////////////////////////////////////////////////////////////////////////////
  5852. // TGIFWriter - GIF block writer
  5853. ////////////////////////////////////////////////////////////////////////////////
  5854. type
  5855. TGIFWriter = class(TGIFStream)
  5856. private
  5857. FOutputDirty : boolean;
  5858. protected
  5859. procedure FlushBuffer;
  5860. public
  5861. constructor Create(Stream: TStream);
  5862. destructor Destroy; override;
  5863. function Write(const Buffer; Count: Longint): Longint; override;
  5864. function WriteByte(Value: BYTE): Longint;
  5865. end;
  5866. constructor TGIFWriter.Create(Stream: TStream);
  5867. begin
  5868. inherited Create(Stream);
  5869. FBufferCount := 1; // Reserve first byte of buffer for length
  5870. FOutputDirty := False;
  5871. end;
  5872. destructor TGIFWriter.Destroy;
  5873. begin
  5874. inherited Destroy;
  5875. if (FOutputDirty) then
  5876. FlushBuffer;
  5877. end;
  5878. procedure TGIFWriter.FlushBuffer;
  5879. begin
  5880. if (FBufferCount <= 0) then
  5881. exit;
  5882. FBuffer[0] := char(FBufferCount-1); // Block size excluding the count
  5883. FStream.WriteBuffer(FBuffer, FBufferCount);
  5884. FBufferCount := 1; // Reserve first byte of buffer for length
  5885. FOutputDirty := False;
  5886. end;
  5887. function TGIFWriter.Write(const Buffer; Count: Longint): Longint;
  5888. var
  5889. n : integer;
  5890. Src : PChar;
  5891. begin
  5892. Result := Count;
  5893. FOutputDirty := True;
  5894. Src := @Buffer;
  5895. while (Count > 0) do
  5896. begin
  5897. // Move data to the internal buffer in 255 byte chunks
  5898. while (FBufferCount < sizeof(FBuffer)) and (Count > 0) do
  5899. begin
  5900. n := sizeof(FBuffer) - FBufferCount;
  5901. if (n > Count) then
  5902. n := Count;
  5903. Move(Src^, FBuffer[FBufferCount], n);
  5904. inc(Src, n);
  5905. inc(FBufferCount, n);
  5906. dec(Count, n);
  5907. end;
  5908. // Flush the buffer when it is full
  5909. if (FBufferCount >= sizeof(FBuffer)) then
  5910. FlushBuffer;
  5911. end;
  5912. end;
  5913. function TGIFWriter.WriteByte(Value: BYTE): Longint;
  5914. begin
  5915. Result := Write(Value, 1);
  5916. end;
  5917. ////////////////////////////////////////////////////////////////////////////////
  5918. // TGIFEncoder - Abstract encoder
  5919. ////////////////////////////////////////////////////////////////////////////////
  5920. type
  5921. TGIFEncoder = class(TObject)
  5922. protected
  5923. FOnWarning : TGIFWarning;
  5924. MaxColor : integer;
  5925. BitsPerPixel : BYTE; // Bits per pixel of image
  5926. Stream : TStream; // Output stream
  5927. Width , // Width of image in pixels
  5928. Height : integer; // height of image in pixels
  5929. Interlace : boolean; // Interlace flag (True = interlaced image)
  5930. Data : PChar; // Pointer to pixel data
  5931. GIFStream : TGIFWriter; // Output buffer
  5932. OutputBucket : longInt; // Output bit bucket
  5933. OutputBits : integer; // Current # of bits in bucket
  5934. ClearFlag : Boolean; // True if dictionary has just been cleared
  5935. BitsPerCode , // Current # of bits per code
  5936. InitialBitsPerCode : integer; // Initial # of bits per code after
  5937. // dictionary has been cleared
  5938. MaxCode : CodeInt; // maximum code, given BitsPerCode
  5939. ClearCode : CodeInt; // Special output code to signal "Clear table"
  5940. EOFCode : CodeInt; // Special output code to signal EOF
  5941. BaseCode : CodeInt; // ...
  5942. Pixel : PChar; // Pointer to current pixel
  5943. cX , // Current X counter (Width - X)
  5944. Y : integer; // Current Y
  5945. Pass : integer; // Interlace pass
  5946. function MaxCodesFromBits(Bits: integer): CodeInt;
  5947. procedure Output(Value: integer); virtual;
  5948. procedure Clear; virtual;
  5949. function BumpPixel: boolean;
  5950. procedure DoCompress; virtual; abstract;
  5951. public
  5952. procedure Compress(AStream: TStream; ABitsPerPixel: integer;
  5953. AWidth, AHeight: integer; AInterlace: boolean; AData: PChar; AMaxColor: integer);
  5954. property Warning: TGIFWarning read FOnWarning write FOnWarning;
  5955. end;
  5956. // Calculate the maximum number of codes that a given number of bits can represent
  5957. // MaxCodes := (1^bits)-1
  5958. function TGIFEncoder.MaxCodesFromBits(Bits: integer): CodeInt;
  5959. begin
  5960. Result := (CodeInt(1) SHL Bits) - 1;
  5961. end;
  5962. // Stuff bits (variable sized codes) into a buffer and output them
  5963. // a byte at a time
  5964. procedure TGIFEncoder.Output(Value: integer);
  5965. const
  5966. BitBucketMask: array[0..16] of longInt =
  5967. ($0000,
  5968. $0001, $0003, $0007, $000F,
  5969. $001F, $003F, $007F, $00FF,
  5970. $01FF, $03FF, $07FF, $0FFF,
  5971. $1FFF, $3FFF, $7FFF, $FFFF);
  5972. begin
  5973. if (OutputBits > 0) then
  5974. OutputBucket :=
  5975. (OutputBucket AND BitBucketMask[OutputBits]) OR (longInt(Value) SHL OutputBits)
  5976. else
  5977. OutputBucket := Value;
  5978. inc(OutputBits, BitsPerCode);
  5979. while (OutputBits >= 8) do
  5980. begin
  5981. GIFStream.WriteByte(OutputBucket AND $FF);
  5982. OutputBucket := OutputBucket SHR 8;
  5983. dec(OutputBits, 8);
  5984. end;
  5985. if (Value = EOFCode) then
  5986. begin
  5987. // At EOF, write the rest of the buffer.
  5988. while (OutputBits > 0) do
  5989. begin
  5990. GIFStream.WriteByte(OutputBucket AND $FF);
  5991. OutputBucket := OutputBucket SHR 8;
  5992. dec(OutputBits, 8);
  5993. end;
  5994. end;
  5995. end;
  5996. procedure TGIFEncoder.Clear;
  5997. begin
  5998. // just_cleared = 1;
  5999. ClearFlag := TRUE;
  6000. Output(ClearCode);
  6001. end;
  6002. // Bump (X,Y) and data pointer to point to the next pixel
  6003. function TGIFEncoder.BumpPixel: boolean;
  6004. begin
  6005. // Bump the current X position
  6006. dec(cX);
  6007. // If we are at the end of a scan line, set cX back to the beginning
  6008. // If we are interlaced, bump Y to the appropriate spot, otherwise,
  6009. // just increment it.
  6010. if (cX <= 0) then
  6011. begin
  6012. if not(Interlace) then
  6013. begin
  6014. // Done - no more data
  6015. Result := False;
  6016. exit;
  6017. end;
  6018. cX := Width;
  6019. case (Pass) of
  6020. 0:
  6021. begin
  6022. inc(Y, 8);
  6023. if (Y >= Height) then
  6024. begin
  6025. inc(Pass);
  6026. Y := 4;
  6027. end;
  6028. end;
  6029. 1:
  6030. begin
  6031. inc(Y, 8);
  6032. if (Y >= Height) then
  6033. begin
  6034. inc(Pass);
  6035. Y := 2;
  6036. end;
  6037. end;
  6038. 2:
  6039. begin
  6040. inc(Y, 4);
  6041. if (Y >= Height) then
  6042. begin
  6043. inc(Pass);
  6044. Y := 1;
  6045. end;
  6046. end;
  6047. 3:
  6048. inc(Y, 2);
  6049. end;
  6050. if (Y >= height) then
  6051. begin
  6052. // Done - No more data
  6053. Result := False;
  6054. exit;
  6055. end;
  6056. Pixel := Data + (Y * Width);
  6057. end;
  6058. Result := True;
  6059. end;
  6060. procedure TGIFEncoder.Compress(AStream: TStream; ABitsPerPixel: integer;
  6061. AWidth, AHeight: integer; AInterlace: boolean; AData: PChar; AMaxColor: integer);
  6062. const
  6063. EndBlockByte = $00; // End of block marker
  6064. {$ifdef DEBUG_COMPRESSPERFORMANCE}
  6065. var
  6066. TimeStartCompress ,
  6067. TimeStopCompress : DWORD;
  6068. {$endif}
  6069. begin
  6070. MaxColor := AMaxColor;
  6071. Stream := AStream;
  6072. BitsPerPixel := ABitsPerPixel;
  6073. Width := AWidth;
  6074. Height := AHeight;
  6075. Interlace := AInterlace;
  6076. Data := AData;
  6077. if (BitsPerPixel <= 1) then
  6078. BitsPerPixel := 2;
  6079. InitialBitsPerCode := BitsPerPixel + 1;
  6080. Stream.Write(BitsPerPixel, 1);
  6081. // out_bits_init = init_bits;
  6082. BitsPerCode := InitialBitsPerCode;
  6083. MaxCode := MaxCodesFromBits(BitsPerCode);
  6084. ClearCode := (1 SHL (InitialBitsPerCode - 1));
  6085. EOFCode := ClearCode + 1;
  6086. BaseCode := EOFCode + 1;
  6087. // Clear bit bucket
  6088. OutputBucket := 0;
  6089. OutputBits := 0;
  6090. // Reset pixel counter
  6091. if (Interlace) then
  6092. cX := Width
  6093. else
  6094. cX := Width*Height;
  6095. // Reset row counter
  6096. Y := 0;
  6097. Pass := 0;
  6098. GIFStream := TGIFWriter.Create(AStream);
  6099. try
  6100. GIFStream.Warning := Warning;
  6101. if (Data <> nil) and (Height > 0) and (Width > 0) then
  6102. begin
  6103. {$ifdef DEBUG_COMPRESSPERFORMANCE}
  6104. TimeStartCompress := timeGetTime;
  6105. {$endif}
  6106. // Call compress implementation
  6107. DoCompress;
  6108. {$ifdef DEBUG_COMPRESSPERFORMANCE}
  6109. TimeStopCompress := timeGetTime;
  6110. ShowMessage(format('Compressed %d pixels in %d mS, Rate %d pixels/mS',
  6111. [Height*Width, TimeStopCompress-TimeStartCompress,
  6112. DWORD(Height*Width) DIV (TimeStopCompress-TimeStartCompress+1)]));
  6113. {$endif}
  6114. // Output the final code.
  6115. Output(EOFCode);
  6116. end else
  6117. // Output the final code (and nothing else).
  6118. TGIFEncoder(self).Output(EOFCode);
  6119. finally
  6120. GIFStream.Free;
  6121. end;
  6122. WriteByte(Stream, EndBlockByte);
  6123. end;
  6124. ////////////////////////////////////////////////////////////////////////////////
  6125. // TRLEEncoder - RLE encoder
  6126. ////////////////////////////////////////////////////////////////////////////////
  6127. type
  6128. TRLEEncoder = class(TGIFEncoder)
  6129. private
  6130. MaxCodes : integer;
  6131. OutBumpInit ,
  6132. OutClearInit : integer;
  6133. Prefix : integer; // Current run color
  6134. RunLengthTableMax ,
  6135. RunLengthTablePixel ,
  6136. OutCount ,
  6137. OutClear ,
  6138. OutBump : integer;
  6139. protected
  6140. function ComputeTriangleCount(count: integer; nrepcodes: integer): integer;
  6141. procedure MaxOutClear;
  6142. procedure ResetOutClear;
  6143. procedure FlushFromClear(Count: integer);
  6144. procedure FlushClearOrRepeat(Count: integer);
  6145. procedure FlushWithTable(Count: integer);
  6146. procedure Flush(RunLengthCount: integer);
  6147. procedure OutputPlain(Value: integer);
  6148. procedure Clear; override;
  6149. procedure DoCompress; override;
  6150. end;
  6151. procedure TRLEEncoder.Clear;
  6152. begin
  6153. OutBump := OutBumpInit;
  6154. OutClear := OutClearInit;
  6155. OutCount := 0;
  6156. RunLengthTableMax := 0;
  6157. inherited Clear;
  6158. BitsPerCode := InitialBitsPerCode;
  6159. end;
  6160. procedure TRLEEncoder.OutputPlain(Value: integer);
  6161. begin
  6162. ClearFlag := False;
  6163. Output(Value);
  6164. inc(OutCount);
  6165. if (OutCount >= OutBump) then
  6166. begin
  6167. inc(BitsPerCode);
  6168. inc(OutBump, 1 SHL (BitsPerCode - 1));
  6169. end;
  6170. if (OutCount >= OutClear) then
  6171. Clear;
  6172. end;
  6173. function TRLEEncoder.ComputeTriangleCount(count: integer; nrepcodes: integer): integer;
  6174. var
  6175. PerRepeat : integer;
  6176. n : integer;
  6177. function iSqrt(x: integer): integer;
  6178. var
  6179. r, v : integer;
  6180. begin
  6181. if (x < 2) then
  6182. begin
  6183. Result := x;
  6184. exit;
  6185. end else
  6186. begin
  6187. v := x;
  6188. r := 1;
  6189. while (v > 0) do
  6190. begin
  6191. v := v DIV 4;
  6192. r := r * 2;
  6193. end;
  6194. end;
  6195. while (True) do
  6196. begin
  6197. v := ((x DIV r) + r) DIV 2;
  6198. if ((v = r) or (v = r+1)) then
  6199. begin
  6200. Result := r;
  6201. exit;
  6202. end;
  6203. r := v;
  6204. end;
  6205. end;
  6206. begin
  6207. Result := 0;
  6208. PerRepeat := (nrepcodes * (nrepcodes+1)) DIV 2;
  6209. while (Count >= PerRepeat) do
  6210. begin
  6211. inc(Result, nrepcodes);
  6212. dec(Count, PerRepeat);
  6213. end;
  6214. if (Count > 0) then
  6215. begin
  6216. n := iSqrt(Count);
  6217. while ((n * (n+1)) >= 2*Count) do
  6218. dec(n);
  6219. while ((n * (n+1)) < 2*Count) do
  6220. inc(n);
  6221. inc(Result, n);
  6222. end;
  6223. end;
  6224. procedure TRLEEncoder.MaxOutClear;
  6225. begin
  6226. OutClear := MaxCodes;
  6227. end;
  6228. procedure TRLEEncoder.ResetOutClear;
  6229. begin
  6230. OutClear := OutClearInit;
  6231. if (OutCount >= OutClear) then
  6232. Clear;
  6233. end;
  6234. procedure TRLEEncoder.FlushFromClear(Count: integer);
  6235. var
  6236. n : integer;
  6237. begin
  6238. MaxOutClear;
  6239. RunLengthTablePixel := Prefix;
  6240. n := 1;
  6241. while (Count > 0) do
  6242. begin
  6243. if (n = 1) then
  6244. begin
  6245. RunLengthTableMax := 1;
  6246. OutputPlain(Prefix);
  6247. dec(Count);
  6248. end else
  6249. if (Count >= n) then
  6250. begin
  6251. RunLengthTableMax := n;
  6252. OutputPlain(BaseCode + n - 2);
  6253. dec(Count, n);
  6254. end else
  6255. if (Count = 1) then
  6256. begin
  6257. inc(RunLengthTableMax);
  6258. OutputPlain(Prefix);
  6259. break;
  6260. end else
  6261. begin
  6262. inc(RunLengthTableMax);
  6263. OutputPlain(BaseCode + Count - 2);
  6264. break;
  6265. end;
  6266. if (OutCount = 0) then
  6267. n := 1
  6268. else
  6269. inc(n);
  6270. end;
  6271. ResetOutClear;
  6272. end;
  6273. procedure TRLEEncoder.FlushClearOrRepeat(Count: integer);
  6274. var
  6275. WithClear : integer;
  6276. begin
  6277. WithClear := 1 + ComputeTriangleCount(Count, MaxCodes);
  6278. if (WithClear < Count) then
  6279. begin
  6280. Clear;
  6281. FlushFromClear(Count);
  6282. end else
  6283. while (Count > 0) do
  6284. begin
  6285. OutputPlain(Prefix);
  6286. dec(Count);
  6287. end;
  6288. end;
  6289. procedure TRLEEncoder.FlushWithTable(Count: integer);
  6290. var
  6291. RepeatMax ,
  6292. RepeatLeft ,
  6293. LeftOver : integer;
  6294. begin
  6295. RepeatMax := Count DIV RunLengthTableMax;
  6296. LeftOver := Count MOD RunLengthTableMax;
  6297. if (LeftOver <> 0) then
  6298. RepeatLeft := 1
  6299. else
  6300. RepeatLeft := 0;
  6301. if (OutCount + RepeatMax + RepeatLeft > MaxCodes) then
  6302. begin
  6303. RepeatMax := MaxCodes - OutCount;
  6304. LeftOver := Count - (RepeatMax * RunLengthTableMax);
  6305. RepeatLeft := 1 + ComputeTriangleCount(LeftOver, MaxCodes);
  6306. end;
  6307. if (1 + ComputeTriangleCount(Count, MaxCodes) < RepeatMax + RepeatLeft) then
  6308. begin
  6309. Clear;
  6310. FlushFromClear(Count);
  6311. exit;
  6312. end;
  6313. MaxOutClear;
  6314. while (RepeatMax > 0) do
  6315. begin
  6316. OutputPlain(BaseCode + RunLengthTableMax-2);
  6317. dec(RepeatMax);
  6318. end;
  6319. if (LeftOver > 0) then
  6320. begin
  6321. if (ClearFlag) then
  6322. FlushFromClear(LeftOver)
  6323. else if (LeftOver = 1) then
  6324. OutputPlain(Prefix)
  6325. else
  6326. OutputPlain(BaseCode + LeftOver - 2);
  6327. end;
  6328. ResetOutClear;
  6329. end;
  6330. procedure TRLEEncoder.Flush(RunLengthCount: integer);
  6331. begin
  6332. if (RunLengthCount = 1) then
  6333. begin
  6334. OutputPlain(Prefix);
  6335. exit;
  6336. end;
  6337. if (ClearFlag) then
  6338. FlushFromClear(RunLengthCount)
  6339. else if ((RunLengthTableMax < 2) or (RunLengthTablePixel <> Prefix)) then
  6340. FlushClearOrRepeat(RunLengthCount)
  6341. else
  6342. FlushWithTable(RunLengthCount);
  6343. end;
  6344. procedure TRLEEncoder.DoCompress;
  6345. var
  6346. Color : CodeInt;
  6347. RunLengthCount : integer;
  6348. begin
  6349. OutBumpInit := ClearCode - 1;
  6350. // For images with a lot of runs, making OutClearInit larger will
  6351. // give better compression.
  6352. if (BitsPerPixel <= 3) then
  6353. OutClearInit := 9
  6354. else
  6355. OutClearInit := OutBumpInit - 1;
  6356. // max_ocodes = (1 << GIFBITS) - ((1 << (out_bits_init - 1)) + 3);
  6357. // <=> MaxCodes := (1 SHL GIFCodeBits) - ((1 SHL (BitsPerCode - 1)) + 3);
  6358. // <=> MaxCodes := (1 SHL GIFCodeBits) - ((1 SHL (InitialBitsPerCode - 1)) + 3);
  6359. // <=> MaxCodes := (1 SHL GIFCodeBits) - (ClearCode + 3);
  6360. // <=> MaxCodes := (1 SHL GIFCodeBits) - (EOFCode + 2);
  6361. // <=> MaxCodes := (1 SHL GIFCodeBits) - (BaseCode + 1);
  6362. // <=> MaxCodes := MaxCodesFromBits(GIFCodeBits) - BaseCode;
  6363. MaxCodes := MaxCodesFromBits(GIFCodeBits) - BaseCode;
  6364. Clear;
  6365. RunLengthCount := 0;
  6366. Pixel := Data;
  6367. Prefix := -1; // Dummy value to make Color <> Prefix
  6368. repeat
  6369. // Fetch the next pixel
  6370. Color := CodeInt(Pixel^);
  6371. inc(Pixel);
  6372. if (Color >= MaxColor) then
  6373. Error(sInvalidColor);
  6374. if (RunLengthCount > 0) and (Color <> Prefix) then
  6375. begin
  6376. // End of current run
  6377. Flush(RunLengthCount);
  6378. RunLengthCount := 0;
  6379. end;
  6380. if (Color = Prefix) then
  6381. // Increment run length
  6382. inc(RunLengthCount)
  6383. else
  6384. begin
  6385. // Start new run
  6386. Prefix := Color;
  6387. RunLengthCount := 1;
  6388. end;
  6389. until not(BumpPixel);
  6390. Flush(RunLengthCount);
  6391. end;
  6392. ////////////////////////////////////////////////////////////////////////////////
  6393. // TLZWEncoder - LZW encoder
  6394. ////////////////////////////////////////////////////////////////////////////////
  6395. const
  6396. TableMaxMaxCode = (1 SHL GIFCodeBits); //
  6397. TableMaxFill = TableMaxMaxCode-1; // Clear table when it fills to
  6398. // this point.
  6399. // Note: Must be <= GIFCodeMax
  6400. type
  6401. TLZWEncoder = class(TGIFEncoder)
  6402. private
  6403. Prefix : CodeInt; // Current run color
  6404. FreeEntry : CodeInt; // next unused code in table
  6405. HashTable : THashTable;
  6406. protected
  6407. procedure Output(Value: integer); override;
  6408. procedure Clear; override;
  6409. procedure DoCompress; override;
  6410. end;
  6411. procedure TLZWEncoder.Output(Value: integer);
  6412. begin
  6413. inherited Output(Value);
  6414. // If the next entry is going to be too big for the code size,
  6415. // then increase it, if possible.
  6416. if (FreeEntry > MaxCode) or (ClearFlag) then
  6417. begin
  6418. if (ClearFlag) then
  6419. begin
  6420. BitsPerCode := InitialBitsPerCode;
  6421. MaxCode := MaxCodesFromBits(BitsPerCode);
  6422. ClearFlag := False;
  6423. end else
  6424. begin
  6425. inc(BitsPerCode);
  6426. if (BitsPerCode = GIFCodeBits) then
  6427. MaxCode := TableMaxMaxCode
  6428. else
  6429. MaxCode := MaxCodesFromBits(BitsPerCode);
  6430. end;
  6431. end;
  6432. end;
  6433. procedure TLZWEncoder.Clear;
  6434. begin
  6435. inherited Clear;
  6436. HashTable.Clear;
  6437. FreeEntry := ClearCode + 2;
  6438. end;
  6439. procedure TLZWEncoder.DoCompress;
  6440. var
  6441. Color : char;
  6442. NewKey : KeyInt;
  6443. NewCode : CodeInt;
  6444. begin
  6445. HashTable := THashTable.Create;
  6446. try
  6447. // clear hash table and sync decoder
  6448. Clear;
  6449. Pixel := Data;
  6450. Prefix := CodeInt(Pixel^);
  6451. inc(Pixel);
  6452. if (Prefix >= MaxColor) then
  6453. Error(sInvalidColor);
  6454. while (BumpPixel) do
  6455. begin
  6456. // Fetch the next pixel
  6457. Color := Pixel^;
  6458. inc(Pixel);
  6459. if (ord(Color) >= MaxColor) then
  6460. Error(sInvalidColor);
  6461. // Append Postfix to Prefix and lookup in table...
  6462. NewKey := (KeyInt(Prefix) SHL 8) OR ord(Color);
  6463. NewCode := HashTable.Lookup(NewKey);
  6464. if (NewCode >= 0) then
  6465. begin
  6466. // ...if found, get next pixel
  6467. Prefix := NewCode;
  6468. continue;
  6469. end;
  6470. // ...if not found, output and start over
  6471. Output(Prefix);
  6472. Prefix := CodeInt(Color);
  6473. if (FreeEntry < TableMaxFill) then
  6474. begin
  6475. HashTable.Insert(NewKey, FreeEntry);
  6476. inc(FreeEntry);
  6477. end else
  6478. Clear;
  6479. end;
  6480. Output(Prefix);
  6481. finally
  6482. HashTable.Free;
  6483. end;
  6484. end;
  6485. ////////////////////////////////////////////////////////////////////////////////
  6486. //
  6487. // TGIFSubImage
  6488. //
  6489. ////////////////////////////////////////////////////////////////////////////////
  6490. /////////////////////////////////////////////////////////////////////////
  6491. // TGIFSubImage.Compress
  6492. /////////////////////////////////////////////////////////////////////////
  6493. procedure TGIFSubImage.Compress(Stream: TStream);
  6494. var
  6495. Encoder : TGIFEncoder;
  6496. BitsPerPixel : BYTE;
  6497. MaxColors : integer;
  6498. begin
  6499. if (ColorMap.Count > 0) then
  6500. begin
  6501. MaxColors := ColorMap.Count;
  6502. BitsPerPixel := ColorMap.BitsPerPixel
  6503. end else
  6504. begin
  6505. BitsPerPixel := Image.BitsPerPixel;
  6506. MaxColors := 1 SHL BitsPerPixel;
  6507. end;
  6508. // Create a RLE or LZW GIF encoder
  6509. if (Image.Compression = gcRLE) then
  6510. Encoder := TRLEEncoder.Create
  6511. else
  6512. Encoder := TLZWEncoder.Create;
  6513. try
  6514. Encoder.Warning := Image.Warning;
  6515. Encoder.Compress(Stream, BitsPerPixel, Width, Height, Interlaced, FData, MaxColors);
  6516. finally
  6517. Encoder.Free;
  6518. end;
  6519. end;
  6520. function TGIFExtensionList.GetExtension(Index: Integer): TGIFExtension;
  6521. begin
  6522. Result := TGIFExtension(Items[Index]);
  6523. end;
  6524. procedure TGIFExtensionList.SetExtension(Index: Integer; Extension: TGIFExtension);
  6525. begin
  6526. Items[Index] := Extension;
  6527. end;
  6528. procedure TGIFExtensionList.LoadFromStream(Stream: TStream; Parent: TObject);
  6529. var
  6530. b : BYTE;
  6531. Extension : TGIFExtension;
  6532. ExtensionClass : TGIFExtensionClass;
  6533. begin
  6534. // Peek ahead to determine block type
  6535. if (Stream.Read(b, 1) <> 1) then
  6536. exit;
  6537. while not(b in [bsTrailer, bsImageDescriptor]) do
  6538. begin
  6539. if (b = bsExtensionIntroducer) then
  6540. begin
  6541. ExtensionClass := TGIFExtension.FindExtension(Stream);
  6542. if (ExtensionClass = nil) then
  6543. Error(sUnknownExtension);
  6544. Stream.Seek(-1, soFromCurrent);
  6545. Extension := ExtensionClass.Create(Parent as TGIFSubImage);
  6546. try
  6547. Extension.LoadFromStream(Stream);
  6548. Add(Extension);
  6549. except
  6550. Extension.Free;
  6551. raise;
  6552. end;
  6553. end else
  6554. begin
  6555. Warning(gsWarning, sBadExtensionLabel);
  6556. break;
  6557. end;
  6558. if (Stream.Read(b, 1) <> 1) then
  6559. exit;
  6560. end;
  6561. Stream.Seek(-1, soFromCurrent);
  6562. end;
  6563. const
  6564. { image descriptor bit masks }
  6565. idLocalColorTable = $80; { set if a local color table follows }
  6566. idInterlaced = $40; { set if image is interlaced }
  6567. idSort = $20; { set if color table is sorted }
  6568. idReserved = $0C; { reserved - must be set to $00 }
  6569. idColorTableSize = $07; { size of color table as above }
  6570. constructor TGIFSubImage.Create(GIFImage: TGIFImage);
  6571. begin
  6572. inherited Create(GIFImage);
  6573. FExtensions := TGIFExtensionList.Create(GIFImage);
  6574. FColorMap := TGIFLocalColorMap.Create(self);
  6575. FImageDescriptor.Separator := bsImageDescriptor;
  6576. FImageDescriptor.Left := 0;
  6577. FImageDescriptor.Top := 0;
  6578. FImageDescriptor.Width := 0;
  6579. FImageDescriptor.Height := 0;
  6580. FImageDescriptor.PackedFields := 0;
  6581. FBitmap := nil;
  6582. FMask := 0;
  6583. FNeedMask := True;
  6584. FData := nil;
  6585. FDataSize := 0;
  6586. FTransparent := False;
  6587. FGCE := nil;
  6588. // Remember to synchronize with TGIFSubImage.Clear
  6589. end;
  6590. destructor TGIFSubImage.Destroy;
  6591. begin
  6592. if (FGIFImage <> nil) then
  6593. FGIFImage.Images.Remove(self);
  6594. Clear;
  6595. FExtensions.Free;
  6596. FColorMap.Free;
  6597. if (FLocalPalette <> 0) then
  6598. DeleteObject(FLocalPalette);
  6599. inherited Destroy;
  6600. end;
  6601. procedure TGIFSubImage.Clear;
  6602. begin
  6603. FExtensions.Clear;
  6604. FColorMap.Clear;
  6605. FreeImage;
  6606. Height := 0;
  6607. Width := 0;
  6608. FTransparent := False;
  6609. FGCE := nil;
  6610. FreeBitmap;
  6611. FreeMask;
  6612. // Remember to synchronize with TGIFSubImage.Create
  6613. end;
  6614. function TGIFSubImage.GetEmpty: Boolean;
  6615. begin
  6616. Result := ((FData = nil) or (FDataSize = 0) or (Height = 0) or (Width = 0));
  6617. end;
  6618. function TGIFSubImage.GetPalette: HPALETTE;
  6619. begin
  6620. if (FBitmap <> nil) and (FBitmap.Palette <> 0) then
  6621. // Use bitmaps own palette if possible
  6622. Result := FBitmap.Palette
  6623. else if (FLocalPalette <> 0) then
  6624. // Or a previously exported local palette
  6625. Result := FLocalPalette
  6626. else if (Image.DoDither) then
  6627. begin
  6628. // or create a new dither palette
  6629. FLocalPalette := WebPalette;
  6630. Result := FLocalPalette;
  6631. end
  6632. else if (ColorMap.Count > 0) then
  6633. begin
  6634. // or create a new if first time
  6635. FLocalPalette := ColorMap.ExportPalette;
  6636. Result := FLocalPalette;
  6637. end else
  6638. // Use global palette if everything else fails
  6639. Result := Image.Palette;
  6640. end;
  6641. procedure TGIFSubImage.SetPalette(Value: HPalette);
  6642. var
  6643. NeedNewBitmap : boolean;
  6644. begin
  6645. if (Value <> FLocalPalette) then
  6646. begin
  6647. // Zap old palette
  6648. if (FLocalPalette <> 0) then
  6649. DeleteObject(FLocalPalette);
  6650. // Zap bitmap unless new palette is same as bitmaps own
  6651. NeedNewBitmap := (FBitmap <> nil) and (Value <> FBitmap.Palette);
  6652. // Use new palette
  6653. FLocalPalette := Value;
  6654. if (NeedNewBitmap) then
  6655. begin
  6656. // Need to create new bitmap and repaint
  6657. FreeBitmap;
  6658. Image.PaletteModified := True;
  6659. Image.Changed(Self);
  6660. end;
  6661. end;
  6662. end;
  6663. procedure TGIFSubImage.NeedImage;
  6664. begin
  6665. if (FData = nil) then
  6666. NewImage;
  6667. if (FDataSize = 0) then
  6668. Error(sEmptyImage);
  6669. end;
  6670. procedure TGIFSubImage.NewImage;
  6671. var
  6672. NewSize : longInt;
  6673. begin
  6674. FreeImage;
  6675. NewSize := Height * Width;
  6676. if (NewSize <> 0) then
  6677. begin
  6678. GetMem(FData, NewSize);
  6679. FillChar(FData^, NewSize, 0);
  6680. end else
  6681. FData := nil;
  6682. FDataSize := NewSize;
  6683. end;
  6684. procedure TGIFSubImage.FreeImage;
  6685. begin
  6686. if (FData <> nil) then
  6687. FreeMem(FData);
  6688. FDataSize := 0;
  6689. FData := nil;
  6690. end;
  6691. function TGIFSubImage.GetHasBitmap: boolean;
  6692. begin
  6693. Result := (FBitmap <> nil);
  6694. end;
  6695. procedure TGIFSubImage.SetHasBitmap(Value: boolean);
  6696. begin
  6697. if (Value <> (FBitmap <> nil)) then
  6698. begin
  6699. if (Value) then
  6700. Bitmap // Referencing Bitmap will automatically create it
  6701. else
  6702. FreeBitmap;
  6703. end;
  6704. end;
  6705. procedure TGIFSubImage.NewBitmap;
  6706. begin
  6707. FreeBitmap;
  6708. FBitmap := TBitmap.Create;
  6709. end;
  6710. procedure TGIFSubImage.FreeBitmap;
  6711. begin
  6712. if (FBitmap <> nil) then
  6713. begin
  6714. FBitmap.Free;
  6715. FBitmap := nil;
  6716. end;
  6717. end;
  6718. procedure TGIFSubImage.FreeMask;
  6719. begin
  6720. if (FMask <> 0) then
  6721. begin
  6722. DeleteObject(FMask);
  6723. FMask := 0;
  6724. end;
  6725. FNeedMask := True;
  6726. end;
  6727. function TGIFSubImage.HasMask: boolean;
  6728. begin
  6729. if (FNeedMask) and (Transparent) then
  6730. begin
  6731. // Zap old bitmap
  6732. FreeBitmap;
  6733. // Create new bitmap and mask
  6734. GetBitmap;
  6735. end;
  6736. Result := (FMask <> 0);
  6737. end;
  6738. function TGIFSubImage.GetBounds(Index: integer): WORD;
  6739. begin
  6740. case (Index) of
  6741. 1: Result := FImageDescriptor.Left;
  6742. 2: Result := FImageDescriptor.Top;
  6743. 3: Result := FImageDescriptor.Width;
  6744. 4: Result := FImageDescriptor.Height;
  6745. else
  6746. Result := 0; // To avoid compiler warnings
  6747. end;
  6748. end;
  6749. procedure TGIFSubImage.SetBounds(Index: integer; Value: WORD);
  6750. begin
  6751. case (Index) of
  6752. 1: DoSetBounds(Value, FImageDescriptor.Top, FImageDescriptor.Width, FImageDescriptor.Height);
  6753. 2: DoSetBounds(FImageDescriptor.Left, Value, FImageDescriptor.Width, FImageDescriptor.Height);
  6754. 3: DoSetBounds(FImageDescriptor.Left, FImageDescriptor.Top, Value, FImageDescriptor.Height);
  6755. 4: DoSetBounds(FImageDescriptor.Left, FImageDescriptor.Top, FImageDescriptor.Width, Value);
  6756. end;
  6757. end;
  6758. {$IFOPT R+}
  6759. {$DEFINE R_PLUS}
  6760. {$RANGECHECKS OFF}
  6761. {$ENDIF}
  6762. function TGIFSubImage.DoGetDitherBitmap: TBitmap;
  6763. var
  6764. ColorLookup : TColorLookup;
  6765. Ditherer : TDitherEngine;
  6766. DIBResult : TDIB;
  6767. Src : PChar;
  6768. Dst : PChar;
  6769. Row : integer;
  6770. Color : TGIFColor;
  6771. ColMap : PColorMap;
  6772. Index : byte;
  6773. TransparentIndex : byte;
  6774. IsTransparent : boolean;
  6775. WasTransparent : boolean;
  6776. MappedTransparentIndex: char;
  6777. MaskBits : PChar;
  6778. MaskDest : PChar;
  6779. MaskRow : PChar;
  6780. MaskRowWidth ,
  6781. MaskRowBitWidth : integer;
  6782. Bit ,
  6783. RightBit : BYTE;
  6784. begin
  6785. Result := TBitmap.Create;
  6786. try
  6787. {$IFNDEF VER9x}
  6788. if (Width*Height > BitmapAllocationThreshold) then
  6789. SetPixelFormat(Result, pf1bit); // To reduce resource consumption of resize
  6790. {$ENDIF}
  6791. if (Empty) then
  6792. begin
  6793. // Set bitmap width and height
  6794. Result.Width := Width;
  6795. Result.Height := Height;
  6796. // Build and copy palette to bitmap
  6797. Result.Palette := CopyPalette(Palette);
  6798. exit;
  6799. end;
  6800. ColorLookup := nil;
  6801. Ditherer := nil;
  6802. DIBResult := nil;
  6803. try // Protect above resources
  6804. ColorLookup := TNetscapeColorLookup.Create(Palette);
  6805. Ditherer := TFloydSteinbergDitherer.Create(Width, ColorLookup);
  6806. // Get DIB buffer for scanline operations
  6807. // It is assumed that the source palette is the 216 color Netscape palette
  6808. DIBResult := TDIBWriter.Create(Result, pf8bit, Width, Height, Palette);
  6809. // Determine if this image is transparent
  6810. ColMap := ActiveColorMap.Data;
  6811. IsTransparent := FNeedMask and Transparent;
  6812. WasTransparent := False;
  6813. FNeedMask := False;
  6814. TransparentIndex := 0;
  6815. MappedTransparentIndex := #0;
  6816. if (FMask = 0) and (IsTransparent) then
  6817. begin
  6818. IsTransparent := True;
  6819. TransparentIndex := GraphicControlExtension.TransparentColorIndex;
  6820. Color := ColMap[ord(TransparentIndex)];
  6821. MappedTransparentIndex := char(Color.Blue DIV 51 +
  6822. MulDiv(6, Color.Green, 51) + MulDiv(36, Color.Red, 51)+1);
  6823. end;
  6824. // Allocate bit buffer for transparency mask
  6825. MaskDest := nil;
  6826. Bit := $00;
  6827. if (IsTransparent) then
  6828. begin
  6829. MaskRowWidth := ((Width+15) DIV 16) * 2;
  6830. MaskRowBitWidth := (Width+7) DIV 8;
  6831. RightBit := $01 SHL ((8 - (Width AND $0007)) AND $0007);
  6832. GetMem(MaskBits, MaskRowWidth * Height);
  6833. FillChar(MaskBits^, MaskRowWidth * Height, 0);
  6834. end else
  6835. begin
  6836. MaskBits := nil;
  6837. MaskRowWidth := 0;
  6838. MaskRowBitWidth := 0;
  6839. RightBit := $00;
  6840. end;
  6841. try
  6842. // Process the image
  6843. Row := 0;
  6844. MaskRow := MaskBits;
  6845. Src := FData;
  6846. while (Row < Height) do
  6847. begin
  6848. if ((Row AND $1F) = 0) then
  6849. Image.Progress(Self, psRunning, MulDiv(Row, 100, Height),
  6850. False, Rect(0,0,0,0), sProgressRendering);
  6851. Dst := DIBResult.ScanLine[Row];
  6852. if (IsTransparent) then
  6853. begin
  6854. // Preset all pixels to transparent
  6855. FillChar(Dst^, Width, ord(MappedTransparentIndex));
  6856. if (Ditherer.Direction = 1) then
  6857. begin
  6858. MaskDest := MaskRow;
  6859. Bit := $80;
  6860. end else
  6861. begin
  6862. MaskDest := MaskRow + MaskRowBitWidth-1;
  6863. Bit := RightBit;
  6864. end;
  6865. end;
  6866. inc(Dst, Ditherer.Column);
  6867. while (Ditherer.Column < Ditherer.Width) and (Ditherer.Column >= 0) do
  6868. begin
  6869. Index := ord(Src^);
  6870. Color := ColMap[ord(Index)];
  6871. if (IsTransparent) and (Index = TransparentIndex) then
  6872. begin
  6873. MaskDest^ := char(byte(MaskDest^) OR Bit);
  6874. WasTransparent := True;
  6875. Ditherer.NextColumn;
  6876. end else
  6877. begin
  6878. // Dither and map a single pixel
  6879. Dst^ := Ditherer.Dither(Color.Red, Color.Green, Color.Blue,
  6880. Color.Red, Color.Green, Color.Blue);
  6881. end;
  6882. if (IsTransparent) then
  6883. begin
  6884. if (Ditherer.Direction = 1) then
  6885. begin
  6886. Bit := Bit SHR 1;
  6887. if (Bit = $00) then
  6888. begin
  6889. Bit := $80;
  6890. inc(MaskDest, 1);
  6891. end;
  6892. end else
  6893. begin
  6894. Bit := Bit SHL 1;
  6895. if (Bit = $00) then
  6896. begin
  6897. Bit := $01;
  6898. dec(MaskDest, 1);
  6899. end;
  6900. end;
  6901. end;
  6902. inc(Src, Ditherer.Direction);
  6903. inc(Dst, Ditherer.Direction);
  6904. end;
  6905. if (IsTransparent) then
  6906. Inc(MaskRow, MaskRowWidth);
  6907. Inc(Row);
  6908. inc(Src, Width-Ditherer.Direction);
  6909. Ditherer.NextLine;
  6910. end;
  6911. // Transparent paint needs a mask bitmap
  6912. if (IsTransparent) and (WasTransparent) then
  6913. FMask := CreateBitmap(Width, Height, 1, 1, MaskBits);
  6914. finally
  6915. if (MaskBits <> nil) then
  6916. FreeMem(MaskBits);
  6917. end;
  6918. finally
  6919. if (ColorLookup <> nil) then
  6920. ColorLookup.Free;
  6921. if (Ditherer <> nil) then
  6922. Ditherer.Free;
  6923. if (DIBResult <> nil) then
  6924. DIBResult.Free;
  6925. end;
  6926. except
  6927. Result.Free;
  6928. raise;
  6929. end;
  6930. end;
  6931. {$IFDEF R_PLUS}
  6932. {$RANGECHECKS ON}
  6933. {$UNDEF R_PLUS}
  6934. {$ENDIF}
  6935. function TGIFSubImage.DoGetBitmap: TBitmap;
  6936. var
  6937. ScanLineRow : Integer;
  6938. DIBResult : TDIB;
  6939. DestScanLine ,
  6940. Src : PChar;
  6941. TransparentIndex : byte;
  6942. IsTransparent : boolean;
  6943. WasTransparent : boolean;
  6944. MaskBits : PChar;
  6945. MaskDest : PChar;
  6946. MaskRow : PChar;
  6947. MaskRowWidth : integer;
  6948. Col : integer;
  6949. MaskByte : byte;
  6950. Bit : byte;
  6951. begin
  6952. Result := TBitmap.Create;
  6953. try
  6954. {$IFNDEF VER9x}
  6955. if (Width*Height > BitmapAllocationThreshold) then
  6956. SetPixelFormat(Result, pf1bit); // To reduce resource consumption of resize
  6957. {$ENDIF}
  6958. if (Empty) then
  6959. begin
  6960. // Set bitmap width and height
  6961. Result.Width := Width;
  6962. Result.Height := Height;
  6963. // Build and copy palette to bitmap
  6964. Result.Palette := CopyPalette(Palette);
  6965. exit;
  6966. end;
  6967. // Get DIB buffer for scanline operations
  6968. DIBResult := TDIBWriter.Create(Result, pf8bit, Width, Height, Palette);
  6969. try
  6970. // Determine if this image is transparent
  6971. IsTransparent := FNeedMask and Transparent;
  6972. WasTransparent := False;
  6973. FNeedMask := False;
  6974. TransparentIndex := 0;
  6975. if (FMask = 0) and (IsTransparent) then
  6976. begin
  6977. IsTransparent := True;
  6978. TransparentIndex := GraphicControlExtension.TransparentColorIndex;
  6979. end;
  6980. // Allocate bit buffer for transparency mask
  6981. if (IsTransparent) then
  6982. begin
  6983. MaskRowWidth := ((Width+15) DIV 16) * 2;
  6984. GetMem(MaskBits, MaskRowWidth * Height);
  6985. FillChar(MaskBits^, MaskRowWidth * Height, 0);
  6986. IsTransparent := (MaskBits <> nil);
  6987. end else
  6988. begin
  6989. MaskBits := nil;
  6990. MaskRowWidth := 0;
  6991. end;
  6992. try
  6993. ScanLineRow := 0;
  6994. Src := FData;
  6995. MaskRow := MaskBits;
  6996. while (ScanLineRow < Height) do
  6997. begin
  6998. DestScanline := DIBResult.ScanLine[ScanLineRow];
  6999. if ((ScanLineRow AND $1F) = 0) then
  7000. Image.Progress(Self, psRunning, MulDiv(ScanLineRow, 100, Height),
  7001. False, Rect(0,0,0,0), sProgressRendering);
  7002. Move(Src^, DestScanline^, Width);
  7003. Inc(ScanLineRow);
  7004. if (IsTransparent) then
  7005. begin
  7006. Bit := $80;
  7007. MaskDest := MaskRow;
  7008. MaskByte := 0;
  7009. for Col := 0 to Width-1 do
  7010. begin
  7011. // Set a bit in the mask if the pixel is transparent
  7012. if (Src^ = char(TransparentIndex)) then
  7013. MaskByte := MaskByte OR Bit;
  7014. Bit := Bit SHR 1;
  7015. if (Bit = $00) then
  7016. begin
  7017. // Store a mask byte for each 8 pixels
  7018. Bit := $80;
  7019. WasTransparent := WasTransparent or (MaskByte <> 0);
  7020. MaskDest^ := char(MaskByte);
  7021. inc(MaskDest);
  7022. MaskByte := 0;
  7023. end;
  7024. Inc(Src);
  7025. end;
  7026. // Save the last mask byte in case the width isn't divisable by 8
  7027. if (MaskByte <> 0) then
  7028. begin
  7029. WasTransparent := True;
  7030. MaskDest^ := char(MaskByte);
  7031. end;
  7032. Inc(MaskRow, MaskRowWidth);
  7033. end else
  7034. Inc(Src, Width);
  7035. end;
  7036. // Transparent paint needs a mask bitmap
  7037. if (IsTransparent) and (WasTransparent) then
  7038. FMask := CreateBitmap(Width, Height, 1, 1, MaskBits);
  7039. finally
  7040. if (MaskBits <> nil) then
  7041. FreeMem(MaskBits);
  7042. end;
  7043. finally
  7044. // Free DIB buffer used for scanline operations
  7045. DIBResult.Free;
  7046. end;
  7047. except
  7048. Result.Free;
  7049. raise;
  7050. end;
  7051. end;
  7052. {$ifdef DEBUG_RENDERPERFORMANCE}
  7053. var
  7054. ImageCount : DWORD = 0;
  7055. RenderTime : DWORD = 0;
  7056. {$endif}
  7057. function TGIFSubImage.GetBitmap: TBitmap;
  7058. var
  7059. n : integer;
  7060. {$ifdef DEBUG_RENDERPERFORMANCE}
  7061. RenderStartTime : DWORD;
  7062. {$endif}
  7063. begin
  7064. {$ifdef DEBUG_RENDERPERFORMANCE}
  7065. if (GetAsyncKeyState(VK_CONTROL) <> 0) then
  7066. begin
  7067. ShowMessage(format('Render %d images in %d mS, Rate %d mS/image (%d images/S)',
  7068. [ImageCount, RenderTime,
  7069. RenderTime DIV (ImageCount+1),
  7070. MulDiv(ImageCount, 1000, RenderTime+1)]));
  7071. end;
  7072. {$endif}
  7073. Result := FBitmap;
  7074. if (Result <> nil) or (Empty) then
  7075. Exit;
  7076. {$ifdef DEBUG_RENDERPERFORMANCE}
  7077. inc(ImageCount);
  7078. RenderStartTime := timeGetTime;
  7079. {$endif}
  7080. try
  7081. Image.Progress(Self, psStarting, 0, False, Rect(0,0,0,0), sProgressRendering);
  7082. try
  7083. if (Image.DoDither) then
  7084. // Create dithered bitmap
  7085. FBitmap := DoGetDitherBitmap
  7086. else
  7087. // Create "regular" bitmap
  7088. FBitmap := DoGetBitmap;
  7089. Result := FBitmap;
  7090. finally
  7091. if ExceptObject = nil then
  7092. n := 100
  7093. else
  7094. n := 0;
  7095. Image.Progress(Self, psEnding, n, Image.PaletteModified, Rect(0,0,0,0),
  7096. sProgressRendering);
  7097. // Make sure new palette gets realized, in case OnProgress event didn't.
  7098. if Image.PaletteModified then
  7099. Image.Changed(Self);
  7100. end;
  7101. except
  7102. on EAbort do ; // OnProgress can raise EAbort to cancel image load
  7103. end;
  7104. {$ifdef DEBUG_RENDERPERFORMANCE}
  7105. inc(RenderTime, timeGetTime-RenderStartTime);
  7106. {$endif}
  7107. end;
  7108. procedure TGIFSubImage.SetBitmap(Value: TBitmap);
  7109. begin
  7110. FreeBitmap;
  7111. if (Value <> nil) then
  7112. Assign(Value);
  7113. end;
  7114. function TGIFSubImage.GetActiveColorMap: TGIFColorMap;
  7115. begin
  7116. if (ColorMap.Count > 0) or (Image.GlobalColorMap.Count = 0) then
  7117. Result := ColorMap
  7118. else
  7119. Result := Image.GlobalColorMap;
  7120. end;
  7121. function TGIFSubImage.GetInterlaced: boolean;
  7122. begin
  7123. Result := (FImageDescriptor.PackedFields AND idInterlaced) <> 0;
  7124. end;
  7125. procedure TGIFSubImage.SetInterlaced(Value: boolean);
  7126. begin
  7127. if (Value) then
  7128. FImageDescriptor.PackedFields := FImageDescriptor.PackedFields OR idInterlaced
  7129. else
  7130. FImageDescriptor.PackedFields := FImageDescriptor.PackedFields AND NOT(idInterlaced);
  7131. end;
  7132. function TGIFSubImage.GetVersion: TGIFVersion;
  7133. var
  7134. v : TGIFVersion;
  7135. i : integer;
  7136. begin
  7137. if (ColorMap.Optimized) then
  7138. Result := gv89a
  7139. else
  7140. Result := inherited GetVersion;
  7141. i := 0;
  7142. while (Result < high(TGIFVersion)) and (i < FExtensions.Count) do
  7143. begin
  7144. v := FExtensions[i].Version;
  7145. if (v > Result) then
  7146. Result := v;
  7147. end;
  7148. end;
  7149. function TGIFSubImage.GetColorResolution: integer;
  7150. begin
  7151. Result := ColorMap.BitsPerPixel-1;
  7152. end;
  7153. function TGIFSubImage.GetBitsPerPixel: integer;
  7154. begin
  7155. Result := ColorMap.BitsPerPixel;
  7156. end;
  7157. function TGIFSubImage.GetBoundsRect: TRect;
  7158. begin
  7159. Result := Rect(FImageDescriptor.Left,
  7160. FImageDescriptor.Top,
  7161. FImageDescriptor.Left+FImageDescriptor.Width,
  7162. FImageDescriptor.Top+FImageDescriptor.Height);
  7163. end;
  7164. procedure TGIFSubImage.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);
  7165. var
  7166. TooLarge : boolean;
  7167. Zap : boolean;
  7168. begin
  7169. Zap := (FImageDescriptor.Width <> Width) or (FImageDescriptor.Height <> AHeight);
  7170. FImageDescriptor.Left := ALeft;
  7171. FImageDescriptor.Top := ATop;
  7172. FImageDescriptor.Width := AWidth;
  7173. FImageDescriptor.Height := AHeight;
  7174. // Delete existing image and bitmaps if size has changed
  7175. if (Zap) then
  7176. begin
  7177. FreeBitmap;
  7178. FreeMask;
  7179. FreeImage;
  7180. // ...and allocate a new image
  7181. NewImage;
  7182. end;
  7183. TooLarge := False;
  7184. // Set width & height if added image is larger than existing images
  7185. {$IFDEF STRICT_MOZILLA}
  7186. // From Mozilla source:
  7187. // Work around broken GIF files where the logical screen
  7188. // size has weird width or height. [...]
  7189. if (Image.Width < AWidth) or (Image.Height < AHeight) then
  7190. begin
  7191. TooLarge := True;
  7192. Image.Width := AWidth;
  7193. Image.Height := AHeight;
  7194. Left := 0;
  7195. Top := 0;
  7196. end;
  7197. {$ELSE}
  7198. if (Image.Width < ALeft+AWidth) then
  7199. begin
  7200. if (Image.Width > 0) then
  7201. begin
  7202. TooLarge := True;
  7203. Warning(gsWarning, sBadWidth)
  7204. end;
  7205. Image.Width := ALeft+AWidth;
  7206. end;
  7207. if (Image.Height < ATop+AHeight) then
  7208. begin
  7209. if (Image.Height > 0) then
  7210. begin
  7211. TooLarge := True;
  7212. Warning(gsWarning, sBadHeight)
  7213. end;
  7214. Image.Height := ATop+AHeight;
  7215. end;
  7216. {$ENDIF}
  7217. if (TooLarge) then
  7218. Warning(gsWarning, sScreenSizeExceeded);
  7219. end;
  7220. procedure TGIFSubImage.SetBoundsRect(const Value: TRect);
  7221. begin
  7222. DoSetBounds(Value.Left, Value.Top, Value.Right-Value.Left+1, Value.Bottom-Value.Top+1);
  7223. end;
  7224. function TGIFSubImage.GetClientRect: TRect;
  7225. begin
  7226. Result := Rect(0, 0, FImageDescriptor.Width, FImageDescriptor.Height);
  7227. end;
  7228. function TGIFSubImage.GetPixel(x, y: integer): BYTE;
  7229. begin
  7230. if (x < 0) or (x > Width-1) then
  7231. Error(sBadPixelCoordinates);
  7232. Result := BYTE(PChar(longInt(Scanline[y]) + x)^);
  7233. end;
  7234. function TGIFSubImage.GetScanline(y: integer): pointer;
  7235. begin
  7236. if (y < 0) or (y > Height-1) then
  7237. Error(sBadPixelCoordinates);
  7238. NeedImage;
  7239. Result := pointer(longInt(FData) + y * Width);
  7240. end;
  7241. procedure TGIFSubImage.Prepare;
  7242. var
  7243. Pack : BYTE;
  7244. begin
  7245. Pack := FImageDescriptor.PackedFields;
  7246. if (ColorMap.Count > 0) then
  7247. begin
  7248. Pack := idLocalColorTable;
  7249. if (ColorMap.Optimized) then
  7250. Pack := Pack OR idSort;
  7251. Pack := (Pack AND NOT(idColorTableSize)) OR (ColorResolution AND idColorTableSize);
  7252. end else
  7253. Pack := Pack AND NOT(idLocalColorTable OR idSort OR idColorTableSize);
  7254. FImageDescriptor.PackedFields := Pack;
  7255. end;
  7256. procedure TGIFSubImage.SaveToStream(Stream: TStream);
  7257. begin
  7258. FExtensions.SaveToStream(Stream);
  7259. if (Empty) then
  7260. exit;
  7261. Prepare;
  7262. Stream.Write(FImageDescriptor, sizeof(TImageDescriptor));
  7263. ColorMap.SaveToStream(Stream);
  7264. Compress(Stream);
  7265. end;
  7266. procedure TGIFSubImage.LoadFromStream(Stream: TStream);
  7267. var
  7268. ColorCount : integer;
  7269. b : BYTE;
  7270. begin
  7271. Clear;
  7272. FExtensions.LoadFromStream(Stream, self);
  7273. // Check for extension without image
  7274. if (Stream.Read(b, 1) <> 1) then
  7275. exit;
  7276. Stream.Seek(-1, soFromCurrent);
  7277. if (b = bsTrailer) or (b = 0) then
  7278. exit;
  7279. ReadCheck(Stream, FImageDescriptor, sizeof(TImageDescriptor));
  7280. // From Mozilla source:
  7281. // Work around more broken GIF files that have zero image
  7282. // width or height
  7283. if (FImageDescriptor.Height = 0) or (FImageDescriptor.Width = 0) then
  7284. begin
  7285. FImageDescriptor.Height := Image.Height;
  7286. FImageDescriptor.Width := Image.Width;
  7287. Warning(gsWarning, sScreenSizeExceeded);
  7288. end;
  7289. if (FImageDescriptor.PackedFields AND idLocalColorTable = idLocalColorTable) then
  7290. begin
  7291. ColorCount := 2 SHL (FImageDescriptor.PackedFields AND idColorTableSize);
  7292. if (ColorCount < 2) or (ColorCount > 256) then
  7293. Error(sImageBadColorSize);
  7294. ColorMap.LoadFromStream(Stream, ColorCount);
  7295. end;
  7296. Decompress(Stream);
  7297. // On-load rendering
  7298. if (GIFImageRenderOnLoad) then
  7299. // Touch bitmap to force frame to be rendered
  7300. Bitmap;
  7301. end;
  7302. procedure TGIFSubImage.AssignTo(Dest: TPersistent);
  7303. begin
  7304. if (Dest is TBitmap) then
  7305. Dest.Assign(Bitmap)
  7306. else
  7307. inherited AssignTo(Dest);
  7308. end;
  7309. procedure TGIFSubImage.Assign(Source: TPersistent);
  7310. var
  7311. MemoryStream : TMemoryStream;
  7312. i : integer;
  7313. PixelFormat : TPixelFormat;
  7314. DIBSource : TDIB;
  7315. ABitmap : TBitmap;
  7316. procedure Import8Bit(Dest: PChar);
  7317. var
  7318. y : integer;
  7319. begin
  7320. // Copy colormap
  7321. {$ifdef VER10_PLUS}
  7322. if (FBitmap.HandleType = bmDIB) then
  7323. FColorMap.ImportDIBColors(FBitmap.Canvas.Handle)
  7324. else
  7325. {$ENDIF}
  7326. FColorMap.ImportPalette(FBitmap.Palette);
  7327. // Copy pixels
  7328. for y := 0 to Height-1 do
  7329. begin
  7330. if ((y AND $1F) = 0) then
  7331. Image.Progress(Self, psRunning, MulDiv(y, 100, Height), False, Rect(0,0,0,0), sProgressConverting);
  7332. Move(DIBSource.Scanline[y]^, Dest^, Width);
  7333. inc(Dest, Width);
  7334. end;
  7335. end;
  7336. procedure Import4Bit(Dest: PChar);
  7337. var
  7338. x, y : integer;
  7339. Scanline : PChar;
  7340. begin
  7341. // Copy colormap
  7342. FColorMap.ImportPalette(FBitmap.Palette);
  7343. // Copy pixels
  7344. for y := 0 to Height-1 do
  7345. begin
  7346. if ((y AND $1F) = 0) then
  7347. Image.Progress(Self, psRunning, MulDiv(y, 100, Height), False, Rect(0,0,0,0), sProgressConverting);
  7348. ScanLine := DIBSource.Scanline[y];
  7349. for x := 0 to Width-1 do
  7350. begin
  7351. if (x AND $01 = 0) then
  7352. Dest^ := chr(ord(ScanLine^) SHR 4)
  7353. else
  7354. begin
  7355. Dest^ := chr(ord(ScanLine^) AND $0F);
  7356. inc(ScanLine);
  7357. end;
  7358. inc(Dest);
  7359. end;
  7360. end;
  7361. end;
  7362. procedure Import1Bit(Dest: PChar);
  7363. var
  7364. x, y : integer;
  7365. Scanline : PChar;
  7366. Bit : integer;
  7367. Byte : integer;
  7368. begin
  7369. // Copy colormap
  7370. FColorMap.ImportPalette(FBitmap.Palette);
  7371. // Copy pixels
  7372. for y := 0 to Height-1 do
  7373. begin
  7374. if ((y AND $1F) = 0) then
  7375. Image.Progress(Self, psRunning, MulDiv(y, 100, Height), False, Rect(0,0,0,0), sProgressConverting);
  7376. ScanLine := DIBSource.Scanline[y];
  7377. x := Width;
  7378. Bit := 0;
  7379. Byte := 0; // To avoid compiler warning
  7380. while (x > 0) do
  7381. begin
  7382. if (Bit = 0) then
  7383. begin
  7384. Bit := 8;
  7385. Byte := ord(ScanLine^);
  7386. inc(Scanline);
  7387. end;
  7388. Dest^ := chr((Byte AND $80) SHR 7);
  7389. Byte := Byte SHL 1;
  7390. inc(Dest);
  7391. dec(Bit);
  7392. dec(x);
  7393. end;
  7394. end;
  7395. end;
  7396. procedure Import24Bit(Dest: PChar);
  7397. type
  7398. TCacheEntry = record
  7399. Color : TColor;
  7400. Index : integer;
  7401. end;
  7402. const
  7403. // Size of palette cache. Must be 2^n.
  7404. // The cache holds the palette index of the last "CacheSize" colors
  7405. // processed. Hopefully the cache can speed things up a bit... Initial
  7406. // testing shows that this is indeed the case at least for non-dithered
  7407. // bitmaps.
  7408. // All the same, a small hash table would probably be much better.
  7409. CacheSize = 8;
  7410. var
  7411. i : integer;
  7412. Cache : array[0..CacheSize-1] of TCacheEntry;
  7413. LastEntry : integer;
  7414. Scanline : PRGBTriple;
  7415. Pixel : TColor;
  7416. RGBTriple : TRGBTriple absolute Pixel;
  7417. x, y : integer;
  7418. ColorMap : PColorMap;
  7419. t : byte;
  7420. label
  7421. NextPixel;
  7422. begin
  7423. for i := 0 to CacheSize-1 do
  7424. Cache[i].Index := -1;
  7425. LastEntry := 0;
  7426. // Copy all pixels and build colormap
  7427. for y := 0 to Height-1 do
  7428. begin
  7429. if ((y AND $1F) = 0) then
  7430. Image.Progress(Self, psRunning, MulDiv(y, 100, Height), False, Rect(0,0,0,0), sProgressConverting);
  7431. ScanLine := DIBSource.Scanline[y];
  7432. for x := 0 to Width-1 do
  7433. begin
  7434. Pixel := 0;
  7435. RGBTriple := Scanline^;
  7436. // Scan cache for color from most recently processed color to last
  7437. // recently processed. This is done because TColorMap.AddUnique is very slow.
  7438. i := LastEntry;
  7439. repeat
  7440. if (Cache[i].Index = -1) then
  7441. break;
  7442. if (Cache[i].Color = Pixel) then
  7443. begin
  7444. Dest^ := chr(Cache[i].Index);
  7445. LastEntry := i;
  7446. goto NextPixel;
  7447. end;
  7448. if (i = 0) then
  7449. i := CacheSize-1
  7450. else
  7451. dec(i);
  7452. until (i = LastEntry);
  7453. // Color not found in cache, do it the slow way instead
  7454. Dest^ := chr(FColorMap.AddUnique(Pixel));
  7455. // Add color and index to cache
  7456. LastEntry := (LastEntry + 1) AND (CacheSize-1);
  7457. Cache[LastEntry].Color := Pixel;
  7458. Cache[LastEntry].Index := ord(Dest^);
  7459. NextPixel:
  7460. Inc(Dest);
  7461. Inc(Scanline);
  7462. end;
  7463. end;
  7464. // Convert colors in colormap from BGR to RGB
  7465. ColorMap := FColorMap.Data;
  7466. i := FColorMap.Count;
  7467. while (i > 0) do
  7468. begin
  7469. t := ColorMap^[0].Red;
  7470. ColorMap^[0].Red := ColorMap^[0].Blue;
  7471. ColorMap^[0].Blue := t;
  7472. inc(integer(ColorMap), sizeof(TGIFColor));
  7473. dec(i);
  7474. end;
  7475. end;
  7476. procedure ImportViaDraw(ABitmap: TBitmap; Graphic: TGraphic);
  7477. begin
  7478. ABitmap.Height := Graphic.Height;
  7479. ABitmap.Width := Graphic.Width;
  7480. // Note: Disable the call to SafeSetPixelFormat below to import
  7481. // in max number of colors with the risk of having to use
  7482. // TCanvas.Pixels to do it (very slow).
  7483. // Make things a little easier for TGIFSubImage.Assign by converting
  7484. // pfDevice to a more import friendly format
  7485. {$ifdef SLOW_BUT_SAFE}
  7486. SafeSetPixelFormat(ABitmap, pf8bit);
  7487. {$else}
  7488. {$ifndef VER9x}
  7489. SetPixelFormat(ABitmap, pf24bit);
  7490. {$endif}
  7491. {$endif}
  7492. ABitmap.Canvas.Draw(0, 0, Graphic);
  7493. end;
  7494. procedure AddMask(Mask: TBitmap);
  7495. var
  7496. DIBReader : TDIBReader;
  7497. TransparentIndex : integer;
  7498. i ,
  7499. j : integer;
  7500. GIFPixel ,
  7501. MaskPixel : PChar;
  7502. WasTransparent : boolean;
  7503. GCE : TGIFGraphicControlExtension;
  7504. begin
  7505. // Optimize colormap to make room for transparent color
  7506. ColorMap.Optimize;
  7507. // Can't make transparent if no color or colormap full
  7508. if (ColorMap.Count = 0) or (ColorMap.Count = 256) then
  7509. exit;
  7510. // Add the transparent color to the color map
  7511. TransparentIndex := ColorMap.Add(TColor(0));
  7512. WasTransparent := False;
  7513. DIBReader := TDIBReader.Create(Mask, pf8bit);
  7514. try
  7515. for i := 0 to Height-1 do
  7516. begin
  7517. MaskPixel := DIBReader.Scanline[i];
  7518. GIFPixel := Scanline[i];
  7519. for j := 0 to Width-1 do
  7520. begin
  7521. // Change all unmasked pixels to transparent
  7522. if (MaskPixel^ <> #0) then
  7523. begin
  7524. GIFPixel^ := chr(TransparentIndex);
  7525. WasTransparent := True;
  7526. end;
  7527. inc(MaskPixel);
  7528. inc(GIFPixel);
  7529. end;
  7530. end;
  7531. finally
  7532. DIBReader.Free;
  7533. end;
  7534. // Add a Graphic Control Extension if any part of the mask was transparent
  7535. if (WasTransparent) then
  7536. begin
  7537. GCE := TGIFGraphicControlExtension.Create(self);
  7538. GCE.Transparent := True;
  7539. GCE.TransparentColorIndex := TransparentIndex;
  7540. Extensions.Add(GCE);
  7541. end else
  7542. // Otherwise removed the transparency color since it wasn't used
  7543. ColorMap.Delete(TransparentIndex);
  7544. end;
  7545. procedure AddMaskOnly(hMask: hBitmap);
  7546. var
  7547. Mask : TBitmap;
  7548. begin
  7549. if (hMask = 0) then
  7550. exit;
  7551. // Encapsulate the mask
  7552. Mask := TBitmap.Create;
  7553. try
  7554. // Mask.Handle := hMask; // 2003.08.04
  7555. Mask.Handle := Windows.CopyImage(hMask, IMAGE_BITMAP, 0, 0, LR_COPYRETURNORG); // 2003.08.04
  7556. AddMask(Mask);
  7557. finally
  7558. // Mask.ReleaseHandle; // 2003.08.04
  7559. Mask.Free;
  7560. end;
  7561. end;
  7562. procedure AddIconMask(Icon: TIcon);
  7563. var
  7564. IconInfo : TIconInfo;
  7565. begin
  7566. if (not GetIconInfo(Icon.Handle, IconInfo)) then
  7567. exit;
  7568. // Extract the icon mask
  7569. AddMaskOnly(IconInfo.hbmMask);
  7570. end;
  7571. procedure AddMetafileMask(Metafile: TMetaFile);
  7572. var
  7573. Mask1 ,
  7574. Mask2 : TBitmap;
  7575. procedure DrawMetafile(ABitmap: TBitmap; Background: TColor);
  7576. begin
  7577. ABitmap.Width := Metafile.Width;
  7578. ABitmap.Height := Metafile.Height;
  7579. {$ifndef VER9x}
  7580. SetPixelFormat(ABitmap, pf24bit);
  7581. {$endif}
  7582. ABitmap.Canvas.Brush.Color := Background;
  7583. ABitmap.Canvas.Brush.Style := bsSolid;
  7584. ABitmap.Canvas.FillRect(ABitmap.Canvas.ClipRect);
  7585. ABitmap.Canvas.Draw(0,0, Metafile);
  7586. end;
  7587. begin
  7588. // Create the metafile mask
  7589. Mask1 := TBitmap.Create;
  7590. try
  7591. Mask2 := TBitmap.Create;
  7592. try
  7593. DrawMetafile(Mask1, clWhite);
  7594. DrawMetafile(Mask2, clBlack);
  7595. Mask1.Canvas.CopyMode := cmSrcInvert;
  7596. Mask1.Canvas.Draw(0,0, Mask2);
  7597. AddMask(Mask1);
  7598. finally
  7599. Mask2.Free;
  7600. end;
  7601. finally
  7602. Mask1.Free;
  7603. end;
  7604. end;
  7605. begin
  7606. if (Source = self) then
  7607. exit;
  7608. if (Source = nil) then
  7609. begin
  7610. Clear;
  7611. end else
  7612. //
  7613. // TGIFSubImage import
  7614. //
  7615. if (Source is TGIFSubImage) then
  7616. begin
  7617. // Zap existing colormap, extensions and bitmap
  7618. Clear;
  7619. if (TGIFSubImage(Source).Empty) then
  7620. exit;
  7621. // Copy source data
  7622. FImageDescriptor := TGIFSubImage(Source).FImageDescriptor;
  7623. FTransparent := TGIFSubImage(Source).Transparent;
  7624. // Copy image data
  7625. NewImage;
  7626. if (FData <> nil) and (TGIFSubImage(Source).Data <> nil) then
  7627. Move(TGIFSubImage(Source).Data^, FData^, FDataSize);
  7628. // Copy palette
  7629. FColorMap.Assign(TGIFSubImage(Source).ColorMap);
  7630. // Copy extensions
  7631. if (TGIFSubImage(Source).Extensions.Count > 0) then
  7632. begin
  7633. MemoryStream := TMemoryStream.Create;
  7634. try
  7635. TGIFSubImage(Source).Extensions.SaveToStream(MemoryStream);
  7636. MemoryStream.Seek(0, soFromBeginning);
  7637. Extensions.LoadFromStream(MemoryStream, Self);
  7638. finally
  7639. MemoryStream.Free;
  7640. end;
  7641. end;
  7642. // Copy bitmap representation
  7643. // (Not really nescessary but improves performance if the bitmap is needed
  7644. // later on)
  7645. if (TGIFSubImage(Source).HasBitmap) then
  7646. begin
  7647. NewBitmap;
  7648. FBitmap.Assign(TGIFSubImage(Source).Bitmap);
  7649. end;
  7650. end else
  7651. //
  7652. // Bitmap import
  7653. //
  7654. if (Source is TBitmap) then
  7655. begin
  7656. // Zap existing colormap, extensions and bitmap
  7657. Clear;
  7658. if (TBitmap(Source).Empty) then
  7659. exit;
  7660. Width := TBitmap(Source).Width;
  7661. Height := TBitmap(Source).Height;
  7662. PixelFormat := GetPixelFormat(TBitmap(Source));
  7663. {$ifdef VER9x}
  7664. // Delphi 2 TBitmaps are always DDBs. This means that if a 24 bit
  7665. // bitmap is loaded in 8 bit device mode, TBitmap.PixelFormat will
  7666. // be pf8bit, but TBitmap.Palette will be 0!
  7667. if (TBitmap(Source).Palette = 0) then
  7668. PixelFormat := pfDevice;
  7669. {$endif}
  7670. if (PixelFormat > pf8bit) or (PixelFormat = pfDevice) then
  7671. begin
  7672. // Convert image to 8 bits/pixel or less
  7673. FBitmap := ReduceColors(TBitmap(Source), Image.ColorReduction,
  7674. Image.DitherMode, Image.ReductionBits, 0);
  7675. PixelFormat := GetPixelFormat(FBitmap);
  7676. end else
  7677. begin
  7678. // Create new bitmap and copy
  7679. NewBitmap;
  7680. FBitmap.Assign(TBitmap(Source));
  7681. end;
  7682. // Allocate new buffer
  7683. NewImage;
  7684. Image.Progress(Self, psStarting, 0, False, Rect(0,0,0,0), sProgressConverting);
  7685. try
  7686. {$ifdef VER9x}
  7687. // This shouldn't happen, but better safe...
  7688. if (FBitmap.Palette = 0) then
  7689. PixelFormat := pf24bit;
  7690. {$endif}
  7691. if (not(PixelFormat in [pf1bit, pf4bit, pf8bit, pf24bit])) then
  7692. PixelFormat := pf24bit;
  7693. DIBSource := TDIBReader.Create(FBitmap, PixelFormat);
  7694. try
  7695. // Copy pixels
  7696. case (PixelFormat) of
  7697. pf8bit: Import8Bit(Fdata);
  7698. pf4bit: Import4Bit(Fdata);
  7699. pf1bit: Import1Bit(Fdata);
  7700. else
  7701. // Error(sUnsupportedBitmap);
  7702. Import24Bit(Fdata);
  7703. end;
  7704. finally
  7705. DIBSource.Free;
  7706. end;
  7707. {$ifdef VER10_PLUS}
  7708. // Add mask for transparent bitmaps
  7709. if (TBitmap(Source).Transparent) then
  7710. AddMaskOnly(TBitmap(Source).MaskHandle);
  7711. {$endif}
  7712. finally
  7713. if ExceptObject = nil then
  7714. i := 100
  7715. else
  7716. i := 0;
  7717. Image.Progress(Self, psEnding, i, Image.PaletteModified, Rect(0,0,0,0), sProgressConverting);
  7718. end;
  7719. end else
  7720. //
  7721. // TGraphic import
  7722. //
  7723. if (Source is TGraphic) then
  7724. begin
  7725. // Zap existing colormap, extensions and bitmap
  7726. Clear;
  7727. if (TGraphic(Source).Empty) then
  7728. exit;
  7729. ABitmap := TBitmap.Create;
  7730. try
  7731. // Import TIcon and TMetafile by drawing them onto a bitmap...
  7732. // ...and then importing the bitmap recursively
  7733. if (Source is TIcon) or (Source is TMetafile) then
  7734. begin
  7735. try
  7736. ImportViaDraw(ABitmap, TGraphic(Source))
  7737. except
  7738. // If import via TCanvas.Draw fails (which it shouldn't), we try the
  7739. // Assign mechanism instead
  7740. ABitmap.Assign(Source);
  7741. end;
  7742. end else
  7743. try
  7744. ABitmap.Assign(Source);
  7745. except
  7746. // If automatic conversion to bitmap fails, we try and draw the
  7747. // graphic on the bitmap instead
  7748. ImportViaDraw(ABitmap, TGraphic(Source));
  7749. end;
  7750. // Convert the bitmap to a GIF frame recursively
  7751. Assign(ABitmap);
  7752. finally
  7753. ABitmap.Free;
  7754. end;
  7755. // Import transparency mask
  7756. if (Source is TIcon) then
  7757. AddIconMask(TIcon(Source));
  7758. if (Source is TMetaFile) then
  7759. AddMetafileMask(TMetaFile(Source));
  7760. end else
  7761. //
  7762. // TPicture import
  7763. //
  7764. if (Source is TPicture) then
  7765. begin
  7766. // Recursively import TGraphic
  7767. Assign(TPicture(Source).Graphic);
  7768. end else
  7769. // Unsupported format - fall back to Source.AssignTo
  7770. inherited Assign(Source);
  7771. end;
  7772. // Copied from D3 graphics.pas
  7773. // Fixed by Brian Lowe of Acro Technology Inc. 30Jan98
  7774. function TransparentStretchBlt(DstDC: HDC; DstX, DstY, DstW, DstH: Integer;
  7775. SrcDC: HDC; SrcX, SrcY, SrcW, SrcH: Integer; MaskDC: HDC; MaskX,
  7776. MaskY: Integer): Boolean;
  7777. const
  7778. ROP_DstCopy = $00AA0029;
  7779. var
  7780. MemDC ,
  7781. OrMaskDC : HDC;
  7782. MemBmp ,
  7783. OrMaskBmp : HBITMAP;
  7784. Save ,
  7785. OrMaskSave : THandle;
  7786. crText, crBack : TColorRef;
  7787. SavePal : HPALETTE;
  7788. begin
  7789. Result := True;
  7790. if (Win32Platform = VER_PLATFORM_WIN32_NT) and (SrcW = DstW) and (SrcH = DstH) then
  7791. begin
  7792. MemBmp := GDICheck(CreateCompatibleBitmap(SrcDC, 1, 1));
  7793. MemBmp := SelectObject(MaskDC, MemBmp);
  7794. try
  7795. MaskBlt(DstDC, DstX, DstY, DstW, DstH, SrcDC, SrcX, SrcY, MemBmp, MaskX,
  7796. MaskY, MakeRop4(ROP_DstCopy, SrcCopy));
  7797. finally
  7798. MemBmp := SelectObject(MaskDC, MemBmp);
  7799. DeleteObject(MemBmp);
  7800. end;
  7801. Exit;
  7802. end;
  7803. SavePal := 0;
  7804. MemDC := GDICheck(CreateCompatibleDC(DstDC));
  7805. try
  7806. { Color bitmap for combining OR mask with source bitmap }
  7807. MemBmp := GDICheck(CreateCompatibleBitmap(DstDC, SrcW, SrcH));
  7808. try
  7809. Save := SelectObject(MemDC, MemBmp);
  7810. try
  7811. { This bitmap needs the size of the source but DC of the dest }
  7812. OrMaskDC := GDICheck(CreateCompatibleDC(DstDC));
  7813. try
  7814. { Need a monochrome bitmap for OR mask!! }
  7815. OrMaskBmp := GDICheck(CreateBitmap(SrcW, SrcH, 1, 1, nil));
  7816. try
  7817. OrMaskSave := SelectObject(OrMaskDC, OrMaskBmp);
  7818. try
  7819. // OrMask := 1
  7820. // Original: BitBlt(OrMaskDC, SrcX, SrcY, SrcW, SrcH, OrMaskDC, SrcX, SrcY, WHITENESS);
  7821. // Replacement, but not needed: PatBlt(OrMaskDC, SrcX, SrcY, SrcW, SrcH, WHITENESS);
  7822. // OrMask := OrMask XOR Mask
  7823. // Not needed: BitBlt(OrMaskDC, SrcX, SrcY, SrcW, SrcH, MaskDC, SrcX, SrcY, SrcInvert);
  7824. // OrMask := NOT Mask
  7825. BitBlt(OrMaskDC, SrcX, SrcY, SrcW, SrcH, MaskDC, SrcX, SrcY, NotSrcCopy);
  7826. // Retrieve source palette (with dummy select)
  7827. SavePal := SelectPalette(SrcDC, SystemPalette16, False);
  7828. // Restore source palette
  7829. SelectPalette(SrcDC, SavePal, False);
  7830. // Select source palette into memory buffer
  7831. if SavePal <> 0 then
  7832. SavePal := SelectPalette(MemDC, SavePal, True)
  7833. else
  7834. SavePal := SelectPalette(MemDC, SystemPalette16, True);
  7835. RealizePalette(MemDC);
  7836. // Mem := OrMask
  7837. BitBlt(MemDC, SrcX, SrcY, SrcW, SrcH, OrMaskDC, SrcX, SrcY, SrcCopy);
  7838. // Mem := Mem AND Src
  7839. {$IFNDEF GIF_TESTMASK} // Define GIF_TESTMASK if you want to know what it does...
  7840. BitBlt(MemDC, SrcX, SrcY, SrcW, SrcH, SrcDC, SrcX, SrcY, SrcAnd);
  7841. {$ELSE}
  7842. StretchBlt(DstDC, DstX, DstY, DstW DIV 2, DstH, MemDC, SrcX, SrcY, SrcW, SrcH, SrcCopy);
  7843. StretchBlt(DstDC, DstX+DstW DIV 2, DstY, DstW DIV 2, DstH, SrcDC, SrcX, SrcY, SrcW, SrcH, SrcCopy);
  7844. exit;
  7845. {$ENDIF}
  7846. finally
  7847. if (OrMaskSave <> 0) then
  7848. SelectObject(OrMaskDC, OrMaskSave);
  7849. end;
  7850. finally
  7851. DeleteObject(OrMaskBmp);
  7852. end;
  7853. finally
  7854. DeleteDC(OrMaskDC);
  7855. end;
  7856. crText := SetTextColor(DstDC, $00000000);
  7857. crBack := SetBkColor(DstDC, $00FFFFFF);
  7858. { All color rendering is done at 1X (no stretching),
  7859. then final 2 masks are stretched to dest DC }
  7860. // Neat trick!
  7861. // Dst := Dst AND Mask
  7862. StretchBlt(DstDC, DstX, DstY, DstW, DstH, MaskDC, SrcX, SrcY, SrcW, SrcH, SrcAnd);
  7863. // Dst := Dst OR Mem
  7864. StretchBlt(DstDC, DstX, DstY, DstW, DstH, MemDC, SrcX, SrcY, SrcW, SrcH, SrcPaint);
  7865. SetTextColor(DstDC, crText);
  7866. SetTextColor(DstDC, crBack);
  7867. finally
  7868. if (Save <> 0) then
  7869. SelectObject(MemDC, Save);
  7870. end;
  7871. finally
  7872. DeleteObject(MemBmp);
  7873. end;
  7874. finally
  7875. if (SavePal <> 0) then
  7876. SelectPalette(MemDC, SavePal, False);
  7877. DeleteDC(MemDC);
  7878. end;
  7879. end;
  7880. procedure TGIFSubImage.Draw(ACanvas: TCanvas; const Rect: TRect;
  7881. DoTransparent, DoTile: boolean);
  7882. begin
  7883. if (DoTile) then
  7884. StretchDraw(ACanvas, Rect, DoTransparent, DoTile)
  7885. else
  7886. StretchDraw(ACanvas, ScaleRect(Rect), DoTransparent, DoTile);
  7887. end;
  7888. type
  7889. // Dummy class used to gain access to protected method TCanvas.Changed
  7890. TChangableCanvas = class(TCanvas)
  7891. end;
  7892. procedure TGIFSubImage.StretchDraw(ACanvas: TCanvas; const Rect: TRect;
  7893. DoTransparent, DoTile: boolean);
  7894. var
  7895. MaskDC : HDC;
  7896. Save : THandle;
  7897. Tile : TRect;
  7898. {$ifdef DEBUG_DRAWPERFORMANCE}
  7899. ImageCount ,
  7900. TimeStart ,
  7901. TimeStop : DWORD;
  7902. {$endif}
  7903. begin
  7904. {$ifdef DEBUG_DRAWPERFORMANCE}
  7905. TimeStart := timeGetTime;
  7906. ImageCount := 0;
  7907. {$endif}
  7908. if (DoTransparent) and (Transparent) and (HasMask) then
  7909. begin
  7910. // Draw transparent using mask
  7911. Save := 0;
  7912. MaskDC := 0;
  7913. try
  7914. MaskDC := GDICheck(CreateCompatibleDC(0));
  7915. Save := SelectObject(MaskDC, FMask);
  7916. if (DoTile) then
  7917. begin
  7918. Tile.Left := Rect.Left+Left;
  7919. Tile.Right := Tile.Left + Width;
  7920. while (Tile.Left < Rect.Right) do
  7921. begin
  7922. Tile.Top := Rect.Top+Top;
  7923. Tile.Bottom := Tile.Top + Height;
  7924. while (Tile.Top < Rect.Bottom) do
  7925. begin
  7926. TransparentStretchBlt(ACanvas.Handle, Tile.Left, Tile.Top, Width, Height,
  7927. Bitmap.Canvas.Handle, 0, 0, Width, Height, MaskDC, 0, 0);
  7928. Tile.Top := Tile.Top + Image.Height;
  7929. Tile.Bottom := Tile.Bottom + Image.Height;
  7930. {$ifdef DEBUG_DRAWPERFORMANCE}
  7931. inc(ImageCount);
  7932. {$endif}
  7933. end;
  7934. Tile.Left := Tile.Left + Image.Width;
  7935. Tile.Right := Tile.Right + Image.Width;
  7936. end;
  7937. end else
  7938. TransparentStretchBlt(ACanvas.Handle, Rect.Left, Rect.Top,
  7939. Rect.Right - Rect.Left, Rect.Bottom - Rect.Top,
  7940. Bitmap.Canvas.Handle, 0, 0, Width, Height, MaskDC, 0, 0);
  7941. // Since we are not using any of the TCanvas functions (only handle)
  7942. // we need to fire the TCanvas.Changed method "manually".
  7943. TChangableCanvas(ACanvas).Changed;
  7944. finally
  7945. if (Save <> 0) then
  7946. SelectObject(MaskDC, Save);
  7947. if (MaskDC <> 0) then
  7948. DeleteDC(MaskDC);
  7949. end;
  7950. end else
  7951. begin
  7952. if (DoTile) then
  7953. begin
  7954. Tile.Left := Rect.Left+Left;
  7955. Tile.Right := Tile.Left + Width;
  7956. while (Tile.Left < Rect.Right) do
  7957. begin
  7958. Tile.Top := Rect.Top+Top;
  7959. Tile.Bottom := Tile.Top + Height;
  7960. while (Tile.Top < Rect.Bottom) do
  7961. begin
  7962. ACanvas.StretchDraw(Tile, Bitmap);
  7963. Tile.Top := Tile.Top + Image.Height;
  7964. Tile.Bottom := Tile.Bottom + Image.Height;
  7965. {$ifdef DEBUG_DRAWPERFORMANCE}
  7966. inc(ImageCount);
  7967. {$endif}
  7968. end;
  7969. Tile.Left := Tile.Left + Image.Width;
  7970. Tile.Right := Tile.Right + Image.Width;
  7971. end;
  7972. end else
  7973. ACanvas.StretchDraw(Rect, Bitmap);
  7974. end;
  7975. {$ifdef DEBUG_DRAWPERFORMANCE}
  7976. if (GetAsyncKeyState(VK_CONTROL) <> 0) then
  7977. begin
  7978. TimeStop := timeGetTime;
  7979. ShowMessage(format('Draw %d images in %d mS, Rate %d images/mS (%d images/S)',
  7980. [ImageCount, TimeStop-TimeStart,
  7981. ImageCount DIV (TimeStop-TimeStart+1),
  7982. MulDiv(ImageCount, 1000, TimeStop-TimeStart+1)]));
  7983. end;
  7984. {$endif}
  7985. end;
  7986. // Given a destination rect (DestRect) calculates the
  7987. // area covered by this sub image
  7988. function TGIFSubImage.ScaleRect(DestRect: TRect): TRect;
  7989. var
  7990. HeightMul ,
  7991. HeightDiv : integer;
  7992. WidthMul ,
  7993. WidthDiv : integer;
  7994. begin
  7995. HeightDiv := Image.Height;
  7996. HeightMul := DestRect.Bottom-DestRect.Top;
  7997. WidthDiv := Image.Width;
  7998. WidthMul := DestRect.Right-DestRect.Left;
  7999. Result.Left := DestRect.Left + muldiv(Left, WidthMul, WidthDiv);
  8000. Result.Top := DestRect.Top + muldiv(Top, HeightMul, HeightDiv);
  8001. Result.Right := DestRect.Left + muldiv(Left+Width, WidthMul, WidthDiv);
  8002. Result.Bottom := DestRect.Top + muldiv(Top+Height, HeightMul, HeightDiv);
  8003. end;
  8004. procedure TGIFSubImage.Crop;
  8005. var
  8006. TransparentColorIndex : byte;
  8007. CropLeft ,
  8008. CropTop ,
  8009. CropRight ,
  8010. CropBottom : integer;
  8011. WasTransparent : boolean;
  8012. i : integer;
  8013. NewSize : integer;
  8014. NewData : PChar;
  8015. NewWidth ,
  8016. NewHeight : integer;
  8017. pSource ,
  8018. pDest : PChar;
  8019. begin
  8020. if (Empty) or (not Transparent) then
  8021. exit;
  8022. TransparentColorIndex := GraphicControlExtension.TransparentColorIndex;
  8023. CropLeft := 0;
  8024. CropRight := Width - 1;
  8025. CropTop := 0;
  8026. CropBottom := Height - 1;
  8027. // Find left edge
  8028. WasTransparent := True;
  8029. while (CropLeft <= CropRight) and (WasTransparent) do
  8030. begin
  8031. for i := CropTop to CropBottom do
  8032. if (Pixels[CropLeft, i] <> TransparentColorIndex) then
  8033. begin
  8034. WasTransparent := False;
  8035. break;
  8036. end;
  8037. if (WasTransparent) then
  8038. inc(CropLeft);
  8039. end;
  8040. // Find right edge
  8041. WasTransparent := True;
  8042. while (CropLeft <= CropRight) and (WasTransparent) do
  8043. begin
  8044. for i := CropTop to CropBottom do
  8045. if (pixels[CropRight, i] <> TransparentColorIndex) then
  8046. begin
  8047. WasTransparent := False;
  8048. break;
  8049. end;
  8050. if (WasTransparent) then
  8051. dec(CropRight);
  8052. end;
  8053. if (CropLeft <= CropRight) then
  8054. begin
  8055. // Find top edge
  8056. WasTransparent := True;
  8057. while (CropTop <= CropBottom) and (WasTransparent) do
  8058. begin
  8059. for i := CropLeft to CropRight do
  8060. if (pixels[i, CropTop] <> TransparentColorIndex) then
  8061. begin
  8062. WasTransparent := False;
  8063. break;
  8064. end;
  8065. if (WasTransparent) then
  8066. inc(CropTop);
  8067. end;
  8068. // Find bottom edge
  8069. WasTransparent := True;
  8070. while (CropTop <= CropBottom) and (WasTransparent) do
  8071. begin
  8072. for i := CropLeft to CropRight do
  8073. if (pixels[i, CropBottom] <> TransparentColorIndex) then
  8074. begin
  8075. WasTransparent := False;
  8076. break;
  8077. end;
  8078. if (WasTransparent) then
  8079. dec(CropBottom);
  8080. end;
  8081. end;
  8082. if (CropLeft > CropRight) or (CropTop > CropBottom) then
  8083. begin
  8084. // Cropped to nothing - frame is invisible
  8085. Clear;
  8086. end else
  8087. begin
  8088. // Crop frame - move data
  8089. NewWidth := CropRight - CropLeft + 1;
  8090. Newheight := CropBottom - CropTop + 1;
  8091. NewSize := NewWidth * NewHeight;
  8092. GetMem(NewData, NewSize);
  8093. pSource := PChar(integer(FData) + CropTop * Width + CropLeft);
  8094. pDest := NewData;
  8095. for i := 0 to NewHeight-1 do
  8096. begin
  8097. Move(pSource^, pDest^, NewWidth);
  8098. inc(pSource, Width);
  8099. inc(pDest, NewWidth);
  8100. end;
  8101. FreeImage;
  8102. FData := NewData;
  8103. FDataSize := NewSize;
  8104. inc(FImageDescriptor.Left, CropLeft);
  8105. inc(FImageDescriptor.Top, CropTop);
  8106. FImageDescriptor.Width := NewWidth;
  8107. FImageDescriptor.Height := NewHeight;
  8108. FreeBitmap;
  8109. FreeMask
  8110. end;
  8111. end;
  8112. procedure TGIFSubImage.Merge(Previous: TGIFSubImage);
  8113. var
  8114. SourceIndex ,
  8115. DestIndex : byte;
  8116. SourceTransparent : boolean;
  8117. NeedTransparentColorIndex: boolean;
  8118. PreviousRect ,
  8119. ThisRect ,
  8120. MergeRect : TRect;
  8121. PreviousY ,
  8122. X ,
  8123. Y : integer;
  8124. pSource ,
  8125. pDest : PChar;
  8126. pSourceMap ,
  8127. pDestMap : PColorMap;
  8128. GCE : TGIFGraphicControlExtension;
  8129. function CanMakeTransparent: boolean;
  8130. begin
  8131. // Is there a local color map...
  8132. if (ColorMap.Count > 0) then
  8133. // ...and is there room in it?
  8134. Result := (ColorMap.Count < 256)
  8135. // Is there a global color map...
  8136. else if (Image.GlobalColorMap.Count > 0) then
  8137. // ...and is there room in it?
  8138. Result := (Image.GlobalColorMap.Count < 256)
  8139. else
  8140. Result := False;
  8141. end;
  8142. function GetTransparentColorIndex: byte;
  8143. var
  8144. i : integer;
  8145. begin
  8146. if (ColorMap.Count > 0) then
  8147. begin
  8148. // Get the transparent color from the local color map
  8149. Result := ColorMap.Add(TColor(0));
  8150. end else
  8151. begin
  8152. // Are any other frames using the global color map for transparency
  8153. for i := 0 to Image.Images.Count-1 do
  8154. if (Image.Images[i] <> self) and (Image.Images[i].Transparent) and
  8155. (Image.Images[i].ColorMap.Count = 0) then
  8156. begin
  8157. // Use the same transparency color as the other frame
  8158. Result := Image.Images[i].GraphicControlExtension.TransparentColorIndex;
  8159. exit;
  8160. end;
  8161. // Get the transparent color from the global color map
  8162. Result := Image.GlobalColorMap.Add(TColor(0));
  8163. end;
  8164. end;
  8165. begin
  8166. // Determine if it is possible to merge this frame
  8167. if (Empty) or (Previous = nil) or (Previous.Empty) or
  8168. ((Previous.GraphicControlExtension <> nil) and
  8169. (Previous.GraphicControlExtension.Disposal in [dmBackground, dmPrevious])) then
  8170. exit;
  8171. PreviousRect := Previous.BoundsRect;
  8172. ThisRect := BoundsRect;
  8173. // Cannot merge unless the frames intersect
  8174. if (not IntersectRect(MergeRect, PreviousRect, ThisRect)) then
  8175. exit;
  8176. // If the frame isn't already transparent, determine
  8177. // if it is possible to make it so
  8178. if (Transparent) then
  8179. begin
  8180. DestIndex := GraphicControlExtension.TransparentColorIndex;
  8181. NeedTransparentColorIndex := False;
  8182. end else
  8183. begin
  8184. if (not CanMakeTransparent) then
  8185. exit;
  8186. DestIndex := 0; // To avoid compiler warning
  8187. NeedTransparentColorIndex := True;
  8188. end;
  8189. SourceTransparent := Previous.Transparent;
  8190. if (SourceTransparent) then
  8191. SourceIndex := Previous.GraphicControlExtension.TransparentColorIndex
  8192. else
  8193. SourceIndex := 0; // To avoid compiler warning
  8194. PreviousY := MergeRect.Top - Previous.Top;
  8195. pSourceMap := Previous.ActiveColorMap.Data;
  8196. pDestMap := ActiveColorMap.Data;
  8197. for Y := MergeRect.Top - Top to MergeRect.Bottom - Top-1 do
  8198. begin
  8199. pSource := PChar(integer(Previous.Scanline[PreviousY]) + MergeRect.Left - Previous.Left);
  8200. pDest := PChar(integer(Scanline[Y]) + MergeRect.Left - Left);
  8201. for X := MergeRect.Left to MergeRect.Right-1 do
  8202. begin
  8203. // Ignore pixels if either this frame's or the previous frame's pixel is transparent
  8204. if (
  8205. not(
  8206. ((not NeedTransparentColorIndex) and (pDest^ = char(DestIndex))) or
  8207. ((SourceTransparent) and (pSource^ = char(SourceIndex)))
  8208. )
  8209. ) and (
  8210. // Replace same colored pixels with transparency
  8211. ((pDestMap = pSourceMap) and (pDest^ = pSource^)) or
  8212. (CompareMem(@(pDestMap^[ord(pDest^)]), @(pSourceMap^[ord(pSource^)]), sizeof(TGIFColor)))
  8213. ) then
  8214. begin
  8215. if (NeedTransparentColorIndex) then
  8216. begin
  8217. NeedTransparentColorIndex := False;
  8218. DestIndex := GetTransparentColorIndex;
  8219. end;
  8220. pDest^ := char(DestIndex);
  8221. end;
  8222. inc(pDest);
  8223. inc(pSource);
  8224. end;
  8225. inc(PreviousY);
  8226. end;
  8227. (*
  8228. ** Create a GCE if the frame wasn't already transparent and any
  8229. ** pixels were made transparent
  8230. *)
  8231. if (not Transparent) and (not NeedTransparentColorIndex) then
  8232. begin
  8233. if (GraphicControlExtension = nil) then
  8234. begin
  8235. GCE := TGIFGraphicControlExtension.Create(self);
  8236. Extensions.Add(GCE);
  8237. end else
  8238. GCE := GraphicControlExtension;
  8239. GCE.Transparent := True;
  8240. GCE.TransparentColorIndex := DestIndex;
  8241. end;
  8242. FreeBitmap;
  8243. FreeMask
  8244. end;
  8245. ////////////////////////////////////////////////////////////////////////////////
  8246. //
  8247. // TGIFTrailer
  8248. //
  8249. ////////////////////////////////////////////////////////////////////////////////
  8250. procedure TGIFTrailer.SaveToStream(Stream: TStream);
  8251. begin
  8252. WriteByte(Stream, bsTrailer);
  8253. end;
  8254. procedure TGIFTrailer.LoadFromStream(Stream: TStream);
  8255. var
  8256. b : BYTE;
  8257. begin
  8258. if (Stream.Read(b, 1) <> 1) then
  8259. exit;
  8260. if (b <> bsTrailer) then
  8261. Warning(gsWarning, sBadTrailer);
  8262. end;
  8263. ////////////////////////////////////////////////////////////////////////////////
  8264. //
  8265. // TGIFExtension registration database
  8266. //
  8267. ////////////////////////////////////////////////////////////////////////////////
  8268. type
  8269. TExtensionLeadIn = packed record
  8270. Introducer: byte; { always $21 }
  8271. ExtensionLabel: byte;
  8272. end;
  8273. PExtRec = ^TExtRec;
  8274. TExtRec = record
  8275. ExtClass: TGIFExtensionClass;
  8276. ExtLabel: BYTE;
  8277. end;
  8278. TExtensionList = class(TList)
  8279. public
  8280. constructor Create;
  8281. destructor Destroy; override;
  8282. procedure Add(eLabel: BYTE; eClass: TGIFExtensionClass);
  8283. function FindExt(eLabel: BYTE): TGIFExtensionClass;
  8284. procedure Remove(eClass: TGIFExtensionClass);
  8285. end;
  8286. constructor TExtensionList.Create;
  8287. begin
  8288. inherited Create;
  8289. Add(bsPlainTextExtension, TGIFTextExtension);
  8290. Add(bsGraphicControlExtension, TGIFGraphicControlExtension);
  8291. Add(bsCommentExtension, TGIFCommentExtension);
  8292. Add(bsApplicationExtension, TGIFApplicationExtension);
  8293. end;
  8294. destructor TExtensionList.Destroy;
  8295. var
  8296. I: Integer;
  8297. begin
  8298. for I := 0 to Count-1 do
  8299. Dispose(PExtRec(Items[I]));
  8300. inherited Destroy;
  8301. end;
  8302. procedure TExtensionList.Add(eLabel: BYTE; eClass: TGIFExtensionClass);
  8303. var
  8304. NewRec: PExtRec;
  8305. begin
  8306. New(NewRec);
  8307. with NewRec^ do
  8308. begin
  8309. ExtLabel := eLabel;
  8310. ExtClass := eClass;
  8311. end;
  8312. inherited Add(NewRec);
  8313. end;
  8314. function TExtensionList.FindExt(eLabel: BYTE): TGIFExtensionClass;
  8315. var
  8316. I: Integer;
  8317. begin
  8318. for I := Count-1 downto 0 do
  8319. with PExtRec(Items[I])^ do
  8320. if ExtLabel = eLabel then
  8321. begin
  8322. Result := ExtClass;
  8323. Exit;
  8324. end;
  8325. Result := nil;
  8326. end;
  8327. procedure TExtensionList.Remove(eClass: TGIFExtensionClass);
  8328. var
  8329. I: Integer;
  8330. P: PExtRec;
  8331. begin
  8332. for I := Count-1 downto 0 do
  8333. begin
  8334. P := PExtRec(Items[I]);
  8335. if P^.ExtClass.InheritsFrom(eClass) then
  8336. begin
  8337. Dispose(P);
  8338. Delete(I);
  8339. end;
  8340. end;
  8341. end;
  8342. var
  8343. ExtensionList: TExtensionList = nil;
  8344. function GetExtensionList: TExtensionList;
  8345. begin
  8346. if (ExtensionList = nil) then
  8347. ExtensionList := TExtensionList.Create;
  8348. Result := ExtensionList;
  8349. end;
  8350. ////////////////////////////////////////////////////////////////////////////////
  8351. //
  8352. // TGIFExtension
  8353. //
  8354. ////////////////////////////////////////////////////////////////////////////////
  8355. function TGIFExtension.GetVersion: TGIFVersion;
  8356. begin
  8357. Result := gv89a;
  8358. end;
  8359. class procedure TGIFExtension.RegisterExtension(eLabel: BYTE; eClass: TGIFExtensionClass);
  8360. begin
  8361. GetExtensionList.Add(eLabel, eClass);
  8362. end;
  8363. class function TGIFExtension.FindExtension(Stream: TStream): TGIFExtensionClass;
  8364. var
  8365. eLabel : BYTE;
  8366. SubClass : TGIFExtensionClass;
  8367. Pos : LongInt;
  8368. begin
  8369. Pos := Stream.Position;
  8370. if (Stream.Read(eLabel, 1) <> 1) then
  8371. begin
  8372. Result := nil;
  8373. exit;
  8374. end;
  8375. Result := GetExtensionList.FindExt(eLabel);
  8376. while (Result <> nil) do
  8377. begin
  8378. SubClass := Result.FindSubExtension(Stream);
  8379. if (SubClass = Result) then
  8380. break;
  8381. Result := SubClass;
  8382. end;
  8383. Stream.Position := Pos;
  8384. end;
  8385. class function TGIFExtension.FindSubExtension(Stream: TStream): TGIFExtensionClass;
  8386. begin
  8387. Result := self;
  8388. end;
  8389. constructor TGIFExtension.Create(ASubImage: TGIFSubImage);
  8390. begin
  8391. inherited Create(ASubImage.Image);
  8392. FSubImage := ASubImage;
  8393. end;
  8394. destructor TGIFExtension.Destroy;
  8395. begin
  8396. if (FSubImage <> nil) then
  8397. FSubImage.Extensions.Remove(self);
  8398. inherited Destroy;
  8399. end;
  8400. procedure TGIFExtension.SaveToStream(Stream: TStream);
  8401. var
  8402. ExtensionLeadIn : TExtensionLeadIn;
  8403. begin
  8404. ExtensionLeadIn.Introducer := bsExtensionIntroducer;
  8405. ExtensionLeadIn.ExtensionLabel := ExtensionType;
  8406. Stream.Write(ExtensionLeadIn, sizeof(ExtensionLeadIn));
  8407. end;
  8408. function TGIFExtension.DoReadFromStream(Stream: TStream): TGIFExtensionType;
  8409. var
  8410. ExtensionLeadIn : TExtensionLeadIn;
  8411. begin
  8412. ReadCheck(Stream, ExtensionLeadIn, sizeof(ExtensionLeadIn));
  8413. if (ExtensionLeadIn.Introducer <> bsExtensionIntroducer) then
  8414. Error(sBadExtensionLabel);
  8415. Result := ExtensionLeadIn.ExtensionLabel;
  8416. end;
  8417. procedure TGIFExtension.LoadFromStream(Stream: TStream);
  8418. begin
  8419. // Seek past lead-in
  8420. // Stream.Seek(sizeof(TExtensionLeadIn), soFromCurrent);
  8421. if (DoReadFromStream(Stream) <> ExtensionType) then
  8422. Error(sBadExtensionInstance);
  8423. end;
  8424. ////////////////////////////////////////////////////////////////////////////////
  8425. //
  8426. // TGIFGraphicControlExtension
  8427. //
  8428. ////////////////////////////////////////////////////////////////////////////////
  8429. const
  8430. { Extension flag bit masks }
  8431. efInputFlag = $02; { 00000010 }
  8432. efDisposal = $1C; { 00011100 }
  8433. efTransparent = $01; { 00000001 }
  8434. efReserved = $E0; { 11100000 }
  8435. constructor TGIFGraphicControlExtension.Create(ASubImage: TGIFSubImage);
  8436. begin
  8437. inherited Create(ASubImage);
  8438. FGCExtension.BlockSize := 4;
  8439. FGCExtension.PackedFields := $00;
  8440. FGCExtension.DelayTime := 0;
  8441. FGCExtension.TransparentColorIndex := 0;
  8442. FGCExtension.Terminator := 0;
  8443. if (ASubImage.FGCE = nil) then
  8444. ASubImage.FGCE := self;
  8445. end;
  8446. destructor TGIFGraphicControlExtension.Destroy;
  8447. begin
  8448. // Clear transparent flag in sub image
  8449. if (Transparent) then
  8450. SubImage.FTransparent := False;
  8451. if (SubImage.FGCE = self) then
  8452. SubImage.FGCE := nil;
  8453. inherited Destroy;
  8454. end;
  8455. function TGIFGraphicControlExtension.GetExtensionType: TGIFExtensionType;
  8456. begin
  8457. Result := bsGraphicControlExtension;
  8458. end;
  8459. function TGIFGraphicControlExtension.GetTransparent: boolean;
  8460. begin
  8461. Result := (FGCExtension.PackedFields AND efTransparent) <> 0;
  8462. end;
  8463. procedure TGIFGraphicControlExtension.SetTransparent(Value: boolean);
  8464. begin
  8465. // Set transparent flag in sub image
  8466. SubImage.FTransparent := Value;
  8467. if (Value) then
  8468. FGCExtension.PackedFields := FGCExtension.PackedFields OR efTransparent
  8469. else
  8470. FGCExtension.PackedFields := FGCExtension.PackedFields AND NOT(efTransparent);
  8471. end;
  8472. function TGIFGraphicControlExtension.GetTransparentColor: TColor;
  8473. begin
  8474. Result := SubImage.ActiveColorMap[TransparentColorIndex];
  8475. end;
  8476. procedure TGIFGraphicControlExtension.SetTransparentColor(Color: TColor);
  8477. begin
  8478. FGCExtension.TransparentColorIndex := Subimage.ActiveColorMap.AddUnique(Color);
  8479. end;
  8480. function TGIFGraphicControlExtension.GetTransparentColorIndex: BYTE;
  8481. begin
  8482. Result := FGCExtension.TransparentColorIndex;
  8483. end;
  8484. procedure TGIFGraphicControlExtension.SetTransparentColorIndex(Value: BYTE);
  8485. begin
  8486. if ((Value >= SubImage.ActiveColorMap.Count) and (SubImage.ActiveColorMap.Count > 0)) then
  8487. begin
  8488. Warning(gsWarning, sBadColorIndex);
  8489. Value := 0;
  8490. end;
  8491. FGCExtension.TransparentColorIndex := Value;
  8492. end;
  8493. function TGIFGraphicControlExtension.GetDelay: WORD;
  8494. begin
  8495. Result := FGCExtension.DelayTime;
  8496. end;
  8497. procedure TGIFGraphicControlExtension.SetDelay(Value: WORD);
  8498. begin
  8499. FGCExtension.DelayTime := Value;
  8500. end;
  8501. function TGIFGraphicControlExtension.GetUserInput: boolean;
  8502. begin
  8503. Result := (FGCExtension.PackedFields AND efInputFlag) <> 0;
  8504. end;
  8505. procedure TGIFGraphicControlExtension.SetUserInput(Value: boolean);
  8506. begin
  8507. if (Value) then
  8508. FGCExtension.PackedFields := FGCExtension.PackedFields OR efInputFlag
  8509. else
  8510. FGCExtension.PackedFields := FGCExtension.PackedFields AND NOT(efInputFlag);
  8511. end;
  8512. function TGIFGraphicControlExtension.GetDisposal: TDisposalMethod;
  8513. begin
  8514. Result := TDisposalMethod((FGCExtension.PackedFields AND efDisposal) SHR 2);
  8515. end;
  8516. procedure TGIFGraphicControlExtension.SetDisposal(Value: TDisposalMethod);
  8517. begin
  8518. FGCExtension.PackedFields := FGCExtension.PackedFields AND NOT(efDisposal)
  8519. OR ((ord(Value) SHL 2) AND efDisposal);
  8520. end;
  8521. procedure TGIFGraphicControlExtension.SaveToStream(Stream: TStream);
  8522. begin
  8523. inherited SaveToStream(Stream);
  8524. Stream.Write(FGCExtension, sizeof(FGCExtension));
  8525. end;
  8526. procedure TGIFGraphicControlExtension.LoadFromStream(Stream: TStream);
  8527. begin
  8528. inherited LoadFromStream(Stream);
  8529. if (Stream.Read(FGCExtension, sizeof(FGCExtension)) <> sizeof(FGCExtension)) then
  8530. begin
  8531. Warning(gsWarning, sOutOfData);
  8532. exit;
  8533. end;
  8534. // Set transparent flag in sub image
  8535. if (Transparent) then
  8536. SubImage.FTransparent := True;
  8537. end;
  8538. ////////////////////////////////////////////////////////////////////////////////
  8539. //
  8540. // TGIFTextExtension
  8541. //
  8542. ////////////////////////////////////////////////////////////////////////////////
  8543. constructor TGIFTextExtension.Create(ASubImage: TGIFSubImage);
  8544. begin
  8545. inherited Create(ASubImage);
  8546. FText := TStringList.Create;
  8547. FPlainTextExtension.BlockSize := 12;
  8548. FPlainTextExtension.Left := 0;
  8549. FPlainTextExtension.Top := 0;
  8550. FPlainTextExtension.Width := 0;
  8551. FPlainTextExtension.Height := 0;
  8552. FPlainTextExtension.CellWidth := 0;
  8553. FPlainTextExtension.CellHeight := 0;
  8554. FPlainTextExtension.TextFGColorIndex := 0;
  8555. FPlainTextExtension.TextBGColorIndex := 0;
  8556. end;
  8557. destructor TGIFTextExtension.Destroy;
  8558. begin
  8559. FText.Free;
  8560. inherited Destroy;
  8561. end;
  8562. function TGIFTextExtension.GetExtensionType: TGIFExtensionType;
  8563. begin
  8564. Result := bsPlainTextExtension;
  8565. end;
  8566. function TGIFTextExtension.GetForegroundColor: TColor;
  8567. begin
  8568. Result := SubImage.ColorMap[ForegroundColorIndex];
  8569. end;
  8570. procedure TGIFTextExtension.SetForegroundColor(Color: TColor);
  8571. begin
  8572. ForegroundColorIndex := SubImage.ActiveColorMap.AddUnique(Color);
  8573. end;
  8574. function TGIFTextExtension.GetBackgroundColor: TColor;
  8575. begin
  8576. Result := SubImage.ActiveColorMap[BackgroundColorIndex];
  8577. end;
  8578. procedure TGIFTextExtension.SetBackgroundColor(Color: TColor);
  8579. begin
  8580. BackgroundColorIndex := SubImage.ColorMap.AddUnique(Color);
  8581. end;
  8582. function TGIFTextExtension.GetBounds(Index: integer): WORD;
  8583. begin
  8584. case (Index) of
  8585. 1: Result := FPlainTextExtension.Left;
  8586. 2: Result := FPlainTextExtension.Top;
  8587. 3: Result := FPlainTextExtension.Width;
  8588. 4: Result := FPlainTextExtension.Height;
  8589. else
  8590. Result := 0; // To avoid compiler warnings
  8591. end;
  8592. end;
  8593. procedure TGIFTextExtension.SetBounds(Index: integer; Value: WORD);
  8594. begin
  8595. case (Index) of
  8596. 1: FPlainTextExtension.Left := Value;
  8597. 2: FPlainTextExtension.Top := Value;
  8598. 3: FPlainTextExtension.Width := Value;
  8599. 4: FPlainTextExtension.Height := Value;
  8600. end;
  8601. end;
  8602. function TGIFTextExtension.GetCharWidthHeight(Index: integer): BYTE;
  8603. begin
  8604. case (Index) of
  8605. 1: Result := FPlainTextExtension.CellWidth;
  8606. 2: Result := FPlainTextExtension.CellHeight;
  8607. else
  8608. Result := 0; // To avoid compiler warnings
  8609. end;
  8610. end;
  8611. procedure TGIFTextExtension.SetCharWidthHeight(Index: integer; Value: BYTE);
  8612. begin
  8613. case (Index) of
  8614. 1: FPlainTextExtension.CellWidth := Value;
  8615. 2: FPlainTextExtension.CellHeight := Value;
  8616. end;
  8617. end;
  8618. function TGIFTextExtension.GetColorIndex(Index: integer): BYTE;
  8619. begin
  8620. case (Index) of
  8621. 1: Result := FPlainTextExtension.TextFGColorIndex;
  8622. 2: Result := FPlainTextExtension.TextBGColorIndex;
  8623. else
  8624. Result := 0; // To avoid compiler warnings
  8625. end;
  8626. end;
  8627. procedure TGIFTextExtension.SetColorIndex(Index: integer; Value: BYTE);
  8628. begin
  8629. case (Index) of
  8630. 1: FPlainTextExtension.TextFGColorIndex := Value;
  8631. 2: FPlainTextExtension.TextBGColorIndex := Value;
  8632. end;
  8633. end;
  8634. procedure TGIFTextExtension.SaveToStream(Stream: TStream);
  8635. begin
  8636. inherited SaveToStream(Stream);
  8637. Stream.Write(FPlainTextExtension, sizeof(FPlainTextExtension));
  8638. WriteStrings(Stream, FText);
  8639. end;
  8640. procedure TGIFTextExtension.LoadFromStream(Stream: TStream);
  8641. begin
  8642. inherited LoadFromStream(Stream);
  8643. ReadCheck(Stream, FPlainTextExtension, sizeof(FPlainTextExtension));
  8644. ReadStrings(Stream, FText);
  8645. end;
  8646. ////////////////////////////////////////////////////////////////////////////////
  8647. //
  8648. // TGIFCommentExtension
  8649. //
  8650. ////////////////////////////////////////////////////////////////////////////////
  8651. constructor TGIFCommentExtension.Create(ASubImage: TGIFSubImage);
  8652. begin
  8653. inherited Create(ASubImage);
  8654. FText := TStringList.Create;
  8655. end;
  8656. destructor TGIFCommentExtension.Destroy;
  8657. begin
  8658. FText.Free;
  8659. inherited Destroy;
  8660. end;
  8661. function TGIFCommentExtension.GetExtensionType: TGIFExtensionType;
  8662. begin
  8663. Result := bsCommentExtension;
  8664. end;
  8665. procedure TGIFCommentExtension.SaveToStream(Stream: TStream);
  8666. begin
  8667. inherited SaveToStream(Stream);
  8668. WriteStrings(Stream, FText);
  8669. end;
  8670. procedure TGIFCommentExtension.LoadFromStream(Stream: TStream);
  8671. begin
  8672. inherited LoadFromStream(Stream);
  8673. ReadStrings(Stream, FText);
  8674. end;
  8675. ////////////////////////////////////////////////////////////////////////////////
  8676. //
  8677. // TGIFApplicationExtension registration database
  8678. //
  8679. ////////////////////////////////////////////////////////////////////////////////
  8680. type
  8681. PAppExtRec = ^TAppExtRec;
  8682. TAppExtRec = record
  8683. AppClass: TGIFAppExtensionClass;
  8684. Ident: TGIFApplicationRec;
  8685. end;
  8686. TAppExtensionList = class(TList)
  8687. public
  8688. constructor Create;
  8689. destructor Destroy; override;
  8690. procedure Add(eIdent: TGIFApplicationRec; eClass: TGIFAppExtensionClass);
  8691. function FindExt(eIdent: TGIFApplicationRec): TGIFAppExtensionClass;
  8692. procedure Remove(eClass: TGIFAppExtensionClass);
  8693. end;
  8694. constructor TAppExtensionList.Create;
  8695. const
  8696. NSLoopIdent: array[0..1] of TGIFApplicationRec =
  8697. ((Identifier: 'NETSCAPE'; Authentication: '2.0'),
  8698. (Identifier: 'ANIMEXTS'; Authentication: '1.0'));
  8699. begin
  8700. inherited Create;
  8701. Add(NSLoopIdent[0], TGIFAppExtNSLoop);
  8702. Add(NSLoopIdent[1], TGIFAppExtNSLoop);
  8703. end;
  8704. destructor TAppExtensionList.Destroy;
  8705. var
  8706. I: Integer;
  8707. begin
  8708. for I := 0 to Count-1 do
  8709. Dispose(PAppExtRec(Items[I]));
  8710. inherited Destroy;
  8711. end;
  8712. procedure TAppExtensionList.Add(eIdent: TGIFApplicationRec; eClass: TGIFAppExtensionClass);
  8713. var
  8714. NewRec: PAppExtRec;
  8715. begin
  8716. New(NewRec);
  8717. NewRec^.Ident := eIdent;
  8718. NewRec^.AppClass := eClass;
  8719. inherited Add(NewRec);
  8720. end;
  8721. function TAppExtensionList.FindExt(eIdent: TGIFApplicationRec): TGIFAppExtensionClass;
  8722. var
  8723. I: Integer;
  8724. begin
  8725. for I := Count-1 downto 0 do
  8726. with PAppExtRec(Items[I])^ do
  8727. if CompareMem(@Ident, @eIdent, sizeof(TGIFApplicationRec)) then
  8728. begin
  8729. Result := AppClass;
  8730. Exit;
  8731. end;
  8732. Result := nil;
  8733. end;
  8734. procedure TAppExtensionList.Remove(eClass: TGIFAppExtensionClass);
  8735. var
  8736. I: Integer;
  8737. P: PAppExtRec;
  8738. begin
  8739. for I := Count-1 downto 0 do
  8740. begin
  8741. P := PAppExtRec(Items[I]);
  8742. if P^.AppClass.InheritsFrom(eClass) then
  8743. begin
  8744. Dispose(P);
  8745. Delete(I);
  8746. end;
  8747. end;
  8748. end;
  8749. var
  8750. AppExtensionList: TAppExtensionList = nil;
  8751. function GetAppExtensionList: TAppExtensionList;
  8752. begin
  8753. if (AppExtensionList = nil) then
  8754. AppExtensionList := TAppExtensionList.Create;
  8755. Result := AppExtensionList;
  8756. end;
  8757. class procedure TGIFApplicationExtension.RegisterExtension(eIdent: TGIFApplicationRec;
  8758. eClass: TGIFAppExtensionClass);
  8759. begin
  8760. GetAppExtensionList.Add(eIdent, eClass);
  8761. end;
  8762. class function TGIFApplicationExtension.FindSubExtension(Stream: TStream): TGIFExtensionClass;
  8763. var
  8764. eIdent : TGIFApplicationRec;
  8765. OldPos : longInt;
  8766. Size : BYTE;
  8767. begin
  8768. OldPos := Stream.Position;
  8769. Result := nil;
  8770. if (Stream.Read(Size, 1) <> 1) then
  8771. exit;
  8772. // Some old Adobe export filters mistakenly uses a value of 10
  8773. if (Size = 10) then
  8774. begin
  8775. { TODO -oanme -cImprovement : replace with seek or read and check contents = 'Adobe' }
  8776. if (Stream.Read(eIdent, 10) <> 10) then
  8777. exit;
  8778. Result := TGIFUnknownAppExtension;
  8779. exit;
  8780. end else
  8781. if (Size <> sizeof(TGIFApplicationRec)) or
  8782. (Stream.Read(eIdent, sizeof(eIdent)) <> sizeof(eIdent)) then
  8783. begin
  8784. Stream.Position := OldPos;
  8785. Result := inherited FindSubExtension(Stream);
  8786. end else
  8787. begin
  8788. Result := GetAppExtensionList.FindExt(eIdent);
  8789. if (Result = nil) then
  8790. Result := TGIFUnknownAppExtension;
  8791. end;
  8792. end;
  8793. ////////////////////////////////////////////////////////////////////////////////
  8794. //
  8795. // TGIFApplicationExtension
  8796. //
  8797. ////////////////////////////////////////////////////////////////////////////////
  8798. constructor TGIFApplicationExtension.Create(ASubImage: TGIFSubImage);
  8799. begin
  8800. inherited Create(ASubImage);
  8801. FillChar(FIdent, sizeof(FIdent), 0);
  8802. end;
  8803. destructor TGIFApplicationExtension.Destroy;
  8804. begin
  8805. inherited Destroy;
  8806. end;
  8807. function TGIFApplicationExtension.GetExtensionType: TGIFExtensionType;
  8808. begin
  8809. Result := bsApplicationExtension;
  8810. end;
  8811. function TGIFApplicationExtension.GetAuthentication: string;
  8812. begin
  8813. Result := FIdent.Authentication;
  8814. end;
  8815. procedure TGIFApplicationExtension.SetAuthentication(const Value: string);
  8816. begin
  8817. if (Length(Value) < sizeof(TGIFAuthenticationCode)) then
  8818. FillChar(FIdent.Authentication, sizeof(TGIFAuthenticationCode), 32);
  8819. StrLCopy(@(FIdent.Authentication[0]), PChar(Value), sizeof(TGIFAuthenticationCode));
  8820. end;
  8821. function TGIFApplicationExtension.GetIdentifier: string;
  8822. begin
  8823. Result := FIdent.Identifier;
  8824. end;
  8825. procedure TGIFApplicationExtension.SetIdentifier(const Value: string);
  8826. begin
  8827. if (Length(Value) < sizeof(TGIFIdentifierCode)) then
  8828. FillChar(FIdent.Identifier, sizeof(TGIFIdentifierCode), 32);
  8829. StrLCopy(@(FIdent.Identifier[0]), PChar(Value), sizeof(TGIFIdentifierCode));
  8830. end;
  8831. procedure TGIFApplicationExtension.SaveToStream(Stream: TStream);
  8832. begin
  8833. inherited SaveToStream(Stream);
  8834. WriteByte(Stream, sizeof(FIdent)); // Block size
  8835. Stream.Write(FIdent, sizeof(FIdent));
  8836. SaveData(Stream);
  8837. end;
  8838. procedure TGIFApplicationExtension.LoadFromStream(Stream: TStream);
  8839. var
  8840. i : integer;
  8841. begin
  8842. inherited LoadFromStream(Stream);
  8843. i := ReadByte(Stream);
  8844. // Some old Adobe export filters mistakenly uses a value of 10
  8845. if (i = 10) then
  8846. FillChar(FIdent, sizeOf(FIdent), 0)
  8847. else
  8848. if (i < 11) then
  8849. Error(sBadBlockSize);
  8850. ReadCheck(Stream, FIdent, sizeof(FIdent));
  8851. Dec(i, sizeof(FIdent));
  8852. // Ignore extra data
  8853. Stream.Seek(i, soFromCurrent);
  8854. // ***FIXME***
  8855. // If self class is TGIFApplicationExtension, this will cause an "abstract
  8856. // error".
  8857. // TGIFApplicationExtension.LoadData should read and ignore rest of block.
  8858. LoadData(Stream);
  8859. end;
  8860. ////////////////////////////////////////////////////////////////////////////////
  8861. //
  8862. // TGIFUnknownAppExtension
  8863. //
  8864. ////////////////////////////////////////////////////////////////////////////////
  8865. constructor TGIFBlock.Create(ASize: integer);
  8866. begin
  8867. inherited Create;
  8868. FSize := ASize;
  8869. GetMem(FData, FSize);
  8870. FillChar(FData^, FSize, 0);
  8871. end;
  8872. destructor TGIFBlock.Destroy;
  8873. begin
  8874. FreeMem(FData);
  8875. inherited Destroy;
  8876. end;
  8877. procedure TGIFBlock.SaveToStream(Stream: TStream);
  8878. begin
  8879. Stream.Write(FSize, 1);
  8880. Stream.Write(FData^, FSize);
  8881. end;
  8882. procedure TGIFBlock.LoadFromStream(Stream: TStream);
  8883. begin
  8884. ReadCheck(Stream, FData^, FSize);
  8885. end;
  8886. constructor TGIFUnknownAppExtension.Create(ASubImage: TGIFSubImage);
  8887. begin
  8888. inherited Create(ASubImage);
  8889. FBlocks := TList.Create;
  8890. end;
  8891. destructor TGIFUnknownAppExtension.Destroy;
  8892. var
  8893. i : integer;
  8894. begin
  8895. for i := 0 to FBlocks.Count-1 do
  8896. TGIFBlock(FBlocks[i]).Free;
  8897. FBlocks.Free;
  8898. inherited Destroy;
  8899. end;
  8900. procedure TGIFUnknownAppExtension.SaveData(Stream: TStream);
  8901. var
  8902. i : integer;
  8903. begin
  8904. for i := 0 to FBlocks.Count-1 do
  8905. TGIFBlock(FBlocks[i]).SaveToStream(Stream);
  8906. // Terminating zero
  8907. WriteByte(Stream, 0);
  8908. end;
  8909. procedure TGIFUnknownAppExtension.LoadData(Stream: TStream);
  8910. var
  8911. b : BYTE;
  8912. Block : TGIFBlock;
  8913. i : integer;
  8914. begin
  8915. // Zap old blocks
  8916. for i := 0 to FBlocks.Count-1 do
  8917. TGIFBlock(FBlocks[i]).Free;
  8918. FBlocks.Clear;
  8919. // Read blocks
  8920. if (Stream.Read(b, 1) <> 1) then
  8921. exit;
  8922. while (b <> 0) do
  8923. begin
  8924. Block := TGIFBlock.Create(b);
  8925. try
  8926. Block.LoadFromStream(Stream);
  8927. except
  8928. Block.Free;
  8929. raise;
  8930. end;
  8931. FBlocks.Add(Block);
  8932. if (Stream.Read(b, 1) <> 1) then
  8933. exit;
  8934. end;
  8935. end;
  8936. ////////////////////////////////////////////////////////////////////////////////
  8937. //
  8938. // TGIFAppExtNSLoop
  8939. //
  8940. ////////////////////////////////////////////////////////////////////////////////
  8941. const
  8942. // Netscape sub block types
  8943. nbLoopExtension = 1;
  8944. nbBufferExtension = 2;
  8945. constructor TGIFAppExtNSLoop.Create(ASubImage: TGIFSubImage);
  8946. const
  8947. NSLoopIdent: TGIFApplicationRec = (Identifier: 'NETSCAPE'; Authentication: '2.0');
  8948. begin
  8949. inherited Create(ASubImage);
  8950. FIdent := NSLoopIdent;
  8951. end;
  8952. procedure TGIFAppExtNSLoop.SaveData(Stream: TStream);
  8953. begin
  8954. // Write loop count
  8955. WriteByte(Stream, 1 + sizeof(FLoops)); // Size of block
  8956. WriteByte(Stream, nbLoopExtension); // Identify sub block as looping extension data
  8957. Stream.Write(FLoops, sizeof(FLoops)); // Loop count
  8958. // Write buffer size if specified
  8959. if (FBufferSize > 0) then
  8960. begin
  8961. WriteByte(Stream, 1 + sizeof(FBufferSize)); // Size of block
  8962. WriteByte(Stream, nbBufferExtension); // Identify sub block as buffer size data
  8963. Stream.Write(FBufferSize, sizeof(FBufferSize)); // Buffer size
  8964. end;
  8965. WriteByte(Stream, 0); // Terminating zero
  8966. end;
  8967. procedure TGIFAppExtNSLoop.LoadData(Stream: TStream);
  8968. var
  8969. BlockSize : integer;
  8970. BlockType : integer;
  8971. begin
  8972. // Read size of first block or terminating zero
  8973. BlockSize := ReadByte(Stream);
  8974. while (BlockSize <> 0) do
  8975. begin
  8976. BlockType := ReadByte(Stream);
  8977. dec(BlockSize);
  8978. case (BlockType AND $07) of
  8979. nbLoopExtension:
  8980. begin
  8981. if (BlockSize < sizeof(FLoops)) then
  8982. Error(sInvalidData);
  8983. // Read loop count
  8984. ReadCheck(Stream, FLoops, sizeof(FLoops));
  8985. dec(BlockSize, sizeof(FLoops));
  8986. end;
  8987. nbBufferExtension:
  8988. begin
  8989. if (BlockSize < sizeof(FBufferSize)) then
  8990. Error(sInvalidData);
  8991. // Read buffer size
  8992. ReadCheck(Stream, FBufferSize, sizeof(FBufferSize));
  8993. dec(BlockSize, sizeof(FBufferSize));
  8994. end;
  8995. end;
  8996. // Skip/ignore unread data
  8997. if (BlockSize > 0) then
  8998. Stream.Seek(BlockSize, soFromCurrent);
  8999. // Read size of next block or terminating zero
  9000. BlockSize := ReadByte(Stream);
  9001. end;
  9002. end;
  9003. ////////////////////////////////////////////////////////////////////////////////
  9004. //
  9005. // TGIFImageList
  9006. //
  9007. ////////////////////////////////////////////////////////////////////////////////
  9008. function TGIFImageList.GetImage(Index: Integer): TGIFSubImage;
  9009. begin
  9010. Result := TGIFSubImage(Items[Index]);
  9011. end;
  9012. procedure TGIFImageList.SetImage(Index: Integer; SubImage: TGIFSubImage);
  9013. begin
  9014. Items[Index] := SubImage;
  9015. end;
  9016. procedure TGIFImageList.LoadFromStream(Stream: TStream; Parent: TObject);
  9017. var
  9018. b : BYTE;
  9019. SubImage : TGIFSubImage;
  9020. begin
  9021. // Peek ahead to determine block type
  9022. repeat
  9023. if (Stream.Read(b, 1) <> 1) then
  9024. exit;
  9025. until (b <> 0); // Ignore 0 padding (non-compliant)
  9026. while (b <> bsTrailer) do
  9027. begin
  9028. Stream.Seek(-1, soFromCurrent);
  9029. if (b in [bsExtensionIntroducer, bsImageDescriptor]) then
  9030. begin
  9031. SubImage := TGIFSubImage.Create(Parent as TGIFImage);
  9032. try
  9033. SubImage.LoadFromStream(Stream);
  9034. Add(SubImage);
  9035. Image.Progress(Self, psRunning, MulDiv(Stream.Position, 100, Stream.Size),
  9036. GIFImageRenderOnLoad, Rect(0,0,0,0), sProgressLoading);
  9037. except
  9038. SubImage.Free;
  9039. raise;
  9040. end;
  9041. end else
  9042. begin
  9043. Warning(gsWarning, sBadBlock);
  9044. break;
  9045. end;
  9046. repeat
  9047. if (Stream.Read(b, 1) <> 1) then
  9048. exit;
  9049. until (b <> 0); // Ignore 0 padding (non-compliant)
  9050. end;
  9051. Stream.Seek(-1, soFromCurrent);
  9052. end;
  9053. procedure TGIFImageList.SaveToStream(Stream: TStream);
  9054. var
  9055. i : integer;
  9056. begin
  9057. for i := 0 to Count-1 do
  9058. begin
  9059. TGIFItem(Items[i]).SaveToStream(Stream);
  9060. Image.Progress(Self, psRunning, MulDiv((i+1), 100, Count), False, Rect(0,0,0,0), sProgressSaving);
  9061. end;
  9062. end;
  9063. ////////////////////////////////////////////////////////////////////////////////
  9064. //
  9065. // TGIFPainter
  9066. //
  9067. ////////////////////////////////////////////////////////////////////////////////
  9068. constructor TGIFPainter.CreateRef(Painter: PGIFPainter; AImage: TGIFImage;
  9069. ACanvas: TCanvas; ARect: TRect; Options: TGIFDrawOptions);
  9070. begin
  9071. Create(AImage, ACanvas, ARect, Options);
  9072. PainterRef := Painter;
  9073. if (PainterRef <> nil) then
  9074. PainterRef^ := self;
  9075. end;
  9076. constructor TGIFPainter.Create(AImage: TGIFImage; ACanvas: TCanvas; ARect: TRect;
  9077. Options: TGIFDrawOptions);
  9078. var
  9079. i : integer;
  9080. BackgroundColor : TColor;
  9081. Disposals : set of TDisposalMethod;
  9082. begin
  9083. inherited Create(True);
  9084. FreeOnTerminate := True;
  9085. Onterminate := DoOnTerminate;
  9086. FImage := AImage;
  9087. FCanvas := ACanvas;
  9088. FRect := ARect;
  9089. FActiveImage := -1;
  9090. FDrawOptions := Options;
  9091. FStarted := False;
  9092. BackupBuffer := nil;
  9093. FrameBuffer := nil;
  9094. Background := nil;
  9095. FEventHandle := 0;
  9096. // This should be a parameter, but I think I've got enough of them already...
  9097. FAnimationSpeed := FImage.AnimationSpeed;
  9098. // An event handle is used for animation delays
  9099. if (FDrawOptions >= [goAnimate, goAsync]) and (FImage.Images.Count > 1) and
  9100. (FAnimationSpeed >= 0) then
  9101. FEventHandle := CreateEvent(nil, False, False, nil);
  9102. // Preprocessing of extensions to determine if we need frame buffers
  9103. Disposals := [];
  9104. if (FImage.DrawBackgroundColor = clNone) then
  9105. begin
  9106. if (FImage.GlobalColorMap.Count > 0) then
  9107. BackgroundColor := FImage.BackgroundColor
  9108. else
  9109. BackgroundColor := ColorToRGB(clWindow);
  9110. end else
  9111. BackgroundColor := ColorToRGB(FImage.DrawBackgroundColor);
  9112. // Need background buffer to clear on loop
  9113. if (goClearOnLoop in FDrawOptions) then
  9114. Include(Disposals, dmBackground);
  9115. for i := 0 to FImage.Images.Count-1 do
  9116. if (FImage.Images[i].GraphicControlExtension <> nil) then
  9117. with (FImage.Images[i].GraphicControlExtension) do
  9118. Include(Disposals, Disposal);
  9119. // Need background buffer to draw transparent on background
  9120. if (dmBackground in Disposals) and (goTransparent in FDrawOptions) then
  9121. begin
  9122. Background := TBitmap.Create;
  9123. Background.Height := FRect.Bottom-FRect.Top;
  9124. Background.Width := FRect.Right-FRect.Left;
  9125. // Copy background immediately
  9126. Background.Canvas.CopyMode := cmSrcCopy;
  9127. Background.Canvas.CopyRect(Background.Canvas.ClipRect, FCanvas, FRect);
  9128. end;
  9129. // Need frame- and backup buffer to restore to previous and background
  9130. if ((Disposals * [dmPrevious, dmBackground]) <> []) then
  9131. begin
  9132. BackupBuffer := TBitmap.Create;
  9133. BackupBuffer.Height := FRect.Bottom-FRect.Top;
  9134. BackupBuffer.Width := FRect.Right-FRect.Left;
  9135. BackupBuffer.Canvas.CopyMode := cmSrcCopy;
  9136. BackupBuffer.Canvas.Brush.Color := BackgroundColor;
  9137. BackupBuffer.Canvas.Brush.Style := bsSolid;
  9138. {$IFDEF DEBUG}
  9139. BackupBuffer.Canvas.Brush.Color := clBlack;
  9140. BackupBuffer.Canvas.Brush.Style := bsDiagCross;
  9141. {$ENDIF}
  9142. // Step 1: Copy destination to backup buffer
  9143. // Always executed before first frame and only once.
  9144. BackupBuffer.Canvas.CopyRect(BackupBuffer.Canvas.ClipRect, FCanvas, FRect);
  9145. FrameBuffer := TBitmap.Create;
  9146. FrameBuffer.Height := FRect.Bottom-FRect.Top;
  9147. FrameBuffer.Width := FRect.Right-FRect.Left;
  9148. FrameBuffer.Canvas.CopyMode := cmSrcCopy;
  9149. FrameBuffer.Canvas.Brush.Color := BackgroundColor;
  9150. FrameBuffer.Canvas.Brush.Style := bsSolid;
  9151. {$IFDEF DEBUG}
  9152. FrameBuffer.Canvas.Brush.Color := clBlack;
  9153. FrameBuffer.Canvas.Brush.Style := bsDiagCross;
  9154. {$ENDIF}
  9155. end;
  9156. end;
  9157. destructor TGIFPainter.Destroy;
  9158. begin
  9159. // OnTerminate isn't called if we are running in main thread, so we must call
  9160. // it manually
  9161. if not(goAsync in DrawOptions) then
  9162. DoOnTerminate(self);
  9163. // Reraise any exptions that were eaten in the Execute method
  9164. if (ExceptObject <> nil) then
  9165. raise ExceptObject at ExceptAddress;
  9166. inherited Destroy;
  9167. end;
  9168. procedure TGIFPainter.SetAnimationSpeed(Value: integer);
  9169. begin
  9170. if (Value < 0) then
  9171. Value := 0
  9172. else if (Value > 1000) then
  9173. Value := 1000;
  9174. if (Value <> FAnimationSpeed) then
  9175. begin
  9176. FAnimationSpeed := Value;
  9177. // Signal WaitForSingleObject delay to abort
  9178. if (FEventHandle <> 0) then
  9179. SetEvent(FEventHandle)
  9180. else
  9181. DoRestart := True;
  9182. end;
  9183. end;
  9184. procedure TGIFPainter.SetActiveImage(const Value: integer);
  9185. begin
  9186. if (Value >= 0) and (Value < FImage.Images.Count) then
  9187. FActiveImage := Value;
  9188. end;
  9189. // Conditional Synchronize
  9190. procedure TGIFPainter.DoSynchronize(Method: TThreadMethod);
  9191. begin
  9192. if (Terminated) then
  9193. exit;
  9194. if (goAsync in FDrawOptions) then
  9195. // Execute Synchronized if requested...
  9196. Synchronize(Method)
  9197. else
  9198. // ...Otherwise just execute in current thread (probably main thread)
  9199. Method;
  9200. end;
  9201. // Delete frame buffers - Executed in main thread
  9202. procedure TGIFPainter.DoOnTerminate(Sender: TObject);
  9203. begin
  9204. // It shouldn't really be nescessary to protect PainterRef in this manner
  9205. // since we are running in the main thread at this point, but I'm a little
  9206. // paranoid about the way PainterRef is being used...
  9207. if Image <> nil then // 2001.02.23
  9208. begin // 2001.02.23
  9209. with Image.Painters.LockList do
  9210. try
  9211. // Zap pointer to self and remove from painter list
  9212. if (PainterRef <> nil) and (PainterRef^ = self) then
  9213. PainterRef^ := nil;
  9214. finally
  9215. Image.Painters.UnLockList;
  9216. end;
  9217. Image.Painters.Remove(self);
  9218. FImage := nil;
  9219. end; // 2001.02.23
  9220. // Free buffers
  9221. if (BackupBuffer <> nil) then
  9222. BackupBuffer.Free;
  9223. if (FrameBuffer <> nil) then
  9224. FrameBuffer.Free;
  9225. if (Background <> nil) then
  9226. Background.Free;
  9227. // Delete event handle
  9228. if (FEventHandle <> 0) then
  9229. CloseHandle(FEventHandle);
  9230. end;
  9231. // Event "dispatcher" - Executed in main thread
  9232. procedure TGIFPainter.DoEvent;
  9233. begin
  9234. if (Assigned(FEvent)) then
  9235. FEvent(self);
  9236. end;
  9237. // Non-buffered paint - Executed in main thread
  9238. procedure TGIFPainter.DoPaint;
  9239. begin
  9240. FImage.Images[ActiveImage].Draw(FCanvas, FRect, (goTransparent in FDrawOptions),
  9241. (goTile in FDrawOptions));
  9242. FStarted := True;
  9243. end;
  9244. // Buffered paint - Executed in main thread
  9245. procedure TGIFPainter.DoPaintFrame;
  9246. var
  9247. DrawDestination : TCanvas;
  9248. DrawRect : TRect;
  9249. DoStep2 ,
  9250. DoStep3 ,
  9251. DoStep5 ,
  9252. DoStep6 : boolean;
  9253. SavePal ,
  9254. SourcePal : HPALETTE;
  9255. procedure ClearBackup;
  9256. var
  9257. r ,
  9258. Tile : TRect;
  9259. FrameTop ,
  9260. FrameHeight : integer;
  9261. ImageWidth ,
  9262. ImageHeight : integer;
  9263. begin
  9264. if (goTransparent in FDrawOptions) then
  9265. begin
  9266. // If the frame is transparent, we must remove it by copying the
  9267. // background buffer over it
  9268. if (goTile in FDrawOptions) then
  9269. begin
  9270. FrameTop := FImage.Images[ActiveImage].Top;
  9271. FrameHeight := FImage.Images[ActiveImage].Height;
  9272. ImageWidth := FImage.Width;
  9273. ImageHeight := FImage.Height;
  9274. Tile.Left := FRect.Left + FImage.Images[ActiveImage].Left;
  9275. Tile.Right := Tile.Left + FImage.Images[ActiveImage].Width;
  9276. while (Tile.Left < FRect.Right) do
  9277. begin
  9278. Tile.Top := FRect.Top + FrameTop;
  9279. Tile.Bottom := Tile.Top + FrameHeight;
  9280. while (Tile.Top < FRect.Bottom) do
  9281. begin
  9282. BackupBuffer.Canvas.CopyRect(Tile, Background.Canvas, Tile);
  9283. Tile.Top := Tile.Top + ImageHeight;
  9284. Tile.Bottom := Tile.Bottom + ImageHeight;
  9285. end;
  9286. Tile.Left := Tile.Left + ImageWidth;
  9287. Tile.Right := Tile.Right + ImageWidth;
  9288. end;
  9289. end else
  9290. begin
  9291. r := FImage.Images[ActiveImage].ScaleRect(BackupBuffer.Canvas.ClipRect);
  9292. BackupBuffer.Canvas.CopyRect(r, Background.Canvas, r)
  9293. end;
  9294. end else
  9295. begin
  9296. // If the frame isn't transparent, we just clear the area covered by
  9297. // it to the background color.
  9298. // Tile the background unless the frame covers all of the image
  9299. if (goTile in FDrawOptions) and
  9300. ((FImage.Width <> FImage.Images[ActiveImage].Width) and
  9301. (FImage.height <> FImage.Images[ActiveImage].Height)) then
  9302. begin
  9303. FrameTop := FImage.Images[ActiveImage].Top;
  9304. FrameHeight := FImage.Images[ActiveImage].Height;
  9305. ImageWidth := FImage.Width;
  9306. ImageHeight := FImage.Height;
  9307. // ***FIXME*** I don't think this does any difference
  9308. BackupBuffer.Canvas.Brush.Color := FImage.DrawBackgroundColor;
  9309. Tile.Left := FRect.Left + FImage.Images[ActiveImage].Left;
  9310. Tile.Right := Tile.Left + FImage.Images[ActiveImage].Width;
  9311. while (Tile.Left < FRect.Right) do
  9312. begin
  9313. Tile.Top := FRect.Top + FrameTop;
  9314. Tile.Bottom := Tile.Top + FrameHeight;
  9315. while (Tile.Top < FRect.Bottom) do
  9316. begin
  9317. BackupBuffer.Canvas.FillRect(Tile);
  9318. Tile.Top := Tile.Top + ImageHeight;
  9319. Tile.Bottom := Tile.Bottom + ImageHeight;
  9320. end;
  9321. Tile.Left := Tile.Left + ImageWidth;
  9322. Tile.Right := Tile.Right + ImageWidth;
  9323. end;
  9324. end else
  9325. BackupBuffer.Canvas.FillRect(FImage.Images[ActiveImage].ScaleRect(FRect));
  9326. end;
  9327. end;
  9328. begin
  9329. if (goValidateCanvas in FDrawOptions) then
  9330. if (GetObjectType(ValidateDC) <> OBJ_DC) then
  9331. begin
  9332. Terminate;
  9333. exit;
  9334. end;
  9335. DrawDestination := nil;
  9336. DoStep2 := (goClearOnLoop in FDrawOptions) and (FActiveImage = 0);
  9337. DoStep3 := False;
  9338. DoStep5 := False;
  9339. DoStep6 := False;
  9340. {
  9341. Disposal mode algorithm:
  9342. Step 1: Copy destination to backup buffer
  9343. Always executed before first frame and only once.
  9344. Done in constructor.
  9345. Step 2: Clear previous frame (implementation is same as step 6)
  9346. Done implicitly by implementation.
  9347. Only done explicitly on first frame if goClearOnLoop option is set.
  9348. Step 3: Copy backup buffer to frame buffer
  9349. Step 4: Draw frame
  9350. Step 5: Copy buffer to destination
  9351. Step 6: Clear frame from backup buffer
  9352. +------------+------------------+---------------------+------------------------+
  9353. |New \ Old | dmNone | dmBackground | dmPrevious |
  9354. +------------+------------------+---------------------+------------------------+
  9355. |dmNone | | | |
  9356. | |4. Paint on backup|4. Paint on backup |4. Paint on backup |
  9357. | |5. Restore |5. Restore |5. Restore |
  9358. +------------+------------------+---------------------+------------------------+
  9359. |dmBackground| | | |
  9360. | |4. Paint on backup|4. Paint on backup |4. Paint on backup |
  9361. | |5. Restore |5. Restore |5. Restore |
  9362. | |6. Clear backup |6. Clear backup |6. Clear backup |
  9363. +------------+------------------+---------------------+------------------------+
  9364. |dmPrevious | | | |
  9365. | | |3. Copy backup to buf|3. Copy backup to buf |
  9366. | |4. Paint on dest |4. Paint on buf |4. Paint on buf |
  9367. | | |5. Copy buf to dest |5. Copy buf to dest |
  9368. +------------+------------------+---------------------+------------------------+
  9369. }
  9370. case (Disposal) of
  9371. dmNone, dmNoDisposal:
  9372. begin
  9373. DrawDestination := BackupBuffer.Canvas;
  9374. DrawRect := BackupBuffer.Canvas.ClipRect;
  9375. DoStep5 := True;
  9376. end;
  9377. dmBackground:
  9378. begin
  9379. DrawDestination := BackupBuffer.Canvas;
  9380. DrawRect := BackupBuffer.Canvas.ClipRect;
  9381. DoStep5 := True;
  9382. DoStep6 := True;
  9383. end;
  9384. dmPrevious:
  9385. case (OldDisposal) of
  9386. dmNone, dmNoDisposal:
  9387. begin
  9388. DrawDestination := FCanvas;
  9389. DrawRect := FRect;
  9390. end;
  9391. dmBackground, dmPrevious:
  9392. begin
  9393. DrawDestination := FrameBuffer.Canvas;
  9394. DrawRect := FrameBuffer.Canvas.ClipRect;
  9395. DoStep3 := True;
  9396. DoStep5 := True;
  9397. end;
  9398. end;
  9399. end;
  9400. // Find source palette
  9401. SourcePal := FImage.Images[ActiveImage].Palette;
  9402. if (SourcePal = 0) then
  9403. SourcePal := SystemPalette16; // This should never happen
  9404. SavePal := SelectPalette(DrawDestination.Handle, SourcePal, False);
  9405. RealizePalette(DrawDestination.Handle);
  9406. // Step 2: Clear previous frame
  9407. if (DoStep2) then
  9408. ClearBackup;
  9409. // Step 3: Copy backup buffer to frame buffer
  9410. if (DoStep3) then
  9411. FrameBuffer.Canvas.CopyRect(FrameBuffer.Canvas.ClipRect,
  9412. BackupBuffer.Canvas, BackupBuffer.Canvas.ClipRect);
  9413. // Step 4: Draw frame
  9414. if (DrawDestination <> nil) then
  9415. FImage.Images[ActiveImage].Draw(DrawDestination, DrawRect,
  9416. (goTransparent in FDrawOptions), (goTile in FDrawOptions));
  9417. // Step 5: Copy buffer to destination
  9418. if (DoStep5) then
  9419. begin
  9420. FCanvas.CopyMode := cmSrcCopy;
  9421. FCanvas.CopyRect(FRect, DrawDestination, DrawRect);
  9422. end;
  9423. if (SavePal <> 0) then
  9424. SelectPalette(DrawDestination.Handle, SavePal, False);
  9425. // Step 6: Clear frame from backup buffer
  9426. if (DoStep6) then
  9427. ClearBackup;
  9428. FStarted := True;
  9429. end;
  9430. // Prefetch bitmap
  9431. // Used to force the GIF image to be rendered as a bitmap
  9432. {$ifdef SERIALIZE_RENDER}
  9433. procedure TGIFPainter.PrefetchBitmap;
  9434. begin
  9435. // Touch current bitmap to force bitmap to be rendered
  9436. if not((FImage.Images[ActiveImage].Empty) or (FImage.Images[ActiveImage].HasBitmap)) then
  9437. FImage.Images[ActiveImage].Bitmap;
  9438. end;
  9439. {$endif}
  9440. // Main thread execution loop - This is where it all happens...
  9441. procedure TGIFPainter.Execute;
  9442. var
  9443. i : integer;
  9444. LoopCount ,
  9445. LoopPoint : integer;
  9446. Looping : boolean;
  9447. Ext : TGIFExtension;
  9448. Msg : TMsg;
  9449. Delay ,
  9450. OldDelay ,
  9451. DelayUsed : longInt;
  9452. DelayStart ,
  9453. NewDelayStart : DWORD;
  9454. procedure FireEvent(Event: TNotifyEvent);
  9455. begin
  9456. if not(Assigned(Event)) then
  9457. exit;
  9458. FEvent := Event;
  9459. try
  9460. DoSynchronize(DoEvent);
  9461. finally
  9462. FEvent := nil;
  9463. end;
  9464. end;
  9465. begin
  9466. {
  9467. Disposal:
  9468. dmNone: Same as dmNodisposal
  9469. dmNoDisposal: Do not dispose
  9470. dmBackground: Clear with background color *)
  9471. dmPrevious: Previous image
  9472. *) Note: Background color should either be a BROWSER SPECIFIED Background
  9473. color (DrawBackgroundColor) or the background image if any frames are
  9474. transparent.
  9475. }
  9476. try
  9477. try
  9478. if (goValidateCanvas in FDrawOptions) then
  9479. ValidateDC := FCanvas.Handle;
  9480. DoRestart := True;
  9481. // Loop to restart paint
  9482. while (DoRestart) and not(Terminated) do
  9483. begin
  9484. FActiveImage := 0;
  9485. // Fire OnStartPaint event
  9486. // Note: ActiveImage may be altered by the event handler
  9487. FireEvent(FOnStartPaint);
  9488. FStarted := False;
  9489. DoRestart := False;
  9490. LoopCount := 1;
  9491. LoopPoint := FActiveImage;
  9492. Looping := False;
  9493. if (goAsync in DrawOptions) then
  9494. Delay := 0
  9495. else
  9496. Delay := 1; // Dummy to process messages
  9497. OldDisposal := dmNoDisposal;
  9498. // Fetch delay start time
  9499. DelayStart := timeGetTime;
  9500. OldDelay := 0;
  9501. // Loop to loop - duh!
  9502. while ((LoopCount <> 0) or (goLoopContinously in DrawOptions)) and
  9503. not(Terminated or DoRestart) do
  9504. begin
  9505. FActiveImage := LoopPoint;
  9506. // Fire OnLoopPaint event
  9507. // Note: ActiveImage may be altered by the event handler
  9508. if (FStarted) then
  9509. FireEvent(FOnLoop);
  9510. // Loop to animate
  9511. while (ActiveImage < FImage.Images.Count) and not(Terminated or DoRestart) do
  9512. begin
  9513. // Ignore empty images
  9514. if (FImage.Images[ActiveImage].Empty) then
  9515. break;
  9516. // Delay from previous image
  9517. if (Delay > 0) then
  9518. begin
  9519. // Prefetch frame bitmap
  9520. {$ifdef SERIALIZE_RENDER}
  9521. DoSynchronize(PrefetchBitmap);
  9522. {$else}
  9523. FImage.Images[ActiveImage].Bitmap;
  9524. {$endif}
  9525. // Calculate inter frame delay
  9526. NewDelayStart := timeGetTime;
  9527. if (FAnimationSpeed > 0) then
  9528. begin
  9529. // Calculate number of mS used in prefetch and display
  9530. try
  9531. DelayUsed := integer(NewDelayStart-DelayStart)-OldDelay;
  9532. // Prevent feedback oscillations caused by over/undercompensation.
  9533. DelayUsed := DelayUsed DIV 2;
  9534. // Convert delay value to mS and...
  9535. // ...Adjust for time already spent converting GIF to bitmap and...
  9536. // ...Adjust for Animation Speed factor.
  9537. Delay := MulDiv(Delay * GIFDelayExp - DelayUsed, 100, FAnimationSpeed);
  9538. OldDelay := Delay;
  9539. except
  9540. Delay := GIFMaximumDelay * GIFDelayExp;
  9541. OldDelay := 0;
  9542. end;
  9543. end else
  9544. begin
  9545. if (goAsync in DrawOptions) then
  9546. Delay := longInt(INFINITE)
  9547. else
  9548. Delay := GIFMaximumDelay * GIFDelayExp;
  9549. end;
  9550. // Fetch delay start time
  9551. DelayStart := NewDelayStart;
  9552. // Sleep in one chunk if we are running in a thread
  9553. if (goAsync in DrawOptions) then
  9554. begin
  9555. // Use of WaitForSingleObject allows TGIFPainter.Stop to wake us up
  9556. if (Delay > 0) or (FAnimationSpeed = 0) then
  9557. begin
  9558. if (WaitForSingleObject(FEventHandle, DWORD(Delay)) <> WAIT_TIMEOUT) then
  9559. begin
  9560. // Don't use interframe delay feedback adjustment if delay
  9561. // were prematurely aborted (e.g. because the animation
  9562. // speed were changed)
  9563. OldDelay := 0;
  9564. DelayStart := longInt(timeGetTime);
  9565. end;
  9566. end;
  9567. end else
  9568. begin
  9569. if (Delay <= 0) then
  9570. Delay := 1;
  9571. // Fetch start time
  9572. NewDelayStart := timeGetTime;
  9573. // If we are not running in a thread we Sleep in small chunks
  9574. // and give the user a chance to abort
  9575. while (Delay > 0) and not(Terminated or DoRestart) do
  9576. begin
  9577. if (Delay < 100) then
  9578. Sleep(Delay)
  9579. else
  9580. Sleep(100);
  9581. // Calculate number of mS delayed in this chunk
  9582. DelayUsed := integer(timeGetTime - NewDelayStart);
  9583. dec(Delay, DelayUsed);
  9584. // Reset start time for chunk
  9585. NewDelaySTart := timeGetTime;
  9586. // Application.ProcessMessages wannabe
  9587. while (not(Terminated or DoRestart)) and
  9588. (PeekMessage(Msg, 0, 0, 0, PM_REMOVE)) do
  9589. begin
  9590. if (Msg.Message <> WM_QUIT) then
  9591. begin
  9592. TranslateMessage(Msg);
  9593. DispatchMessage(Msg);
  9594. end else
  9595. begin
  9596. // Put WM_QUIT back in queue and get out of here fast
  9597. PostQuitMessage(Msg.WParam);
  9598. Terminate;
  9599. end;
  9600. end;
  9601. end;
  9602. end;
  9603. end else
  9604. Sleep(0); // Yield
  9605. if (Terminated) then
  9606. break;
  9607. // Fire OnPaint event
  9608. // Note: ActiveImage may be altered by the event handler
  9609. FireEvent(FOnPaint);
  9610. if (Terminated) then
  9611. break;
  9612. // Pre-draw processing of extensions
  9613. Disposal := dmNoDisposal;
  9614. for i := 0 to FImage.Images[ActiveImage].Extensions.Count-1 do
  9615. begin
  9616. Ext := FImage.Images[ActiveImage].Extensions[i];
  9617. if (Ext is TGIFAppExtNSLoop) then
  9618. begin
  9619. // Recursive loops not supported (or defined)
  9620. if (Looping) then
  9621. continue;
  9622. Looping := True;
  9623. LoopCount := TGIFAppExtNSLoop(Ext).Loops;
  9624. if ((LoopCount = 0) or (goLoopContinously in DrawOptions)) and
  9625. (goAsync in DrawOptions) then
  9626. LoopCount := -1; // Infinite if running in separate thread
  9627. {$IFNDEF STRICT_MOZILLA}
  9628. // Loop from this image and on
  9629. // Note: This is not standard behavior
  9630. LoopPoint := ActiveImage;
  9631. {$ENDIF}
  9632. end else
  9633. if (Ext is TGIFGraphicControlExtension) then
  9634. Disposal := TGIFGraphicControlExtension(Ext).Disposal;
  9635. end;
  9636. // Paint the image
  9637. if (BackupBuffer <> nil) then
  9638. DoSynchronize(DoPaintFrame)
  9639. else
  9640. DoSynchronize(DoPaint);
  9641. OldDisposal := Disposal;
  9642. if (Terminated) then
  9643. break;
  9644. Delay := GIFDefaultDelay; // Default delay
  9645. // Post-draw processing of extensions
  9646. if (FImage.Images[ActiveImage].GraphicControlExtension <> nil) then
  9647. if (FImage.Images[ActiveImage].GraphicControlExtension.Delay > 0) then
  9648. begin
  9649. Delay := FImage.Images[ActiveImage].GraphicControlExtension.Delay;
  9650. // Enforce minimum animation delay in compliance with Mozilla
  9651. if (Delay < GIFMinimumDelay) then
  9652. Delay := GIFMinimumDelay;
  9653. // Do not delay more than 10 seconds if running in main thread
  9654. if (Delay > GIFMaximumDelay) and not(goAsync in DrawOptions) then
  9655. Delay := GIFMaximumDelay; // Max 10 seconds
  9656. end;
  9657. // Fire OnAfterPaint event
  9658. // Note: ActiveImage may be altered by the event handler
  9659. i := FActiveImage;
  9660. FireEvent(FOnAfterPaint);
  9661. if (Terminated) then
  9662. break;
  9663. // Don't increment frame counter if event handler modified
  9664. // current frame
  9665. if (FActiveImage = i) then
  9666. Inc(FActiveImage);
  9667. // Nothing more to do unless we are animating
  9668. if not(goAnimate in DrawOptions) then
  9669. break;
  9670. end;
  9671. if (LoopCount > 0) then
  9672. Dec(LoopCount);
  9673. if ([goAnimate, goLoop] * DrawOptions <> [goAnimate, goLoop]) then
  9674. break;
  9675. end;
  9676. if (Terminated) then // 2001.07.23
  9677. break; // 2001.07.23
  9678. end;
  9679. FActiveImage := -1;
  9680. // Fire OnEndPaint event
  9681. FireEvent(FOnEndPaint);
  9682. finally
  9683. // If we are running in the main thread we will have to zap our self
  9684. if not(goAsync in DrawOptions) then
  9685. Free;
  9686. end;
  9687. except
  9688. on E: Exception do
  9689. begin
  9690. // Eat exception and terminate thread...
  9691. // If we allow the exception to abort the thread at this point, the
  9692. // application will hang since the thread destructor will never be called
  9693. // and the application will wait forever for the thread to die!
  9694. Terminate;
  9695. // Clone exception
  9696. ExceptObject := E.Create(E.Message);
  9697. ExceptAddress := ExceptAddr;
  9698. end;
  9699. end;
  9700. end;
  9701. procedure TGIFPainter.Start;
  9702. begin
  9703. if (goAsync in FDrawOptions) then
  9704. Resume;
  9705. end;
  9706. procedure TGIFPainter.Stop;
  9707. begin
  9708. Terminate;
  9709. if (goAsync in FDrawOptions) then
  9710. begin
  9711. // Signal WaitForSingleObject delay to abort
  9712. if (FEventHandle <> 0) then
  9713. SetEvent(FEventHandle);
  9714. Priority := tpNormal;
  9715. if (Suspended) then
  9716. Resume; // Must be running before we can terminate
  9717. end;
  9718. end;
  9719. procedure TGIFPainter.Restart;
  9720. begin
  9721. DoRestart := True;
  9722. if (Suspended) and (goAsync in FDrawOptions) then
  9723. Resume; // Must be running before we can terminate
  9724. end;
  9725. ////////////////////////////////////////////////////////////////////////////////
  9726. //
  9727. // TColorMapOptimizer
  9728. //
  9729. ////////////////////////////////////////////////////////////////////////////////
  9730. // Used by TGIFImage to optimize local color maps to a single global color map.
  9731. // The following algorithm is used:
  9732. // 1) Build a histogram for each image
  9733. // 2) Merge histograms
  9734. // 3) Sum equal colors and adjust max # of colors
  9735. // 4) Map entries > max to entries <= 256
  9736. // 5) Build new color map
  9737. // 6) Map images to new color map
  9738. ////////////////////////////////////////////////////////////////////////////////
  9739. type
  9740. POptimizeEntry = ^TOptimizeEntry;
  9741. TColorRec = record
  9742. case byte of
  9743. 0: (Value: integer);
  9744. 1: (Color: TGIFColor);
  9745. 2: (SameAs: POptimizeEntry); // Used if TOptimizeEntry.Count = 0
  9746. end;
  9747. TOptimizeEntry = record
  9748. Count : integer; // Usage count
  9749. OldIndex : integer; // Color OldIndex
  9750. NewIndex : integer; // NewIndex color OldIndex
  9751. Color : TColorRec; // Color value
  9752. end;
  9753. TOptimizeEntries = array[0..255] of TOptimizeEntry;
  9754. POptimizeEntries = ^TOptimizeEntries;
  9755. THistogram = class(TObject)
  9756. private
  9757. PHistogram : POptimizeEntries;
  9758. FCount : integer;
  9759. FColorMap : TGIFColorMap;
  9760. FList : TList;
  9761. FImages : TList;
  9762. public
  9763. constructor Create(AColorMap: TGIFColorMap);
  9764. destructor Destroy; override;
  9765. function ProcessSubImage(Image: TGIFSubImage): boolean;
  9766. function Prune: integer;
  9767. procedure MapImages(UseTransparency: boolean; NewTransparentColorIndex: byte);
  9768. property Count: integer read FCount;
  9769. property ColorMap: TGIFColorMap read FColorMap;
  9770. property List: TList read FList;
  9771. end;
  9772. TColorMapOptimizer = class(TObject)
  9773. private
  9774. FImage : TGIFImage;
  9775. FHistogramList : TList;
  9776. FHistogram : TList;
  9777. FColorMap : TColorMap;
  9778. FFinalCount : integer;
  9779. FUseTransparency : boolean;
  9780. FNewTransparentColorIndex: byte;
  9781. protected
  9782. procedure ProcessImage;
  9783. procedure MergeColors;
  9784. procedure MapColors;
  9785. procedure ReplaceColorMaps;
  9786. public
  9787. constructor Create(AImage: TGIFImage);
  9788. destructor Destroy; override;
  9789. procedure Optimize;
  9790. end;
  9791. function CompareColor(Item1, Item2: Pointer): integer;
  9792. begin
  9793. Result := POptimizeEntry(Item2)^.Color.Value - POptimizeEntry(Item1)^.Color.Value;
  9794. end;
  9795. function CompareCount(Item1, Item2: Pointer): integer;
  9796. begin
  9797. Result := POptimizeEntry(Item2)^.Count - POptimizeEntry(Item1)^.Count;
  9798. end;
  9799. constructor THistogram.Create(AColorMap: TGIFColorMap);
  9800. var
  9801. i : integer;
  9802. begin
  9803. inherited Create;
  9804. FCount := AColorMap.Count;
  9805. FColorMap := AColorMap;
  9806. FImages := TList.Create;
  9807. // Allocate memory for histogram
  9808. GetMem(PHistogram, FCount * sizeof(TOptimizeEntry));
  9809. FList := TList.Create;
  9810. FList.Capacity := FCount;
  9811. // Move data to histogram and initialize
  9812. for i := 0 to FCount-1 do
  9813. with PHistogram^[i] do
  9814. begin
  9815. FList.Add(@PHistogram^[i]);
  9816. OldIndex := i;
  9817. Count := 0;
  9818. Color.Value := 0;
  9819. Color.Color := AColorMap.Data^[i];
  9820. NewIndex := 256; // Used to signal unmapped
  9821. end;
  9822. end;
  9823. destructor THistogram.Destroy;
  9824. begin
  9825. FImages.Free;
  9826. FList.Free;
  9827. FreeMem(PHistogram);
  9828. inherited Destroy;
  9829. end;
  9830. //: Build a color histogram
  9831. function THistogram.ProcessSubImage(Image: TGIFSubImage): boolean;
  9832. var
  9833. Size : integer;
  9834. Pixel : PChar;
  9835. IsTransparent ,
  9836. WasTransparent : boolean;
  9837. OldTransparentColorIndex: byte;
  9838. begin
  9839. Result := False;
  9840. if (Image.Empty) then
  9841. exit;
  9842. FImages.Add(Image);
  9843. Pixel := Image.data;
  9844. Size := Image.Width * Image.Height;
  9845. IsTransparent := Image.Transparent;
  9846. if (IsTransparent) then
  9847. OldTransparentColorIndex := Image.GraphicControlExtension.TransparentColorIndex
  9848. else
  9849. OldTransparentColorIndex := 0; // To avoid compiler warning
  9850. WasTransparent := False;
  9851. (*
  9852. ** Sum up usage count for each color
  9853. *)
  9854. while (Size > 0) do
  9855. begin
  9856. // Ignore transparent pixels
  9857. if (not IsTransparent) or (ord(Pixel^) <> OldTransparentColorIndex) then
  9858. begin
  9859. // Check for invalid color index
  9860. if (ord(Pixel^) >= FCount) then
  9861. begin
  9862. Pixel^ := #0; // ***FIXME*** Isn't this an error condition?
  9863. Image.Warning(gsWarning, sInvalidColor);
  9864. end;
  9865. with PHistogram^[ord(Pixel^)] do
  9866. begin
  9867. // Stop if any color reaches the max count
  9868. if (Count = high(integer)) then
  9869. break;
  9870. inc(Count);
  9871. end;
  9872. end else
  9873. WasTransparent := WasTransparent or IsTransparent;
  9874. inc(Pixel);
  9875. dec(Size);
  9876. end;
  9877. (*
  9878. ** Clear frames transparency flag if the frame claimed to
  9879. ** be transparent, but wasn't
  9880. *)
  9881. if (IsTransparent and not WasTransparent) then
  9882. begin
  9883. Image.GraphicControlExtension.TransparentColorIndex := 0;
  9884. Image.GraphicControlExtension.Transparent := False;
  9885. end;
  9886. Result := WasTransparent;
  9887. end;
  9888. //: Removed unused color entries from the histogram
  9889. function THistogram.Prune: integer;
  9890. var
  9891. i, j : integer;
  9892. begin
  9893. (*
  9894. ** Sort by usage count
  9895. *)
  9896. FList.Sort(CompareCount);
  9897. (*
  9898. ** Determine number of used colors
  9899. *)
  9900. for i := 0 to FCount-1 do
  9901. // Find first unused color entry
  9902. if (POptimizeEntry(FList[i])^.Count = 0) then
  9903. begin
  9904. // Zap unused colors
  9905. for j := i to FCount-1 do
  9906. POptimizeEntry(FList[j])^.Count := -1; // Use -1 to signal unused entry
  9907. // Remove unused entries
  9908. FCount := i;
  9909. FList.Count := FCount;
  9910. break;
  9911. end;
  9912. Result := FCount;
  9913. end;
  9914. //: Convert images from old color map to new color map
  9915. procedure THistogram.MapImages(UseTransparency: boolean; NewTransparentColorIndex: byte);
  9916. var
  9917. i : integer;
  9918. Size : integer;
  9919. Pixel : PChar;
  9920. ReverseMap : array[byte] of byte;
  9921. IsTransparent : boolean;
  9922. OldTransparentColorIndex: byte;
  9923. begin
  9924. (*
  9925. ** Build NewIndex map
  9926. *)
  9927. for i := 0 to List.Count-1 do
  9928. ReverseMap[POptimizeEntry(List[i])^.OldIndex] := POptimizeEntry(List[i])^.NewIndex;
  9929. (*
  9930. ** Reorder all images using this color map
  9931. *)
  9932. for i := 0 to FImages.Count-1 do
  9933. with TGIFSubImage(FImages[i]) do
  9934. begin
  9935. Pixel := Data;
  9936. Size := Width * Height;
  9937. // Determine frame transparency
  9938. IsTransparent := (Transparent) and (UseTransparency);
  9939. if (IsTransparent) then
  9940. begin
  9941. OldTransparentColorIndex := GraphicControlExtension.TransparentColorIndex;
  9942. // Map transparent color
  9943. GraphicControlExtension.TransparentColorIndex := NewTransparentColorIndex;
  9944. end else
  9945. OldTransparentColorIndex := 0; // To avoid compiler warning
  9946. // Map all pixels to new color map
  9947. while (Size > 0) do
  9948. begin
  9949. // Map transparent pixels to the new transparent color index and...
  9950. if (IsTransparent) and (ord(Pixel^) = OldTransparentColorIndex) then
  9951. Pixel^ := char(NewTransparentColorIndex)
  9952. else
  9953. // ... all other pixels to their new color index
  9954. Pixel^ := char(ReverseMap[ord(Pixel^)]);
  9955. dec(size);
  9956. inc(Pixel);
  9957. end;
  9958. end;
  9959. end;
  9960. constructor TColorMapOptimizer.Create(AImage: TGIFImage);
  9961. begin
  9962. inherited Create;
  9963. FImage := AImage;
  9964. FHistogramList := TList.Create;
  9965. FHistogram := TList.Create;
  9966. end;
  9967. destructor TColorMapOptimizer.Destroy;
  9968. var
  9969. i : integer;
  9970. begin
  9971. FHistogram.Free;
  9972. for i := FHistogramList.Count-1 downto 0 do
  9973. THistogram(FHistogramList[i]).Free;
  9974. FHistogramList.Free;
  9975. inherited Destroy;
  9976. end;
  9977. procedure TColorMapOptimizer.ProcessImage;
  9978. var
  9979. Hist : THistogram;
  9980. i : integer;
  9981. ProcessedImage : boolean;
  9982. begin
  9983. FUseTransparency := False;
  9984. (*
  9985. ** First process images using global color map
  9986. *)
  9987. if (FImage.GlobalColorMap.Count > 0) then
  9988. begin
  9989. Hist := THistogram.Create(FImage.GlobalColorMap);
  9990. ProcessedImage := False;
  9991. // Process all images that are using the global color map
  9992. for i := 0 to FImage.Images.Count-1 do
  9993. if (FImage.Images[i].ColorMap.Count = 0) and (not FImage.Images[i].Empty) then
  9994. begin
  9995. ProcessedImage := True;
  9996. // Note: Do not change order of statements. Shortcircuit evaluation not desired!
  9997. FUseTransparency := Hist.ProcessSubImage(FImage.Images[i]) or FUseTransparency;
  9998. end;
  9999. // Keep the histogram if any images used the global color map...
  10000. if (ProcessedImage) then
  10001. FHistogramList.Add(Hist)
  10002. else // ... otherwise delete it
  10003. Hist.Free;
  10004. end;
  10005. (*
  10006. ** Next process images that have a local color map
  10007. *)
  10008. for i := 0 to FImage.Images.Count-1 do
  10009. if (FImage.Images[i].ColorMap.Count > 0) and (not FImage.Images[i].Empty) then
  10010. begin
  10011. Hist := THistogram.Create(FImage.Images[i].ColorMap);
  10012. FHistogramList.Add(Hist);
  10013. // Note: Do not change order of statements. Shortcircuit evaluation not desired!
  10014. FUseTransparency := Hist.ProcessSubImage(FImage.Images[i]) or FUseTransparency;
  10015. end;
  10016. end;
  10017. procedure TColorMapOptimizer.MergeColors;
  10018. var
  10019. Entry, SameEntry : POptimizeEntry;
  10020. i : integer;
  10021. begin
  10022. (*
  10023. ** Sort by color value
  10024. *)
  10025. FHistogram.Sort(CompareColor);
  10026. (*
  10027. ** Merge same colors
  10028. *)
  10029. SameEntry := POptimizeEntry(FHistogram[0]);
  10030. for i := 1 to FHistogram.Count-1 do
  10031. begin
  10032. Entry := POptimizeEntry(FHistogram[i]);
  10033. ASSERT(Entry^.Count > 0, 'Unused entry exported from THistogram');
  10034. if (Entry^.Color.Value = SameEntry^.Color.Value) then
  10035. begin
  10036. // Transfer usage count to first entry
  10037. inc(SameEntry^.Count, Entry^.Count);
  10038. Entry^.Count := 0; // Use 0 to signal merged entry
  10039. Entry^.Color.SameAs := SameEntry; // Point to master
  10040. end else
  10041. SameEntry := Entry;
  10042. end;
  10043. end;
  10044. procedure TColorMapOptimizer.MapColors;
  10045. var
  10046. i, j : integer;
  10047. Delta, BestDelta : integer;
  10048. BestIndex : integer;
  10049. MaxColors : integer;
  10050. begin
  10051. (*
  10052. ** Sort by usage count
  10053. *)
  10054. FHistogram.Sort(CompareCount);
  10055. (*
  10056. ** Handle transparency
  10057. *)
  10058. if (FUseTransparency) then
  10059. MaxColors := 255
  10060. else
  10061. MaxColors := 256;
  10062. (*
  10063. ** Determine number of colors used (max 256)
  10064. *)
  10065. FFinalCount := FHistogram.Count;
  10066. for i := 0 to FFinalCount-1 do
  10067. if (i >= MaxColors) or (POptimizeEntry(FHistogram[i])^.Count = 0) then
  10068. begin
  10069. FFinalCount := i;
  10070. break;
  10071. end;
  10072. (*
  10073. ** Build color map and reverse map for final entries
  10074. *)
  10075. for i := 0 to FFinalCount-1 do
  10076. begin
  10077. POptimizeEntry(FHistogram[i])^.NewIndex := i;
  10078. FColorMap[i] := POptimizeEntry(FHistogram[i])^.Color.Color;
  10079. end;
  10080. (*
  10081. ** Map colors > 256 to colors <= 256 and build NewIndex color map
  10082. *)
  10083. for i := FFinalCount to FHistogram.Count-1 do
  10084. with POptimizeEntry(FHistogram[i])^ do
  10085. begin
  10086. // Entries with a usage count of -1 is unused
  10087. ASSERT(Count <> -1, 'Internal error: Unused entry exported');
  10088. // Entries with a usage count of 0 has been merged with another entry
  10089. if (Count = 0) then
  10090. begin
  10091. // Use mapping of master entry
  10092. ASSERT(Color.SameAs.NewIndex < 256, 'Internal error: Mapping to unmapped color');
  10093. NewIndex := Color.SameAs.NewIndex;
  10094. end else
  10095. begin
  10096. // Search for entry with nearest color value
  10097. BestIndex := 0;
  10098. BestDelta := 255*3;
  10099. for j := 0 to FFinalCount-1 do
  10100. begin
  10101. Delta := ABS((POptimizeEntry(FHistogram[j])^.Color.Color.Red - Color.Color.Red) +
  10102. (POptimizeEntry(FHistogram[j])^.Color.Color.Green - Color.Color.Green) +
  10103. (POptimizeEntry(FHistogram[j])^.Color.Color.Blue - Color.Color.Blue));
  10104. if (Delta < BestDelta) then
  10105. begin
  10106. BestDelta := Delta;
  10107. BestIndex := j;
  10108. end;
  10109. end;
  10110. NewIndex := POptimizeEntry(FHistogram[BestIndex])^.NewIndex;;
  10111. end;
  10112. end;
  10113. (*
  10114. ** Add transparency color to new color map
  10115. *)
  10116. if (FUseTransparency) then
  10117. begin
  10118. FNewTransparentColorIndex := FFinalCount;
  10119. FColorMap[FFinalCount].Red := 0;
  10120. FColorMap[FFinalCount].Green := 0;
  10121. FColorMap[FFinalCount].Blue := 0;
  10122. inc(FFinalCount);
  10123. end;
  10124. end;
  10125. procedure TColorMapOptimizer.ReplaceColorMaps;
  10126. var
  10127. i : integer;
  10128. begin
  10129. // Zap all local color maps
  10130. for i := 0 to FImage.Images.Count-1 do
  10131. if (FImage.Images[i].ColorMap <> nil) then
  10132. FImage.Images[i].ColorMap.Clear;
  10133. // Store optimized global color map
  10134. FImage.GlobalColorMap.ImportColorMap(FColorMap, FFinalCount);
  10135. FImage.GlobalColorMap.Optimized := True;
  10136. end;
  10137. procedure TColorMapOptimizer.Optimize;
  10138. var
  10139. Total : integer;
  10140. i, j : integer;
  10141. begin
  10142. // Stop all painters during optimize...
  10143. FImage.PaintStop;
  10144. // ...and prevent any new from starting while we are doing our thing
  10145. FImage.Painters.LockList;
  10146. try
  10147. (*
  10148. ** Process all sub images
  10149. *)
  10150. ProcessImage;
  10151. // Prune histograms and calculate total number of colors
  10152. Total := 0;
  10153. for i := 0 to FHistogramList.Count-1 do
  10154. inc(Total, THistogram(FHistogramList[i]).Prune);
  10155. // Allocate global histogram
  10156. FHistogram.Clear;
  10157. FHistogram.Capacity := Total;
  10158. // Move data pointers from local histograms to global histogram
  10159. for i := 0 to FHistogramList.Count-1 do
  10160. with THistogram(FHistogramList[i]) do
  10161. for j := 0 to Count-1 do
  10162. begin
  10163. ASSERT(POptimizeEntry(List[j])^.Count > 0, 'Unused entry exported from THistogram');
  10164. FHistogram.Add(List[j]);
  10165. end;
  10166. (*
  10167. ** Merge same colors
  10168. *)
  10169. MergeColors;
  10170. (*
  10171. ** Build color map and NewIndex map for final entries
  10172. *)
  10173. MapColors;
  10174. (*
  10175. ** Replace local colormaps with global color map
  10176. *)
  10177. ReplaceColorMaps;
  10178. (*
  10179. ** Process images for each color map
  10180. *)
  10181. for i := 0 to FHistogramList.Count-1 do
  10182. THistogram(FHistogramList[i]).MapImages(FUseTransparency, FNewTransparentColorIndex);
  10183. (*
  10184. ** Delete the frame's old bitmaps and palettes
  10185. *)
  10186. for i := 0 to FImage.Images.Count-1 do
  10187. begin
  10188. FImage.Images[i].HasBitmap := False;
  10189. FImage.Images[i].Palette := 0;
  10190. end;
  10191. finally
  10192. FImage.Painters.UnlockList;
  10193. end;
  10194. end;
  10195. ////////////////////////////////////////////////////////////////////////////////
  10196. //
  10197. // TGIFImage
  10198. //
  10199. ////////////////////////////////////////////////////////////////////////////////
  10200. constructor TGIFImage.Create;
  10201. begin
  10202. inherited Create;
  10203. FImages := TGIFImageList.Create(self);
  10204. FHeader := TGIFHeader.Create(self);
  10205. FPainters := TThreadList.Create;
  10206. FGlobalPalette := 0;
  10207. // Load defaults
  10208. FDrawOptions := GIFImageDefaultDrawOptions;
  10209. ColorReduction := GIFImageDefaultColorReduction;
  10210. FReductionBits := GIFImageDefaultColorReductionBits;
  10211. FDitherMode := GIFImageDefaultDitherMode;
  10212. FCompression := GIFImageDefaultCompression;
  10213. FThreadPriority := GIFImageDefaultThreadPriority;
  10214. FAnimationSpeed := GIFImageDefaultAnimationSpeed;
  10215. FDrawBackgroundColor := clNone;
  10216. IsDrawing := False;
  10217. IsInsideGetPalette := False;
  10218. FForceFrame := -1; // 2004.03.09
  10219. NewImage;
  10220. end;
  10221. destructor TGIFImage.Destroy;
  10222. var
  10223. i : integer;
  10224. begin
  10225. PaintStop;
  10226. with FPainters.LockList do
  10227. try
  10228. for i := Count-1 downto 0 do
  10229. TGIFPainter(Items[i]).FImage := nil;
  10230. finally
  10231. FPainters.UnLockList;
  10232. end;
  10233. Clear;
  10234. FPainters.Free;
  10235. FImages.Free;
  10236. FHeader.Free;
  10237. inherited Destroy;
  10238. end;
  10239. procedure TGIFImage.Clear;
  10240. begin
  10241. PaintStop;
  10242. FreeBitmap;
  10243. FImages.Clear;
  10244. FHeader.ColorMap.Clear;
  10245. FHeader.Height := 0;
  10246. FHeader.Width := 0;
  10247. FHeader.Prepare;
  10248. Palette := 0;
  10249. end;
  10250. procedure TGIFImage.NewImage;
  10251. begin
  10252. Clear;
  10253. end;
  10254. function TGIFImage.GetVersion: TGIFVersion;
  10255. var
  10256. v : TGIFVersion;
  10257. i : integer;
  10258. begin
  10259. Result := gvUnknown;
  10260. for i := 0 to FImages.Count-1 do
  10261. begin
  10262. v := FImages[i].Version;
  10263. if (v > Result) then
  10264. Result := v;
  10265. if (v >= high(TGIFVersion)) then
  10266. break;
  10267. end;
  10268. end;
  10269. function TGIFImage.GetColorResolution: integer;
  10270. var
  10271. i : integer;
  10272. begin
  10273. Result := FHeader.ColorResolution;
  10274. for i := 0 to FImages.Count-1 do
  10275. if (FImages[i].ColorResolution > Result) then
  10276. Result := FImages[i].ColorResolution;
  10277. end;
  10278. function TGIFImage.GetBitsPerPixel: integer;
  10279. var
  10280. i : integer;
  10281. begin
  10282. Result := FHeader.BitsPerPixel;
  10283. for i := 0 to FImages.Count-1 do
  10284. if (FImages[i].BitsPerPixel > Result) then
  10285. Result := FImages[i].BitsPerPixel;
  10286. end;
  10287. function TGIFImage.GetBackgroundColorIndex: BYTE;
  10288. begin
  10289. Result := FHeader.BackgroundColorIndex;
  10290. end;
  10291. procedure TGIFImage.SetBackgroundColorIndex(const Value: BYTE);
  10292. begin
  10293. FHeader.BackgroundColorIndex := Value;
  10294. end;
  10295. function TGIFImage.GetBackgroundColor: TColor;
  10296. begin
  10297. Result := FHeader.BackgroundColor;
  10298. end;
  10299. procedure TGIFImage.SetBackgroundColor(const Value: TColor);
  10300. begin
  10301. FHeader.BackgroundColor := Value;
  10302. end;
  10303. function TGIFImage.GetAspectRatio: BYTE;
  10304. begin
  10305. Result := FHeader.AspectRatio;
  10306. end;
  10307. procedure TGIFImage.SetAspectRatio(const Value: BYTE);
  10308. begin
  10309. FHeader.AspectRatio := Value;
  10310. end;
  10311. procedure TGIFImage.SetDrawOptions(Value: TGIFDrawOptions);
  10312. begin
  10313. if (FDrawOptions = Value) then
  10314. exit;
  10315. if (DrawPainter <> nil) then
  10316. DrawPainter.Stop;
  10317. FDrawOptions := Value;
  10318. // Zap all bitmaps
  10319. Pack;
  10320. Changed(self);
  10321. end;
  10322. function TGIFImage.GetAnimate: Boolean;
  10323. begin // 2002.07.07
  10324. Result:= goAnimate in DrawOptions;
  10325. end;
  10326. procedure TGIFImage.SetAnimate(const Value: Boolean);
  10327. begin // 2002.07.07
  10328. if Value then
  10329. DrawOptions:= DrawOptions + [goAnimate]
  10330. else
  10331. DrawOptions:= DrawOptions - [goAnimate];
  10332. end;
  10333. procedure TGIFImage.SetForceFrame(const Value: Integer);
  10334. begin // 2004.03.09
  10335. FForceFrame := Value;
  10336. Changed(Self);
  10337. end;
  10338. procedure TGIFImage.SetAnimationSpeed(Value: integer);
  10339. begin
  10340. if (Value < 0) then
  10341. Value := 0
  10342. else if (Value > 1000) then
  10343. Value := 1000;
  10344. if (Value <> FAnimationSpeed) then
  10345. begin
  10346. FAnimationSpeed := Value;
  10347. // Use the FPainters threadlist to protect FDrawPainter from being modified
  10348. // by the thread while we mess with it
  10349. with FPainters.LockList do
  10350. try
  10351. if (FDrawPainter <> nil) then
  10352. FDrawPainter.AnimationSpeed := FAnimationSpeed;
  10353. finally
  10354. // Release the lock on FPainters to let paint thread kill itself
  10355. FPainters.UnLockList;
  10356. end;
  10357. end;
  10358. end;
  10359. procedure TGIFImage.SetReductionBits(Value: integer);
  10360. begin
  10361. if (Value < 3) or (Value > 8) then
  10362. Error(sInvalidBitSize);
  10363. FReductionBits := Value;
  10364. end;
  10365. procedure TGIFImage.OptimizeColorMap;
  10366. var
  10367. ColorMapOptimizer : TColorMapOptimizer;
  10368. begin
  10369. ColorMapOptimizer := TColorMapOptimizer.Create(self);
  10370. try
  10371. ColorMapOptimizer.Optimize;
  10372. finally
  10373. ColorMapOptimizer.Free;
  10374. end;
  10375. end;
  10376. procedure TGIFImage.Optimize(Options: TGIFOptimizeOptions;
  10377. ColorReduction: TColorReduction; DitherMode: TDitherMode;
  10378. ReductionBits: integer);
  10379. var
  10380. i ,
  10381. j : integer;
  10382. Delay : integer;
  10383. GCE : TGIFGraphicControlExtension;
  10384. ThisRect ,
  10385. NextRect ,
  10386. MergeRect : TRect;
  10387. Prog ,
  10388. MaxProg : integer;
  10389. function Scan(Buf: PChar; Value: Byte; Count: integer): boolean; assembler;
  10390. asm
  10391. PUSH EDI
  10392. MOV EDI, Buf
  10393. MOV ECX, Count
  10394. MOV AL, Value
  10395. REPNE SCASB
  10396. MOV EAX, False
  10397. JNE @@1
  10398. MOV EAX, True
  10399. @@1:POP EDI
  10400. end;
  10401. begin
  10402. if (Empty) then
  10403. exit;
  10404. // Stop all painters during optimize...
  10405. PaintStop;
  10406. // ...and prevent any new from starting while we are doing our thing
  10407. FPainters.LockList;
  10408. try
  10409. Progress(Self, psStarting, 0, False, Rect(0,0,0,0), sProgressOptimizing);
  10410. try
  10411. Prog := 0;
  10412. MaxProg := Images.Count*6;
  10413. // Sort color map by usage and remove unused entries
  10414. if (ooColorMap in Options) then
  10415. begin
  10416. // Optimize global color map
  10417. if (GlobalColorMap.Count > 0) then
  10418. GlobalColorMap.Optimize;
  10419. // Optimize local color maps
  10420. for i := 0 to Images.Count-1 do
  10421. begin
  10422. inc(Prog);
  10423. if (Images[i].ColorMap.Count > 0) then
  10424. begin
  10425. Images[i].ColorMap.Optimize;
  10426. Progress(Self, psRunning, MulDiv(Prog, 100, MaxProg), False,
  10427. Rect(0,0,0,0), sProgressOptimizing);
  10428. end;
  10429. end;
  10430. end;
  10431. // Remove passive elements, pass 1
  10432. if (ooCleanup in Options) then
  10433. begin
  10434. // Check for transparency flag without any transparent pixels
  10435. for i := 0 to Images.Count-1 do
  10436. begin
  10437. inc(Prog);
  10438. if (Images[i].Transparent) then
  10439. begin
  10440. if not(Scan(Images[i].Data,
  10441. Images[i].GraphicControlExtension.TransparentColorIndex,
  10442. Images[i].DataSize)) then
  10443. begin
  10444. Images[i].GraphicControlExtension.Transparent := False;
  10445. Progress(Self, psRunning, MulDiv(Prog, 100, MaxProg), False,
  10446. Rect(0,0,0,0), sProgressOptimizing);
  10447. end;
  10448. end;
  10449. end;
  10450. // Change redundant disposal modes
  10451. for i := 0 to Images.Count-2 do
  10452. begin
  10453. inc(Prog);
  10454. if (Images[i].GraphicControlExtension <> nil) and
  10455. (Images[i].GraphicControlExtension.Disposal in [dmPrevious, dmBackground]) and
  10456. (not Images[i+1].Transparent) then
  10457. begin
  10458. ThisRect := Images[i].BoundsRect;
  10459. NextRect := Images[i+1].BoundsRect;
  10460. if (not IntersectRect(MergeRect, ThisRect, NextRect)) then
  10461. continue;
  10462. // If the next frame completely covers the current frame,
  10463. // change the disposal mode to dmNone
  10464. if (EqualRect(MergeRect, NextRect)) then
  10465. Images[i].GraphicControlExtension.Disposal := dmNone;
  10466. Progress(Self, psRunning, MulDiv(Prog, 100, MaxProg), False,
  10467. Rect(0,0,0,0), sProgressOptimizing);
  10468. end;
  10469. end;
  10470. end else
  10471. inc(Prog, 2*Images.Count);
  10472. // Merge layers of equal pixels (remove redundant pixels)
  10473. if (ooMerge in Options) then
  10474. begin
  10475. // Merge from last to first to avoid intefering with merge
  10476. for i := Images.Count-1 downto 1 do
  10477. begin
  10478. inc(Prog);
  10479. j := i-1;
  10480. // If the "previous" frames uses dmPrevious disposal mode, we must
  10481. // instead merge with the frame before the previous
  10482. while (j > 0) and
  10483. ((Images[j].GraphicControlExtension <> nil) and
  10484. (Images[j].GraphicControlExtension.Disposal = dmPrevious)) do
  10485. dec(j);
  10486. // Merge
  10487. Images[i].Merge(Images[j]);
  10488. Progress(Self, psRunning, MulDiv(Prog, 100, MaxProg), False,
  10489. Rect(0,0,0,0), sProgressOptimizing);
  10490. end;
  10491. end else
  10492. inc(Prog, Images.Count);
  10493. // Crop transparent areas
  10494. if (ooCrop in Options) then
  10495. begin
  10496. for i := Images.Count-1 downto 0 do
  10497. begin
  10498. inc(Prog);
  10499. if (not Images[i].Empty) and (Images[i].Transparent) then
  10500. begin
  10501. // Remember frames delay in case frame is deleted
  10502. Delay := Images[i].GraphicControlExtension.Delay;
  10503. // Crop
  10504. Images[i].Crop;
  10505. // If the frame was completely transparent we remove it
  10506. if (Images[i].Empty) then
  10507. begin
  10508. // Transfer delay to previous frame in case frame was deleted
  10509. if (i > 0) and (Images[i-1].Transparent) then
  10510. Images[i-1].GraphicControlExtension.Delay :=
  10511. Images[i-1].GraphicControlExtension.Delay + Delay;
  10512. Images.Delete(i);
  10513. end;
  10514. Progress(Self, psRunning, MulDiv(Prog, 100, MaxProg), False,
  10515. Rect(0,0,0,0), sProgressOptimizing);
  10516. end;
  10517. end;
  10518. end else
  10519. inc(Prog, Images.Count);
  10520. // Remove passive elements, pass 2
  10521. inc(Prog, Images.Count);
  10522. if (ooCleanup in Options) then
  10523. begin
  10524. for i := Images.Count-1 downto 0 do
  10525. begin
  10526. // Remove comments and application extensions
  10527. for j := Images[i].Extensions.Count-1 downto 0 do
  10528. if (Images[i].Extensions[j] is TGIFCommentExtension) or
  10529. (Images[i].Extensions[j] is TGIFTextExtension) or
  10530. (Images[i].Extensions[j] is TGIFUnknownAppExtension) or
  10531. ((Images[i].Extensions[j] is TGIFAppExtNSLoop) and
  10532. ((i > 0) or (Images.Count = 1))) then
  10533. Images[i].Extensions.Delete(j);
  10534. if (Images[i].GraphicControlExtension <> nil) then
  10535. begin
  10536. GCE := Images[i].GraphicControlExtension;
  10537. // Zap GCE if all of the following are true:
  10538. // * No delay or only one image
  10539. // * Not transparent
  10540. // * No prompt
  10541. // * No disposal or only one image
  10542. if ((GCE.Delay = 0) or (Images.Count = 1)) and
  10543. (not GCE.Transparent) and
  10544. (not GCE.UserInput) and
  10545. ((GCE.Disposal in [dmNone, dmNoDisposal]) or (Images.Count = 1)) then
  10546. begin
  10547. GCE.Free;
  10548. end;
  10549. end;
  10550. // Zap frame if it has become empty
  10551. if (Images[i].Empty) and (Images[i].Extensions.Count = 0) then
  10552. Images[i].Free;
  10553. end;
  10554. Progress(Self, psRunning, MulDiv(Prog, 100, MaxProg), False,
  10555. Rect(0,0,0,0), sProgressOptimizing);
  10556. end else
  10557. // Reduce color depth
  10558. if (ooReduceColors in Options) then
  10559. begin
  10560. if (ColorReduction = rmPalette) then
  10561. Error(sInvalidReduction);
  10562. { TODO -oanme -cFeature : Implement ooReduceColors option. }
  10563. // Not implemented!
  10564. end;
  10565. finally
  10566. if ExceptObject = nil then
  10567. i := 100
  10568. else
  10569. i := 0;
  10570. Progress(Self, psEnding, i, False, Rect(0,0,0,0), sProgressOptimizing);
  10571. end;
  10572. finally
  10573. FPainters.UnlockList;
  10574. end;
  10575. end;
  10576. procedure TGIFImage.Pack;
  10577. var
  10578. i : integer;
  10579. begin
  10580. // Zap bitmaps and palettes
  10581. FreeBitmap;
  10582. Palette := 0;
  10583. for i := 0 to FImages.Count-1 do
  10584. begin
  10585. FImages[i].Bitmap := nil;
  10586. FImages[i].Palette := 0;
  10587. end;
  10588. // Only pack if no global colormap and a single image
  10589. if (FHeader.ColorMap.Count > 0) or (FImages.Count <> 1) then
  10590. exit;
  10591. // Copy local colormap to global
  10592. FHeader.ColorMap.Assign(FImages[0].ColorMap);
  10593. // Zap local colormap
  10594. FImages[0].ColorMap.Clear;
  10595. end;
  10596. procedure TGIFImage.SaveToStream(Stream: TStream);
  10597. var
  10598. n : Integer;
  10599. begin
  10600. Progress(Self, psStarting, 0, False, Rect(0,0,0,0), sProgressSaving);
  10601. try
  10602. // Write header
  10603. FHeader.SaveToStream(Stream);
  10604. // Write images
  10605. FImages.SaveToStream(Stream);
  10606. // Write trailer
  10607. with TGIFTrailer.Create(self) do
  10608. try
  10609. SaveToStream(Stream);
  10610. finally
  10611. Free;
  10612. end;
  10613. finally
  10614. if ExceptObject = nil then
  10615. n := 100
  10616. else
  10617. n := 0;
  10618. Progress(Self, psEnding, n, True, Rect(0,0,0,0), sProgressSaving);
  10619. end;
  10620. end;
  10621. procedure TGIFImage.LoadFromStream(Stream: TStream);
  10622. var
  10623. n : Integer;
  10624. Position : integer;
  10625. begin
  10626. Progress(Self, psStarting, 0, False, Rect(0,0,0,0), sProgressLoading);
  10627. try
  10628. // Zap old image
  10629. Clear;
  10630. Position := Stream.Position;
  10631. try
  10632. // Read header
  10633. FHeader.LoadFromStream(Stream);
  10634. // Read images
  10635. FImages.LoadFromStream(Stream, self);
  10636. // Read trailer
  10637. with TGIFTrailer.Create(self) do
  10638. try
  10639. LoadFromStream(Stream);
  10640. finally
  10641. Free;
  10642. end;
  10643. except
  10644. // Restore stream position in case of error.
  10645. // Not required, but "a nice thing to do"
  10646. Stream.Position := Position;
  10647. raise;
  10648. end;
  10649. finally
  10650. if ExceptObject = nil then
  10651. n := 100
  10652. else
  10653. n := 0;
  10654. Progress(Self, psEnding, n, True, Rect(0,0,0,0), sProgressLoading);
  10655. end;
  10656. end;
  10657. procedure TGIFImage.LoadFromResourceName(Instance: THandle; const ResName: String);
  10658. // 2002.07.07
  10659. var
  10660. Stream: TCustomMemoryStream;
  10661. begin
  10662. Stream := TResourceStream.Create(Instance, ResName, RT_RCDATA);
  10663. try
  10664. LoadFromStream(Stream);
  10665. finally
  10666. Stream.Free;
  10667. end;
  10668. end;
  10669. function TGIFImage.GetBitmap: TBitmap;
  10670. begin
  10671. if not(Empty) then
  10672. begin
  10673. Result := FBitmap;
  10674. if (Result <> nil) then
  10675. exit;
  10676. FBitmap := TBitmap.Create;
  10677. Result := FBitmap;
  10678. FBitmap.OnChange := Changed;
  10679. // Use first image as default
  10680. if (Images.Count > 0) then
  10681. begin
  10682. if (Images[0].Width = Width) and (Images[0].Height = Height) then
  10683. begin
  10684. // Use first image as it has same dimensions
  10685. FBitmap.Assign(Images[0].Bitmap);
  10686. end else
  10687. begin
  10688. // Draw first image on bitmap
  10689. FBitmap.Palette := CopyPalette(Palette);
  10690. FBitmap.Height := Height;
  10691. FBitmap.Width := Width;
  10692. Images[0].Draw(FBitmap.Canvas, FBitmap.Canvas.ClipRect, False, False);
  10693. end;
  10694. end;
  10695. end else
  10696. Result := nil
  10697. end;
  10698. // Create a new (empty) bitmap
  10699. function TGIFImage.NewBitmap: TBitmap;
  10700. begin
  10701. Result := FBitmap;
  10702. if (Result <> nil) then
  10703. exit;
  10704. FBitmap := TBitmap.Create;
  10705. Result := FBitmap;
  10706. FBitmap.OnChange := Changed;
  10707. // Draw first image on bitmap
  10708. FBitmap.Palette := CopyPalette(Palette);
  10709. FBitmap.Height := Height;
  10710. FBitmap.Width := Width;
  10711. end;
  10712. procedure TGIFImage.FreeBitmap;
  10713. begin
  10714. if (DrawPainter <> nil) then
  10715. DrawPainter.Stop;
  10716. if (FBitmap <> nil) then
  10717. begin
  10718. FBitmap.Free;
  10719. FBitmap := nil;
  10720. end;
  10721. end;
  10722. function TGIFImage.Add(Source: TPersistent): integer;
  10723. var
  10724. Image : TGIFSubImage;
  10725. begin
  10726. Image := nil; // To avoid compiler warning - not needed.
  10727. if (Source is TGraphic) then
  10728. begin
  10729. Image := TGIFSubImage.Create(self);
  10730. try
  10731. Image.Assign(Source);
  10732. // ***FIXME*** Documentation should explain the inconsistency here:
  10733. // TGIFimage does not take ownership of Source after TGIFImage.Add() and
  10734. // therefore does not delete Source.
  10735. except
  10736. Image.Free;
  10737. raise;
  10738. end;
  10739. end else
  10740. if (Source is TGIFSubImage) then
  10741. Image := TGIFSubImage(Source)
  10742. else
  10743. Error(sUnsupportedClass);
  10744. Result := FImages.Add(Image);
  10745. FreeBitmap;
  10746. Changed(self);
  10747. end;
  10748. function TGIFImage.GetEmpty: Boolean;
  10749. begin
  10750. Result := (FImages.Count = 0);
  10751. end;
  10752. function TGIFImage.GetHeight: Integer;
  10753. begin
  10754. Result := FHeader.Height;
  10755. end;
  10756. function TGIFImage.GetWidth: Integer;
  10757. begin
  10758. Result := FHeader.Width;
  10759. end;
  10760. function TGIFImage.GetIsTransparent: Boolean;
  10761. var
  10762. i : integer;
  10763. begin
  10764. Result := False;
  10765. for i := 0 to Images.Count-1 do
  10766. if (Images[i].GraphicControlExtension <> nil) and
  10767. (Images[i].GraphicControlExtension.Transparent) then
  10768. begin
  10769. Result := True;
  10770. exit;
  10771. end;
  10772. end;
  10773. function TGIFImage.Equals(Graphic: TGraphic): Boolean;
  10774. begin
  10775. Result := (Graphic = self);
  10776. end;
  10777. function TGIFImage.GetPalette: HPALETTE;
  10778. begin
  10779. // Check for recursion
  10780. // (TGIFImage.GetPalette->TGIFSubImage.GetPalette->TGIFImage.GetPalette etc...)
  10781. if (IsInsideGetPalette) then
  10782. Error(sNoColorTable);
  10783. IsInsideGetPalette := True;
  10784. try
  10785. Result := 0;
  10786. if (FBitmap <> nil) and (FBitmap.Palette <> 0) then
  10787. // Use bitmaps own palette if possible
  10788. Result := FBitmap.Palette
  10789. else if (FGlobalPalette <> 0) then
  10790. // Or a previously exported global palette
  10791. Result := FGlobalPalette
  10792. else if (DoDither) then
  10793. begin
  10794. // or create a new dither palette
  10795. FGlobalPalette := WebPalette;
  10796. Result := FGlobalPalette;
  10797. end else
  10798. if (FHeader.ColorMap.Count > 0) then
  10799. begin
  10800. // or create a new if first time
  10801. FGlobalPalette := FHeader.ColorMap.ExportPalette;
  10802. Result := FGlobalPalette;
  10803. end else
  10804. if (FImages.Count > 0) then
  10805. // This can cause a recursion if no global palette exist and image[0]
  10806. // hasn't got one either. Checked by the IsInsideGetPalette semaphor.
  10807. Result := FImages[0].Palette;
  10808. finally
  10809. IsInsideGetPalette := False;
  10810. end;
  10811. end;
  10812. procedure TGIFImage.SetPalette(Value: HPalette);
  10813. var
  10814. NeedNewBitmap : boolean;
  10815. begin
  10816. if (Value <> FGlobalPalette) then
  10817. begin
  10818. // Zap old palette
  10819. if (FGlobalPalette <> 0) then
  10820. DeleteObject(FGlobalPalette);
  10821. // Zap bitmap unless new palette is same as bitmaps own
  10822. NeedNewBitmap := (FBitmap <> nil) and (Value <> FBitmap.Palette);
  10823. // Use new palette
  10824. FGlobalPalette := Value;
  10825. if (NeedNewBitmap) then
  10826. begin
  10827. // Need to create new bitmap and repaint
  10828. FreeBitmap;
  10829. PaletteModified := True;
  10830. Changed(Self);
  10831. end;
  10832. end;
  10833. end;
  10834. // Obsolete
  10835. // procedure TGIFImage.Changed(Sender: TObject);
  10836. // begin
  10837. // inherited Changed(Sender);
  10838. // end;
  10839. procedure TGIFImage.SetHeight(Value: Integer);
  10840. var
  10841. i : integer;
  10842. begin
  10843. for i := 0 to Images.Count-1 do
  10844. if (Images[i].Top + Images[i].Height > Value) then
  10845. Error(sBadHeight);
  10846. if (Value <> Header.Height) then
  10847. begin
  10848. Header.Height := Value;
  10849. FreeBitmap;
  10850. Changed(self);
  10851. end;
  10852. end;
  10853. procedure TGIFImage.SetWidth(Value: Integer);
  10854. var
  10855. i : integer;
  10856. begin
  10857. for i := 0 to Images.Count-1 do
  10858. if (Images[i].Left + Images[i].Width > Value) then
  10859. Error(sBadWidth);
  10860. if (Value <> Header.Width) then
  10861. begin
  10862. Header.Width := Value;
  10863. FreeBitmap;
  10864. Changed(self);
  10865. end;
  10866. end;
  10867. procedure TGIFImage.WriteData(Stream: TStream);
  10868. begin
  10869. if (GIFImageOptimizeOnStream) then
  10870. Optimize([ooCrop, ooMerge, ooCleanup, ooColorMap, ooReduceColors], rmNone, dmNearest, 8);
  10871. inherited WriteData(Stream);
  10872. end;
  10873. procedure TGIFImage.AssignTo(Dest: TPersistent);
  10874. begin
  10875. if (Dest is TBitmap) then
  10876. Dest.Assign(Bitmap)
  10877. else
  10878. inherited AssignTo(Dest);
  10879. end;
  10880. { TODO 1 -oanme -cImprovement : Better handling of TGIFImage.Assign(Empty TBitmap). }
  10881. procedure TGIFImage.Assign(Source: TPersistent);
  10882. var
  10883. i : integer;
  10884. Image : TGIFSubImage;
  10885. begin
  10886. if (Source = self) then
  10887. exit;
  10888. if (Source = nil) then
  10889. begin
  10890. Clear;
  10891. end else
  10892. //
  10893. // TGIFImage import
  10894. //
  10895. if (Source is TGIFImage) then
  10896. begin
  10897. Clear;
  10898. // Temporarily copy event handlers to be able to generate progress events
  10899. // during the copy and handle copy errors
  10900. OnProgress := TGIFImage(Source).OnProgress;
  10901. try
  10902. FOnWarning := TGIFImage(Source).OnWarning;
  10903. Progress(Self, psStarting, 0, False, Rect(0,0,0,0), sProgressCopying);
  10904. try
  10905. FHeader.Assign(TGIFImage(Source).Header);
  10906. FThreadPriority := TGIFImage(Source).ThreadPriority;
  10907. FDrawBackgroundColor := TGIFImage(Source).DrawBackgroundColor;
  10908. FDrawOptions := TGIFImage(Source).DrawOptions;
  10909. FColorReduction := TGIFImage(Source).ColorReduction;
  10910. FDitherMode := TGIFImage(Source).DitherMode;
  10911. FForceFrame := TGIFImage(Source).ForceFrame; // 2004.03.09
  10912. // 2002.07.07 ->
  10913. FOnWarning:= TGIFImage(Source).FOnWarning;
  10914. FOnStartPaint:= TGIFImage(Source).FOnStartPaint;
  10915. FOnPaint:= TGIFImage(Source).FOnPaint;
  10916. FOnEndPaint:= TGIFImage(Source).FOnEndPaint;
  10917. FOnAfterPaint:= TGIFImage(Source).FOnAfterPaint;
  10918. FOnLoop:= TGIFImage(Source).FOnLoop;
  10919. // 2002.07.07 <-
  10920. for i := 0 to TGIFImage(Source).Images.Count-1 do
  10921. begin
  10922. Image := TGIFSubImage.Create(self);
  10923. Image.Assign(TGIFImage(Source).Images[i]);
  10924. Add(Image);
  10925. Progress(Self, psRunning, MulDiv((i+1), 100, TGIFImage(Source).Images.Count),
  10926. False, Rect(0,0,0,0), sProgressCopying);
  10927. end;
  10928. finally
  10929. if ExceptObject = nil then
  10930. i := 100
  10931. else
  10932. i := 0;
  10933. Progress(Self, psEnding, i, False, Rect(0,0,0,0), sProgressCopying);
  10934. end;
  10935. finally
  10936. // Reset event handlers
  10937. FOnWarning := nil;
  10938. OnProgress := nil;
  10939. end;
  10940. end else
  10941. //
  10942. // Import via TGIFSubImage.Assign
  10943. //
  10944. begin
  10945. Clear;
  10946. Image := TGIFSubImage.Create(self);
  10947. try
  10948. Image.Assign(Source);
  10949. Add(Image);
  10950. except
  10951. on E: EConvertError do
  10952. begin
  10953. Image.Free;
  10954. // Unsupported format - fall back to Source.AssignTo
  10955. inherited Assign(Source);
  10956. end;
  10957. else
  10958. // Unknown conversion error
  10959. Image.Free;
  10960. raise;
  10961. end;
  10962. end;
  10963. end;
  10964. procedure TGIFImage.LoadFromClipboardFormat(AFormat: Word; AData: THandle;
  10965. APalette: HPALETTE);
  10966. {$IFDEF REGISTER_TGIFIMAGE}
  10967. var
  10968. Size : Longint;
  10969. Buffer : Pointer;
  10970. Stream : TMemoryStream;
  10971. Bmp : TBitmap;
  10972. {$ENDIF} // 2002.07.07
  10973. begin // 2002.07.07
  10974. {$IFDEF REGISTER_TGIFIMAGE} // 2002.07.07
  10975. if (AData = 0) then
  10976. AData := GetClipboardData(AFormat);
  10977. if (AData <> 0) and (AFormat = CF_GIF) then
  10978. begin
  10979. // Get size and pointer to data
  10980. Size := GlobalSize(AData);
  10981. Buffer := GlobalLock(AData);
  10982. try
  10983. Stream := TMemoryStream.Create;
  10984. try
  10985. // Copy data to a stream
  10986. Stream.SetSize(Size);
  10987. Move(Buffer^, Stream.Memory^, Size);
  10988. // Load GIF from stream
  10989. LoadFromStream(Stream);
  10990. finally
  10991. Stream.Free;
  10992. end;
  10993. finally
  10994. GlobalUnlock(AData);
  10995. end;
  10996. end else
  10997. if (AData <> 0) and (AFormat = CF_BITMAP) then
  10998. begin
  10999. // No GIF on clipboard - try loading a bitmap instead
  11000. Bmp := TBitmap.Create;
  11001. try
  11002. Bmp.LoadFromClipboardFormat(AFormat, AData, APalette);
  11003. Assign(Bmp);
  11004. finally
  11005. Bmp.Free;
  11006. end;
  11007. end else
  11008. Error(sUnknownClipboardFormat);
  11009. {$ELSE} // 2002.07.07
  11010. Error(sGIFToClipboard); // 2002.07.07
  11011. {$ENDIF} // 2002.07.07
  11012. end;
  11013. procedure TGIFImage.SaveToClipboardFormat(var AFormat: Word; var AData: THandle;
  11014. var APalette: HPALETTE);
  11015. {$IFDEF REGISTER_TGIFIMAGE}
  11016. var
  11017. Stream : TMemoryStream;
  11018. Data : THandle;
  11019. Buffer : Pointer;
  11020. {$ENDIF} // 2002.07.07
  11021. begin // 2002.07.07
  11022. {$IFDEF REGISTER_TGIFIMAGE} // 2002.07.07
  11023. if (Empty) then
  11024. exit;
  11025. // First store a bitmap version on the clipboard...
  11026. Bitmap.SaveToClipboardFormat(AFormat, AData, APalette);
  11027. // ...then store a GIF
  11028. Stream := TMemoryStream.Create;
  11029. try
  11030. // Save the GIF to a memory stream
  11031. SaveToStream(Stream);
  11032. Stream.Position := 0;
  11033. // Allocate some memory for the GIF data
  11034. Data := GlobalAlloc(HeapAllocFlags, Stream.Size);
  11035. try
  11036. if (Data <> 0) then
  11037. begin
  11038. Buffer := GlobalLock(Data);
  11039. try
  11040. // Copy GIF data from stream memory to clipboard memory
  11041. Move(Stream.Memory^, Buffer^, Stream.Size);
  11042. finally
  11043. GlobalUnlock(Data);
  11044. end;
  11045. // Transfer data to clipboard
  11046. if (SetClipboardData(CF_GIF, Data) = 0) then
  11047. Error(sFailedPaste);
  11048. end;
  11049. except
  11050. GlobalFree(Data);
  11051. raise;
  11052. end;
  11053. finally
  11054. Stream.Free;
  11055. end;
  11056. {$ELSE} // 2002.07.07
  11057. Error(sGIFToClipboard); // 2002.07.07
  11058. {$ENDIF} // 2002.07.07
  11059. end;
  11060. function TGIFImage.GetColorMap: TGIFColorMap;
  11061. begin
  11062. Result := FHeader.ColorMap;
  11063. end;
  11064. function TGIFImage.GetDoDither: boolean;
  11065. begin
  11066. Result := (goDither in DrawOptions) and
  11067. (((goAutoDither in DrawOptions) and DoAutoDither) or
  11068. not(goAutoDither in DrawOptions));
  11069. end;
  11070. {$IFDEF VER9x}
  11071. procedure TGIFImage.Progress(Sender: TObject; Stage: TProgressStage;
  11072. PercentDone: Byte; RedrawNow: Boolean; const R: TRect; const Msg: string);
  11073. begin
  11074. if Assigned(FOnProgress) then
  11075. FOnProgress(Sender, Stage, PercentDone, RedrawNow, R, Msg);
  11076. end;
  11077. {$ENDIF}
  11078. procedure TGIFImage.StopDraw;
  11079. {$IFNDEF VER14_PLUS} // 2001.07.23
  11080. var
  11081. Msg : TMsg;
  11082. ThreadWindow : HWND;
  11083. {$ENDIF} // 2001.07.23
  11084. begin
  11085. repeat
  11086. // Use the FPainters threadlist to protect FDrawPainter from being modified
  11087. // by the thread while we mess with it
  11088. with FPainters.LockList do
  11089. try
  11090. if (FDrawPainter = nil) then
  11091. break;
  11092. // Tell thread to terminate
  11093. FDrawPainter.Stop;
  11094. // No need to wait for "thread" to terminate if running in main thread
  11095. if not(goAsync in FDrawPainter.DrawOptions) then
  11096. break;
  11097. finally
  11098. // Release the lock on FPainters to let paint thread kill itself
  11099. FPainters.UnLockList;
  11100. end;
  11101. {$IFDEF VER14_PLUS}
  11102. // 2002.07.07
  11103. if (GetCurrentThreadID = MainThreadID) then
  11104. while CheckSynchronize do {loop};
  11105. {$ELSE}
  11106. // Process Messages to make Synchronize work
  11107. // (Instead of Application.ProcessMessages)
  11108. //{$IFDEF VER14_PLUS} // 2001.07.23
  11109. // Break; // 2001.07.23
  11110. // Sleep(0); // Yield // 2001.07.23
  11111. //{$ELSE} // 2001.07.23
  11112. ThreadWindow := FindWindow('TThreadWindow', nil);
  11113. while PeekMessage(Msg, ThreadWindow, CM_DESTROYWINDOW, CM_EXECPROC, PM_REMOVE) do
  11114. begin
  11115. if (Msg.Message <> WM_QUIT) then
  11116. begin
  11117. TranslateMessage(Msg);
  11118. DispatchMessage(Msg);
  11119. end else
  11120. begin
  11121. PostQuitMessage(Msg.WParam);
  11122. exit;
  11123. end;
  11124. end;
  11125. {$ENDIF} // 2001.07.23
  11126. Sleep(0); // Yield
  11127. until (False);
  11128. FreeBitmap;
  11129. end;
  11130. procedure TGIFImage.Draw(ACanvas: TCanvas; const Rect: TRect);
  11131. var
  11132. Canvas : TCanvas;
  11133. DestRect : TRect;
  11134. {$IFNDEF VER14_PLUS} // 2001.07.23
  11135. Msg : TMsg;
  11136. ThreadWindow : HWND;
  11137. {$ENDIF} // 2001.07.23
  11138. procedure DrawTile(Rect: TRect; Bitmap: TBitmap);
  11139. var
  11140. Tile : TRect;
  11141. begin
  11142. if (goTile in FDrawOptions) then
  11143. begin
  11144. // Note: This design does not handle transparency correctly!
  11145. Tile.Left := Rect.Left;
  11146. Tile.Right := Tile.Left + Width;
  11147. while (Tile.Left < Rect.Right) do
  11148. begin
  11149. Tile.Top := Rect.Top;
  11150. Tile.Bottom := Tile.Top + Height;
  11151. while (Tile.Top < Rect.Bottom) do
  11152. begin
  11153. ACanvas.StretchDraw(Tile, Bitmap);
  11154. Tile.Top := Tile.Top + Height;
  11155. Tile.Bottom := Tile.Top + Height;
  11156. end;
  11157. Tile.Left := Tile.Left + Width;
  11158. Tile.Right := Tile.Left + Width;
  11159. end;
  11160. end else
  11161. ACanvas.StretchDraw(Rect, Bitmap);
  11162. end;
  11163. begin
  11164. // Prevent recursion(s(s(s)))
  11165. if (IsDrawing) or (FImages.Count = 0) then
  11166. exit;
  11167. IsDrawing := True;
  11168. try
  11169. // Copy bitmap to canvas if we are already drawing
  11170. // (or have drawn but are finished)
  11171. if (FImages.Count = 1) or // Only one image
  11172. (not (goAnimate in FDrawOptions)) then // Don't animate
  11173. begin
  11174. // 2004.03.09 ->
  11175. if (FForceFrame >= 0) and (FForceFrame < FImages.Count) then
  11176. FImages[FForceFrame].Draw(ACanvas, Rect, (goTransparent in FDrawOptions), (goTile in FDrawOptions))
  11177. else
  11178. // 2004.03.09 <-
  11179. FImages[0].Draw(ACanvas, Rect, (goTransparent in FDrawOptions), (goTile in FDrawOptions));
  11180. exit;
  11181. end else
  11182. if (FBitmap <> nil) and not(goDirectDraw in FDrawOptions) then
  11183. begin
  11184. DrawTile(Rect, Bitmap);
  11185. exit;
  11186. end;
  11187. // Use the FPainters threadlist to protect FDrawPainter from being modified
  11188. // by the thread while we mess with it
  11189. with FPainters.LockList do
  11190. try
  11191. // If we are already painting on the canvas in goDirectDraw mode
  11192. // and at the same location, just exit and let the painter do
  11193. // its thing when it's ready
  11194. if (FDrawPainter <> nil) and (FDrawPainter.Canvas = ACanvas) and
  11195. EqualRect(FDrawPainter.Rect, Rect) then
  11196. exit;
  11197. // Kill the current paint thread
  11198. StopDraw;
  11199. if not(goDirectDraw in FDrawOptions) then
  11200. begin
  11201. // Create a bitmap to draw on
  11202. NewBitmap;
  11203. Canvas := FBitmap.Canvas;
  11204. DestRect := Canvas.ClipRect;
  11205. // Initialize bitmap canvas with background image
  11206. Canvas.CopyRect(DestRect, ACanvas, Rect);
  11207. end else
  11208. begin
  11209. Canvas := ACanvas;
  11210. DestRect := Rect;
  11211. end;
  11212. // Create new paint thread
  11213. InternalPaint(@FDrawPainter, Canvas, DestRect, FDrawOptions);
  11214. if (FDrawPainter <> nil) then
  11215. begin
  11216. // Launch thread
  11217. FDrawPainter.Start;
  11218. if not(goDirectDraw in FDrawOptions) then
  11219. begin
  11220. {$IFDEF VER14_PLUS}
  11221. // 2002.07.07
  11222. while (FDrawPainter <> nil) and (not FDrawPainter.Terminated) and
  11223. (not FDrawPainter.Started) do
  11224. begin
  11225. if not CheckSynchronize then
  11226. Sleep(0); // Yield
  11227. end;
  11228. {$ELSE}
  11229. //{$IFNDEF VER14_PLUS} // 2001.07.23
  11230. ThreadWindow := FindWindow('TThreadWindow', nil);
  11231. // Wait for thread to render first frame
  11232. while (FDrawPainter <> nil) and (not FDrawPainter.Terminated) and
  11233. (not FDrawPainter.Started) do
  11234. // Process Messages to make Synchronize work
  11235. // (Instead of Application.ProcessMessages)
  11236. if PeekMessage(Msg, ThreadWindow, CM_DESTROYWINDOW, CM_EXECPROC, PM_REMOVE) then
  11237. begin
  11238. if (Msg.Message <> WM_QUIT) then
  11239. begin
  11240. TranslateMessage(Msg);
  11241. DispatchMessage(Msg);
  11242. end else
  11243. begin
  11244. PostQuitMessage(Msg.WParam);
  11245. exit;
  11246. end;
  11247. end else
  11248. Sleep(0); // Yield
  11249. {$ENDIF} // 2001.07.23
  11250. // Draw frame to destination
  11251. DrawTile(Rect, Bitmap);
  11252. end;
  11253. end;
  11254. finally
  11255. FPainters.UnLockList;
  11256. end;
  11257. finally
  11258. IsDrawing := False;
  11259. end;
  11260. end;
  11261. // Internal pain(t) routine used by Draw()
  11262. function TGIFImage.InternalPaint(Painter: PGifPainter; ACanvas: TCanvas;
  11263. const Rect: TRect; Options: TGIFDrawOptions): TGIFPainter;
  11264. begin
  11265. if (Empty) or (Rect.Left >= Rect.Right) or (Rect.Top >= Rect.Bottom) then
  11266. begin
  11267. Result := nil;
  11268. if (Painter <> nil) then
  11269. Painter^ := Result;
  11270. exit;
  11271. end;
  11272. // Draw in main thread if only one image
  11273. if (Images.Count = 1) then
  11274. Options := Options - [goAsync, goAnimate];
  11275. Result := TGIFPainter.CreateRef(Painter, self, ACanvas, Rect, Options);
  11276. FPainters.Add(Result);
  11277. Result.OnStartPaint := FOnStartPaint;
  11278. Result.OnPaint := FOnPaint;
  11279. Result.OnAfterPaint := FOnAfterPaint;
  11280. Result.OnLoop := FOnLoop;
  11281. Result.OnEndPaint := FOnEndPaint;
  11282. if not(goAsync in Options) then
  11283. begin
  11284. // Run in main thread
  11285. Result.Execute;
  11286. // Note: Painter threads executing in the main thread are freed upon exit
  11287. // from the Execute method, so no need to do it here.
  11288. Result := nil;
  11289. if (Painter <> nil) then
  11290. Painter^ := Result;
  11291. end else
  11292. Result.Priority := FThreadPriority;
  11293. end;
  11294. function TGIFImage.Paint(ACanvas: TCanvas; const Rect: TRect;
  11295. Options: TGIFDrawOptions): TGIFPainter;
  11296. begin
  11297. Result := InternalPaint(nil, ACanvas, Rect, Options);
  11298. if (Result <> nil) then
  11299. // Run in separate thread
  11300. Result.Start;
  11301. end;
  11302. procedure TGIFImage.PaintStart;
  11303. var
  11304. i : integer;
  11305. begin
  11306. with FPainters.LockList do
  11307. try
  11308. for i := 0 to Count-1 do
  11309. TGIFPainter(Items[i]).Start;
  11310. finally
  11311. FPainters.UnLockList;
  11312. end;
  11313. end;
  11314. procedure TGIFImage.PaintStop;
  11315. var
  11316. Ghosts : integer;
  11317. i : integer;
  11318. {$IFNDEF VER14_PLUS} // 2001.07.23
  11319. Msg : TMsg;
  11320. ThreadWindow : HWND;
  11321. {$ENDIF} // 2001.07.23
  11322. {$IFNDEF VER14_PLUS} // 2001.07.23
  11323. procedure KillThreads;
  11324. var
  11325. i : integer;
  11326. begin
  11327. with FPainters.LockList do
  11328. try
  11329. for i := Count-1 downto 0 do
  11330. if (goAsync in TGIFPainter(Items[i]).DrawOptions) then
  11331. begin
  11332. TerminateThread(TGIFPainter(Items[i]).Handle, 0);
  11333. Delete(i);
  11334. end;
  11335. finally
  11336. FPainters.UnLockList;
  11337. end;
  11338. end;
  11339. {$ENDIF} // 2001.07.23
  11340. begin
  11341. try
  11342. // Loop until all have died
  11343. repeat
  11344. with FPainters.LockList do
  11345. try
  11346. if (Count = 0) then
  11347. exit;
  11348. // Signal painters to terminate
  11349. // Painters will attempt to remove them self from the
  11350. // painter list when they die
  11351. Ghosts := Count;
  11352. for i := Ghosts-1 downto 0 do
  11353. begin
  11354. if not(goAsync in TGIFPainter(Items[i]).DrawOptions) then
  11355. dec(Ghosts);
  11356. TGIFPainter(Items[i]).Stop;
  11357. end;
  11358. finally
  11359. FPainters.UnLockList;
  11360. end;
  11361. // If all painters were synchronous, there's no purpose waiting for them
  11362. // to terminate, because they are running in the main thread.
  11363. if (Ghosts = 0) then
  11364. exit;
  11365. {$IFDEF VER14_PLUS}
  11366. // 2002.07.07
  11367. if (GetCurrentThreadID = MainThreadID) then
  11368. while CheckSynchronize do {loop};
  11369. {$ELSE}
  11370. // Process Messages to make TThread.Synchronize work
  11371. // (Instead of Application.ProcessMessages)
  11372. //{$IFDEF VER14_PLUS} // 2001.07.23
  11373. // Exit; // 2001.07.23
  11374. //{$ELSE} // 2001.07.23
  11375. ThreadWindow := FindWindow('TThreadWindow', nil);
  11376. if (ThreadWindow = 0) then
  11377. begin
  11378. KillThreads;
  11379. Exit;
  11380. end;
  11381. while PeekMessage(Msg, ThreadWindow, CM_DESTROYWINDOW, CM_EXECPROC, PM_REMOVE) do
  11382. begin
  11383. if (Msg.Message <> WM_QUIT) then
  11384. begin
  11385. TranslateMessage(Msg);
  11386. DispatchMessage(Msg);
  11387. end else
  11388. begin
  11389. KillThreads;
  11390. Exit;
  11391. end;
  11392. end;
  11393. {$ENDIF} // 2001.07.23
  11394. Sleep(0);
  11395. until (False);
  11396. finally
  11397. FreeBitmap;
  11398. end;
  11399. end;
  11400. procedure TGIFImage.PaintPause;
  11401. var
  11402. i : integer;
  11403. begin
  11404. with FPainters.LockList do
  11405. try
  11406. for i := 0 to Count-1 do
  11407. TGIFPainter(Items[i]).Suspend;
  11408. finally
  11409. FPainters.UnLockList;
  11410. end;
  11411. end;
  11412. procedure TGIFImage.PaintResume;
  11413. var
  11414. i : integer;
  11415. begin
  11416. // Implementation is currently same as PaintStart, but don't call PaintStart
  11417. // in case its implementation changes
  11418. with FPainters.LockList do
  11419. try
  11420. for i := 0 to Count-1 do
  11421. TGIFPainter(Items[i]).Start;
  11422. finally
  11423. FPainters.UnLockList;
  11424. end;
  11425. end;
  11426. procedure TGIFImage.PaintRestart;
  11427. var
  11428. i : integer;
  11429. begin
  11430. with FPainters.LockList do
  11431. try
  11432. for i := 0 to Count-1 do
  11433. TGIFPainter(Items[i]).Restart;
  11434. finally
  11435. FPainters.UnLockList;
  11436. end;
  11437. end;
  11438. procedure TGIFImage.Warning(Sender: TObject; Severity: TGIFSeverity; Message: string);
  11439. begin
  11440. if (Assigned(FOnWarning)) then
  11441. FOnWarning(Sender, Severity, Message);
  11442. end;
  11443. {$IFDEF VER12_PLUS}
  11444. {$IFNDEF VER14_PLUS} // not anymore need for Delphi 6 and up // 2001.07.23
  11445. type
  11446. TDummyThread = class(TThread)
  11447. protected
  11448. procedure Execute; override;
  11449. end;
  11450. procedure TDummyThread.Execute;
  11451. begin
  11452. end;
  11453. {$ENDIF} // 2001.07.23
  11454. {$ENDIF}
  11455. var
  11456. DesktopDC: HDC;
  11457. {$IFDEF VER12_PLUS}
  11458. {$IFNDEF VER14_PLUS} // not anymore need for Delphi 6 and up // 2001.07.23
  11459. DummyThread: TThread;
  11460. {$ENDIF} // 2001.07.23
  11461. {$ENDIF}
  11462. ////////////////////////////////////////////////////////////////////////////////
  11463. //
  11464. // Initialization
  11465. //
  11466. ////////////////////////////////////////////////////////////////////////////////
  11467. initialization
  11468. {$IFDEF REGISTER_TGIFIMAGE}
  11469. TPicture.RegisterFileFormat('GIF', sGIFImageFile, TGIFImage);
  11470. CF_GIF := RegisterClipboardFormat(PChar(sGIFImageFile));
  11471. TPicture.RegisterClipboardFormat(CF_GIF, TGIFImage);
  11472. {$ENDIF}
  11473. DesktopDC := GetDC(0);
  11474. try
  11475. PaletteDevice := (GetDeviceCaps(DesktopDC, BITSPIXEL) * GetDeviceCaps(DesktopDC, PLANES) <= 8);
  11476. DoAutoDither := PaletteDevice;
  11477. finally
  11478. ReleaseDC(0, DesktopDC);
  11479. end;
  11480. {$IFDEF VER9x}
  11481. // Note: This doesn't return the same palette as the Delphi 3 system palette
  11482. // since the true system palette contains 20 entries and the Delphi 3 system
  11483. // palette only contains 16.
  11484. // For our purpose this doesn't matter since we do not care about the actual
  11485. // colors (or their number) in the palette.
  11486. // Stock objects doesn't have to be deleted.
  11487. SystemPalette16 := GetStockObject(DEFAULT_PALETTE);
  11488. {$ENDIF}
  11489. {$IFDEF VER12_PLUS}
  11490. // Make sure that at least one thread always exist.
  11491. // This is done to circumvent a race condition bug in Delphi 4.x and later:
  11492. // When threads are deleted and created in rapid succesion, a situation might
  11493. // arise where the thread window is deleted *after* the threads it controls
  11494. // has been created. See the Delphi Bug Lists for more information.
  11495. {$IFNDEF VER14_PLUS} // not anymore need for Delphi 6 and up // 2001.07.23
  11496. DummyThread := TDummyThread.Create(True);
  11497. {$ENDIF} // 2001.07.23
  11498. {$ENDIF}
  11499. ////////////////////////////////////////////////////////////////////////////////
  11500. //
  11501. // Finalization
  11502. //
  11503. ////////////////////////////////////////////////////////////////////////////////
  11504. finalization
  11505. ExtensionList.Free;
  11506. AppExtensionList.Free;
  11507. {$IFNDEF VER9x}
  11508. {$IFDEF REGISTER_TGIFIMAGE}
  11509. TPicture.UnregisterGraphicClass(TGIFImage);
  11510. {$ENDIF}
  11511. {$IFDEF VER100}
  11512. if (pf8BitBitmap <> nil) then
  11513. pf8BitBitmap.Free;
  11514. {$ENDIF}
  11515. {$ENDIF}
  11516. {$IFDEF VER12_PLUS}
  11517. {$IFNDEF VER14_PLUS} // not anymore need for Delphi 6 and up // 2001.07.23
  11518. if (DummyThread <> nil) then
  11519. DummyThread.Free;
  11520. {$ENDIF} // 2001.07.23
  11521. {$ENDIF}
  11522. end.