| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825 |
- {Portable Network Graphics Delphi 1.564 (31 July 2006) }
- {This is a full, open sourced implementation of png in Delphi }
- {It has native support for most of png features including the }
- {partial transparency, gamma and more. }
- {For the latest version, please be sure to check my website }
- {http://pngdelphi.sourceforge.net }
- {Gustavo Huffenbacher Daud (gustavo.daud@terra.com.br) }
- {
- Version 1.564
- 2006-07-25 BUG 1 - There was one GDI Palette object leak
- when assigning from other PNG (fixed)
- BUG 2 - Loosing color information when assigning png
- to bmp on lower screen depth system
- BUG 3 - There was a bug in TStream.GetSize
- (fixed thanks to Vladimir Panteleev)
- IMPROVE 1 - When assigning png to bmp now alpha information
- is drawn (simulated into a white background)
- Version 1.563
- 2006-07-25 BUG 1 - There was a memory bug in the main component
- destructor (fixed thanks to Steven L Brenner)
- BUG 2 - The packages name contained spaces which was
- causing some strange bugs in Delphi
- (fixed thanks to Martijn Saly)
- BUG 3 - Lots of fixes when handling palettes
- (bugs implemented in the last version)
- Fixed thanks to Gabriel Corneanu!!!
- BUG 4 - CreateAlpha was raising an error because it did
- not resized the palette chunk it created;
- Fixed thanks to Miha Sokolov
- IMPROVE 1 - Renamed the pngzlib.pas unit to zlibpas.pas
- as a tentative to all libraries use the same
- shared zlib implementation and to avoid including
- two or three times the same P-Code.
- (Gabriel Corneanu idea)
- Version 1.561
- 2006-05-17 BUG 1 - There was a bug in the method that draws semi
- transparent images (a memory leak). fixed.
- Version 1.56
- 2006-05-09 - IMPROVE 1 - Delphi standard TCanvas support is now implemented
- IMPROVE 2 - The PNG files may now be resized and created from
- scratch using CreateBlank, Resize, Width and Height
- BUG 1 - Fixed some bugs on handling tRNS transparencies
- BUG 2 - Fixed bugs related to palette handling
- Version 1.535
- 2006-04-21 - IMPROVE 1 - Now the library uses the latest ZLIB release (1.2.3)
- (thanks to: Roberto Della Pasqua
- http://www.dellapasqua.com/delphizlib/)
- Version 1.53
- 2006-04-14 -
- BUG 1 - Remove transparency was not working for
- RGB Alpha and Grayscale alpha. fixed
- BUG 2 - There was a bug were compressed text chunks no keyword
- name could not be read
- IMPROVE 1 - Add classes and methods to work with the pHYs chunk
- (including TPNGObject.DrawUsingPixelInformation)
- IMPROVE 3 - Included a property Version to return the library
- version
- IMPROVE 4 - New polish translation (thanks to Piotr Domanski)
- IMPROVE 5 - Now packages for delphi 5, 6, 7, 2005 and 2006
- Also Martijn Saly (thany) made some improvements in the library:
- IMPROVE 1 - SetPixel now works with grayscale
- IMPROVE 2 - Palette property now can be written using a
- windows handle
- Thanks !!
- Version 1.5
- 2005-06-29 - Fixed a lot of bugs using tips from mails that I´ve
- being receiving for some time
- BUG 1 - Loosing palette when assigning to TBitmap. fixed
- BUG 2 - SetPixels and GetPixels worked only with
- parameters in range 0..255. fixed
- BUG 3 - Force type address off using directive
- BUG 4 - TChunkzTXt contained an error
- BUG 5 - MaxIdatSize was not working correctly (fixed thanks
- to Gabriel Corneanu
- BUG 6 - Corrected german translation (thanks to Mael Horz)
- And the following improvements:
- IMPROVE 1 - Create ImageHandleValue properties as public in
- TChunkIHDR to get access to this handle
- IMPROVE 2 - Using SetStretchBltMode to improve stretch quality
- IMPROVE 3 - Scale is now working for alpha transparent images
- IMPROVE 4 - GammaTable propery is now public to support an
- article in the help file
- Version 1.4361
- 2003-03-04 - Fixed important bug for simple transparency when using
- RGB, Grayscale color modes
- Version 1.436
- 2003-03-04 - * NEW * Property Pixels for direct access to pixels
- * IMPROVED * Palette property (TPngObject) (read only)
- Slovenian traslation for the component (Miha Petelin)
- Help file update (scanline article/png->jpg example)
- Version 1.435
- 2003-11-03 - * NEW * New chunk implementation zTXt (method AddzTXt)
- * NEW * New compiler flags to store the extra 8 bits
- from 16 bits samples (when saving it is ignored), the
- extra data may be acessed using ExtraScanline property
- * Fixed * a bug on tIMe chunk
- French translation included (Thanks to IBE Software)
- Bugs fixed
- Version 1.432
- 2002-08-24 - * NEW * A new method, CreateAlpha will transform the
- current image into partial transparency.
- Help file updated with a new article on how to handle
- partial transparency.
- Version 1.431
- 2002-08-14 - Fixed and tested to work on:
- C++ Builder 3
- C++ Builder 5
- Delphi 3
- There was an error when setting TransparentColor, fixed
- New method, RemoveTransparency to remove image
- BIT TRANSPARENCY
- Version 1.43
- 2002-08-01 - * NEW * Support for Delphi 3 and C++ Builder 3
- Implements mostly some things that were missing,
- a few tweaks and fixes.
- Version 1.428
- 2002-07-24 - More minor fixes (thanks to Ian Boyd)
- Bit transparency fixes
- * NEW * Finally support to bit transparency
- (palette / rgb / grayscale -> all)
- Version 1.427
- 2002-07-19 - Lots of bugs and leaks fixed
- * NEW * method to easy adding text comments, AddtEXt
- * NEW * property for setting bit transparency,
- TransparentColor
- Version 1.426
- 2002-07-18 - Clipboard finally fixed and working
- Changed UseDelphi trigger to UseDelphi
- * NEW * Support for bit transparency bitmaps
- when assigning from/to TBitmap objects
- Altough it does not support drawing transparent
- parts of bit transparency pngs (only partial)
- it is closer than ever
- Version 1.425
- 2002-07-01 - Clipboard methods implemented
- Lots of bugs fixed
- Version 1.424
- 2002-05-16 - Scanline and AlphaScanline are now working correctly.
- New methods for handling the clipboard
- Version 1.423
- 2002-05-16 - * NEW * Partial transparency for 1, 2, 4 and 8 bits is
- also supported using the tRNS chunk (for palette and
- grayscaling).
- New bug fixes (Peter Haas).
- Version 1.422
- 2002-05-14 - Fixed some critical leaks, thanks to Peter Haas tips.
- New translation for German (Peter Haas).
- Version 1.421
- 2002-05-06 - Now uses new ZLIB version, 1.1.4 with some security
- fixes.
- LoadFromResourceID and LoadFromResourceName added and
- help file updated for that.
- The resources strings are now located in pnglang.pas.
- New translation for Brazilian Portuguese.
- Bugs fixed.
- IMPORTANT: As always I´m looking for bugs on the library. If
- anyone has found one, please send me an email and
- I will fix asap. Thanks for all the help and ideas
- I'm receiving so far.}
- {My email is : gustavo.daud@terra.com.br}
- {Website link : http://pngdelphi.sourceforge.net}
- {Gustavo Huffenbacher Daud}
- unit pngimage;
- interface
- {Triggers avaliable (edit the fields bellow)}
- {$TYPEDADDRESS OFF}
- {$DEFINE UseDelphi} //Disable fat vcl units(perfect for small apps)
- {$DEFINE ErrorOnUnknownCritical} //Error when finds an unknown critical chunk
- {$DEFINE CheckCRC} //Enables CRC checking
- {$DEFINE RegisterGraphic} //Registers TPNGObject to use with TPicture
- {$DEFINE PartialTransparentDraw} //Draws partial transparent images
- {$DEFINE Store16bits} //Stores the extra 8 bits from 16bits/sample
- {$RANGECHECKS OFF} {$J+}
- uses
- Windows {$IFDEF UseDelphi}, Classes, Graphics, SysUtils{$ENDIF},
- zlibpas, pnglang;
- const
- LibraryVersion = '1.564';
- {$IFNDEF UseDelphi}
- const
- soFromBeginning = 0;
- soFromCurrent = 1;
- soFromEnd = 2;
- {$ENDIF}
- const
- {ZLIB constants}
- ZLIBErrors: Array[-6..2] of string = ('incompatible version (-6)',
- 'buffer error (-5)', 'insufficient memory (-4)', 'data error (-3)',
- 'stream error (-2)', 'file error (-1)', '(0)', 'stream end (1)',
- 'need dictionary (2)');
- Z_NO_FLUSH = 0;
- Z_FINISH = 4;
- Z_STREAM_END = 1;
- {Avaliable PNG filters for mode 0}
- FILTER_NONE = 0;
- FILTER_SUB = 1;
- FILTER_UP = 2;
- FILTER_AVERAGE = 3;
- FILTER_PAETH = 4;
- {Avaliable color modes for PNG}
- COLOR_GRAYSCALE = 0;
- COLOR_RGB = 2;
- COLOR_PALETTE = 3;
- COLOR_GRAYSCALEALPHA = 4;
- COLOR_RGBALPHA = 6;
- type
- {$IFNDEF UseDelphi}
- {Custom exception handler}
- Exception = class(TObject)
- constructor Create(Msg: String);
- end;
- ExceptClass = class of Exception;
- TColor = ColorRef;
- {$ENDIF}
- {Error types}
- EPNGOutMemory = class(Exception);
- EPngError = class(Exception);
- EPngUnexpectedEnd = class(Exception);
- EPngInvalidCRC = class(Exception);
- EPngInvalidIHDR = class(Exception);
- EPNGMissingMultipleIDAT = class(Exception);
- EPNGZLIBError = class(Exception);
- EPNGInvalidPalette = class(Exception);
- EPNGInvalidFileHeader = class(Exception);
- EPNGIHDRNotFirst = class(Exception);
- EPNGNotExists = class(Exception);
- EPNGSizeExceeds = class(Exception);
- EPNGMissingPalette = class(Exception);
- EPNGUnknownCriticalChunk = class(Exception);
- EPNGUnknownCompression = class(Exception);
- EPNGUnknownInterlace = class(Exception);
- EPNGNoImageData = class(Exception);
- EPNGCouldNotLoadResource = class(Exception);
- EPNGCannotChangeTransparent = class(Exception);
- EPNGHeaderNotPresent = class(Exception);
- EPNGInvalidNewSize = class(Exception);
- EPNGInvalidSpec = class(Exception);
- type
- {Direct access to pixels using R,G,B}
- TRGBLine = array[word] of TRGBTriple;
- pRGBLine = ^TRGBLine;
- {Same as TBitmapInfo but with allocated space for}
- {palette entries}
- TMAXBITMAPINFO = packed record
- bmiHeader: TBitmapInfoHeader;
- bmiColors: packed array[0..255] of TRGBQuad;
- end;
- {Transparency mode for pngs}
- TPNGTransparencyMode = (ptmNone, ptmBit, ptmPartial);
- {Pointer to a cardinal type}
- pCardinal = ^Cardinal;
- {Access to a rgb pixel}
- pRGBPixel = ^TRGBPixel;
- TRGBPixel = packed record
- B, G, R: Byte;
- end;
- {Pointer to an array of bytes type}
- TByteArray = Array[Word] of Byte;
- pByteArray = ^TByteArray;
- {Forward}
- TPNGObject = class;
- pPointerArray = ^TPointerArray;
- TPointerArray = Array[Word] of Pointer;
- {Contains a list of objects}
- TPNGPointerList = class
- private
- fOwner: TPNGObject;
- fCount : Cardinal;
- fMemory: pPointerArray;
- function GetItem(Index: Cardinal): Pointer;
- procedure SetItem(Index: Cardinal; const Value: Pointer);
- protected
- {Removes an item}
- function Remove(Value: Pointer): Pointer; virtual;
- {Inserts an item}
- procedure Insert(Value: Pointer; Position: Cardinal);
- {Add a new item}
- procedure Add(Value: Pointer);
- {Returns an item}
- property Item[Index: Cardinal]: Pointer read GetItem write SetItem;
- {Set the size of the list}
- procedure SetSize(const Size: Cardinal);
- {Returns owner}
- property Owner: TPNGObject read fOwner;
- public
- {Returns number of items}
- property Count: Cardinal read fCount write SetSize;
- {Object being either created or destroyed}
- constructor Create(AOwner: TPNGObject);
- destructor Destroy; override;
- end;
- {Forward declaration}
- TChunk = class;
- TChunkClass = class of TChunk;
- {Same as TPNGPointerList but providing typecasted values}
- TPNGList = class(TPNGPointerList)
- private
- {Used with property Item}
- function GetItem(Index: Cardinal): TChunk;
- public
- {Finds the first item with this class}
- function FindChunk(ChunkClass: TChunkClass): TChunk;
- {Removes an item}
- procedure RemoveChunk(Chunk: TChunk); overload;
- {Add a new chunk using the class from the parameter}
- function Add(ChunkClass: TChunkClass): TChunk;
- {Returns pointer to the first chunk of class}
- function ItemFromClass(ChunkClass: TChunkClass): TChunk;
- {Returns a chunk item from the list}
- property Item[Index: Cardinal]: TChunk read GetItem;
- end;
- {$IFNDEF UseDelphi}
- {The STREAMs bellow are only needed in case delphi provided ones is not}
- {avaliable (UseDelphi trigger not set)}
- {Object becomes handles}
- TCanvas = THandle;
- TBitmap = HBitmap;
- {Trick to work}
- TPersistent = TObject;
- {Base class for all streams}
- TStream = class
- protected
- {Returning/setting size}
- function GetSize: Longint; virtual;
- procedure SetSize(const Value: Longint); virtual; abstract;
- {Returns/set position}
- function GetPosition: Longint; virtual;
- procedure SetPosition(const Value: Longint); virtual;
- public
- {Returns/sets current position}
- property Position: Longint read GetPosition write SetPosition;
- {Property returns/sets size}
- property Size: Longint read GetSize write SetSize;
- {Allows reading/writing data}
- function Read(var Buffer; Count: Longint): Cardinal; virtual; abstract;
- function Write(const Buffer; Count: Longint): Cardinal; virtual; abstract;
- {Copies from another Stream}
- function CopyFrom(Source: TStream;
- Count: Cardinal): Cardinal; virtual;
- {Seeks a stream position}
- function Seek(Offset: Longint; Origin: Word): Longint; virtual; abstract;
- end;
- {File stream modes}
- TFileStreamMode = (fsmRead, fsmWrite, fsmCreate);
- TFileStreamModeSet = set of TFileStreamMode;
- {File stream for reading from files}
- TFileStream = class(TStream)
- private
- {Opened mode}
- Filemode: TFileStreamModeSet;
- {Handle}
- fHandle: THandle;
- protected
- {Set the size of the file}
- procedure SetSize(const Value: Longint); override;
- public
- {Seeks a file position}
- function Seek(Offset: Longint; Origin: Word): Longint; override;
- {Reads/writes data from/to the file}
- function Read(var Buffer; Count: Longint): Cardinal; override;
- function Write(const Buffer; Count: Longint): Cardinal; override;
- {Stream being created and destroy}
- constructor Create(Filename: String; Mode: TFileStreamModeSet);
- destructor Destroy; override;
- end;
- {Stream for reading from resources}
- TResourceStream = class(TStream)
- constructor Create(Instance: HInst; const ResName: String; ResType:PChar);
- private
- {Variables for reading}
- Size: Integer;
- Memory: Pointer;
- Position: Integer;
- protected
- {Set the size of the file}
- procedure SetSize(const Value: Longint); override;
- public
- {Stream processing}
- function Read(var Buffer; Count: Integer): Cardinal; override;
- function Seek(Offset: Integer; Origin: Word): Longint; override;
- function Write(const Buffer; Count: Longint): Cardinal; override;
- end;
- {$ENDIF}
- {Forward}
- TChunkIHDR = class;
- TChunkpHYs = class;
- {Interlace method}
- TInterlaceMethod = (imNone, imAdam7);
- {Compression level type}
- TCompressionLevel = 0..9;
- {Filters type}
- TFilter = (pfNone, pfSub, pfUp, pfAverage, pfPaeth);
- TFilters = set of TFilter;
- {Png implementation object}
- TPngObject = class{$IFDEF UseDelphi}(TGraphic){$ENDIF}
- protected
- {Inverse gamma table values}
- InverseGamma: Array[Byte] of Byte;
- procedure InitializeGamma;
- private
- {Canvas}
- {$IFDEF UseDelphi}fCanvas: TCanvas;{$ENDIF}
- {Filters to test to encode}
- fFilters: TFilters;
- {Compression level for ZLIB}
- fCompressionLevel: TCompressionLevel;
- {Maximum size for IDAT chunks}
- fMaxIdatSize: Integer;
- {Returns if image is interlaced}
- fInterlaceMethod: TInterlaceMethod;
- {Chunks object}
- fChunkList: TPngList;
- {Clear all chunks in the list}
- procedure ClearChunks;
- {Returns if header is present}
- function HeaderPresent: Boolean;
- procedure GetPixelInfo(var LineSize, Offset: Cardinal);
- {Returns linesize and byte offset for pixels}
- procedure SetMaxIdatSize(const Value: Integer);
- function GetAlphaScanline(const LineIndex: Integer): pByteArray;
- function GetScanline(const LineIndex: Integer): Pointer;
- {$IFDEF Store16bits}
- function GetExtraScanline(const LineIndex: Integer): Pointer;
- {$ENDIF}
- function GetPixelInformation: TChunkpHYs;
- function GetTransparencyMode: TPNGTransparencyMode;
- function GetTransparentColor: TColor;
- procedure SetTransparentColor(const Value: TColor);
- {Returns the version}
- function GetLibraryVersion: String;
- protected
- {Being created}
- BeingCreated: Boolean;
- {Returns / set the image palette}
- function GetPalette: HPALETTE; {$IFDEF UseDelphi}override;{$ENDIF}
- procedure SetPalette(Value: HPALETTE); {$IFDEF UseDelphi}override;{$ENDIF}
- procedure DoSetPalette(Value: HPALETTE; const UpdateColors: Boolean);
- {Returns/sets image width and height}
- function GetWidth: Integer; {$IFDEF UseDelphi}override;{$ENDIF}
- function GetHeight: Integer; {$IFDEF UseDelphi}override; {$ENDIF}
- procedure SetWidth(Value: Integer); {$IFDEF UseDelphi}override; {$ENDIF}
- procedure SetHeight(Value: Integer); {$IFDEF UseDelphi}override;{$ENDIF}
- {Assigns from another TPNGObject}
- procedure AssignPNG(Source: TPNGObject);
- {Returns if the image is empty}
- function GetEmpty: Boolean; {$IFDEF UseDelphi}override; {$ENDIF}
- {Used with property Header}
- function GetHeader: TChunkIHDR;
- {Draws using partial transparency}
- procedure DrawPartialTrans(DC: HDC; Rect: TRect);
- {$IFDEF UseDelphi}
- {Returns if the image is transparent}
- function GetTransparent: Boolean; override;
- {$ENDIF}
- {Returns a pixel}
- function GetPixels(const X, Y: Integer): TColor; virtual;
- procedure SetPixels(const X, Y: Integer; const Value: TColor); virtual;
- public
- {Gamma table array}
- GammaTable: Array[Byte] of Byte;
- {Resizes the PNG image}
- procedure Resize(const CX, CY: Integer);
- {Generates alpha information}
- procedure CreateAlpha;
- {Removes the image transparency}
- procedure RemoveTransparency;
- {Transparent color}
- property TransparentColor: TColor read GetTransparentColor write
- SetTransparentColor;
- {Add text chunk, TChunkTEXT, TChunkzTXT}
- procedure AddtEXt(const Keyword, Text: String);
- procedure AddzTXt(const Keyword, Text: String);
- {$IFDEF UseDelphi}
- {Saves to clipboard format (thanks to Antoine Pottern)}
- procedure SaveToClipboardFormat(var AFormat: Word; var AData: THandle;
- var APalette: HPalette); override;
- procedure LoadFromClipboardFormat(AFormat: Word; AData: THandle;
- APalette: HPalette); override;
- {$ENDIF}
- {Calling errors}
- procedure RaiseError(ExceptionClass: ExceptClass; Text: String);
- {Returns a scanline from png}
- property Scanline[const Index: Integer]: Pointer read GetScanline;
- {$IFDEF Store16bits}
- property ExtraScanline[const Index: Integer]: Pointer read GetExtraScanline;
- {$ENDIF}
- {Used to return pixel information}
- function HasPixelInformation: Boolean;
- property PixelInformation: TChunkpHYs read GetPixelInformation;
- property AlphaScanline[const Index: Integer]: pByteArray read
- GetAlphaScanline;
- procedure DrawUsingPixelInformation(Canvas: TCanvas; Point: TPoint);
- {Canvas}
- {$IFDEF UseDelphi}property Canvas: TCanvas read fCanvas;{$ENDIF}
- {Returns pointer to the header}
- property Header: TChunkIHDR read GetHeader;
- {Returns the transparency mode used by this png}
- property TransparencyMode: TPNGTransparencyMode read GetTransparencyMode;
- {Assigns from another object}
- procedure Assign(Source: TPersistent);{$IFDEF UseDelphi}override;{$ENDIF}
- {Assigns to another object}
- procedure AssignTo(Dest: TPersistent);{$IFDEF UseDelphi}override;{$ENDIF}
- {Assigns from a windows bitmap handle}
- procedure AssignHandle(Handle: HBitmap; Transparent: Boolean;
- TransparentColor: ColorRef);
- {Draws the image into a canvas}
- procedure Draw(ACanvas: TCanvas; const Rect: TRect);
- {$IFDEF UseDelphi}override;{$ENDIF}
- {Width and height properties}
- property Width: Integer read GetWidth;
- property Height: Integer read GetHeight;
- {Returns if the image is interlaced}
- property InterlaceMethod: TInterlaceMethod read fInterlaceMethod
- write fInterlaceMethod;
- {Filters to test to encode}
- property Filters: TFilters read fFilters write fFilters;
- {Maximum size for IDAT chunks, default and minimum is 65536}
- property MaxIdatSize: Integer read fMaxIdatSize write SetMaxIdatSize;
- {Property to return if the image is empty or not}
- property Empty: Boolean read GetEmpty;
- {Compression level}
- property CompressionLevel: TCompressionLevel read fCompressionLevel
- write fCompressionLevel;
- {Access to the chunk list}
- property Chunks: TPngList read fChunkList;
- {Object being created and destroyed}
- constructor Create; {$IFDEF UseDelphi}override;{$ENDIF}
- constructor CreateBlank(ColorType, Bitdepth: Cardinal; cx, cy: Integer);
- destructor Destroy; override;
- {$IFNDEF UseDelphi}procedure LoadFromFile(const Filename: String);{$ENDIF}
- {$IFNDEF UseDelphi}procedure SaveToFile(const Filename: String);{$ENDIF}
- procedure LoadFromStream(Stream: TStream);
- {$IFDEF UseDelphi}override;{$ENDIF}
- procedure SaveToStream(Stream: TStream); {$IFDEF UseDelphi}override;{$ENDIF}
- {Loading the image from resources}
- procedure LoadFromResourceName(Instance: HInst; const Name: String);
- procedure LoadFromResourceID(Instance: HInst; ResID: Integer);
- {Access to the png pixels}
- property Pixels[const X, Y: Integer]: TColor read GetPixels write SetPixels;
- {Palette property}
- {$IFNDEF UseDelphi}property Palette: HPalette read GetPalette write
- SetPalette;{$ENDIF}
- {Returns the version}
- property Version: String read GetLibraryVersion;
- end;
- {Chunk name object}
- TChunkName = Array[0..3] of Char;
- {Global chunk object}
- TChunk = class
- private
- {Contains data}
- fData: Pointer;
- fDataSize: Cardinal;
- {Stores owner}
- fOwner: TPngObject;
- {Stores the chunk name}
- fName: TChunkName;
- {Returns pointer to the TChunkIHDR}
- function GetHeader: TChunkIHDR;
- {Used with property index}
- function GetIndex: Integer;
- {Should return chunk class/name}
- class function GetName: String; virtual;
- {Returns the chunk name}
- function GetChunkName: String;
- public
- {Returns index from list}
- property Index: Integer read GetIndex;
- {Returns pointer to the TChunkIHDR}
- property Header: TChunkIHDR read GetHeader;
- {Resize the data}
- procedure ResizeData(const NewSize: Cardinal);
- {Returns data and size}
- property Data: Pointer read fData;
- property DataSize: Cardinal read fDataSize;
- {Assigns from another TChunk}
- procedure Assign(Source: TChunk); virtual;
- {Returns owner}
- property Owner: TPngObject read fOwner;
- {Being destroyed/created}
- constructor Create(Owner: TPngObject); virtual;
- destructor Destroy; override;
- {Returns chunk class/name}
- property Name: String read GetChunkName;
- {Loads the chunk from a stream}
- function LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean; virtual;
- {Saves the chunk to a stream}
- function SaveData(Stream: TStream): Boolean;
- function SaveToStream(Stream: TStream): Boolean; virtual;
- end;
- {Chunk classes}
- TChunkIEND = class(TChunk); {End chunk}
- {IHDR data}
- pIHDRData = ^TIHDRData;
- TIHDRData = packed record
- Width, Height: Cardinal;
- BitDepth,
- ColorType,
- CompressionMethod,
- FilterMethod,
- InterlaceMethod: Byte;
- end;
- {Information header chunk}
- TChunkIHDR = class(TChunk)
- private
- {Current image}
- ImageHandle: HBitmap;
- ImageDC: HDC;
- ImagePalette: HPalette;
- {Output windows bitmap}
- HasPalette: Boolean;
- BitmapInfo: TMaxBitmapInfo;
- {Stores the image bytes}
- {$IFDEF Store16bits}ExtraImageData: Pointer;{$ENDIF}
- ImageData: pointer;
- ImageAlpha: Pointer;
- {Contains all the ihdr data}
- IHDRData: TIHDRData;
- protected
- BytesPerRow: Integer;
- {Creates a grayscale palette}
- function CreateGrayscalePalette(Bitdepth: Integer): HPalette;
- {Copies the palette to the Device Independent bitmap header}
- procedure PaletteToDIB(Palette: HPalette);
- {Resizes the image data to fill the color type, bit depth, }
- {width and height parameters}
- procedure PrepareImageData;
- {Release allocated ImageData memory}
- procedure FreeImageData;
- public
- {Access to ImageHandle}
- property ImageHandleValue: HBitmap read ImageHandle;
- {Properties}
- property Width: Cardinal read IHDRData.Width write IHDRData.Width;
- property Height: Cardinal read IHDRData.Height write IHDRData.Height;
- property BitDepth: Byte read IHDRData.BitDepth write IHDRData.BitDepth;
- property ColorType: Byte read IHDRData.ColorType write IHDRData.ColorType;
- property CompressionMethod: Byte read IHDRData.CompressionMethod
- write IHDRData.CompressionMethod;
- property FilterMethod: Byte read IHDRData.FilterMethod
- write IHDRData.FilterMethod;
- property InterlaceMethod: Byte read IHDRData.InterlaceMethod
- write IHDRData.InterlaceMethod;
- {Loads the chunk from a stream}
- function LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean; override;
- {Saves the chunk to a stream}
- function SaveToStream(Stream: TStream): Boolean; override;
- {Destructor/constructor}
- constructor Create(Owner: TPngObject); override;
- destructor Destroy; override;
- {Assigns from another TChunk}
- procedure Assign(Source: TChunk); override;
- end;
- {pHYs chunk}
- pUnitType = ^TUnitType;
- TUnitType = (utUnknownType{utUnknown}, utMeter);
- TChunkpHYs = class(TChunk)
- private
- fPPUnitX, fPPUnitY: Cardinal;
- fUnit: TUnitType;
- public
- {Returns the properties}
- property PPUnitX: Cardinal read fPPUnitX write fPPUnitX;
- property PPUnitY: Cardinal read fPPUnitY write fPPUnitY;
- property UnitType: TUnitType read fUnit write fUnit;
- {Loads the chunk from a stream}
- function LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean; override;
- {Saves the chunk to a stream}
- function SaveToStream(Stream: TStream): Boolean; override;
- {Assigns from another TChunk}
- procedure Assign(Source: TChunk); override;
- end;
- {Gamma chunk}
- TChunkgAMA = class(TChunk)
- private
- {Returns/sets the value for the gamma chunk}
- function GetValue: Cardinal;
- procedure SetValue(const Value: Cardinal);
- public
- {Returns/sets gamma value}
- property Gamma: Cardinal read GetValue write SetValue;
- {Loading the chunk from a stream}
- function LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean; override;
- {Being created}
- constructor Create(Owner: TPngObject); override;
- {Assigns from another TChunk}
- procedure Assign(Source: TChunk); override;
- end;
- {ZLIB Decompression extra information}
- TZStreamRec2 = packed record
- {From ZLIB}
- ZLIB: TZStreamRec;
- {Additional info}
- Data: Pointer;
- fStream : TStream;
- end;
- {Palette chunk}
- TChunkPLTE = class(TChunk)
- protected
- {Number of items in the palette}
- fCount: Integer;
- private
- {Contains the palette handle}
- function GetPaletteItem(Index: Byte): TRGBQuad;
- public
- {Returns the color for each item in the palette}
- property Item[Index: Byte]: TRGBQuad read GetPaletteItem;
- {Returns the number of items in the palette}
- property Count: Integer read fCount;
- {Loads the chunk from a stream}
- function LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean; override;
- {Saves the chunk to a stream}
- function SaveToStream(Stream: TStream): Boolean; override;
- {Assigns from another TChunk}
- procedure Assign(Source: TChunk); override;
- end;
- {Transparency information}
- TChunktRNS = class(TChunk)
- private
- fBitTransparency: Boolean;
- function GetTransparentColor: ColorRef;
- {Returns the transparent color}
- procedure SetTransparentColor(const Value: ColorRef);
- public
- {Palette values for transparency}
- PaletteValues: Array[Byte] of Byte;
- {Returns if it uses bit transparency}
- property BitTransparency: Boolean read fBitTransparency;
- {Returns the transparent color}
- property TransparentColor: ColorRef read GetTransparentColor write
- SetTransparentColor;
- {Loads/saves the chunk from/to a stream}
- function LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean; override;
- function SaveToStream(Stream: TStream): Boolean; override;
- {Assigns from another TChunk}
- procedure Assign(Source: TChunk); override;
- end;
- {Actual image information}
- TChunkIDAT = class(TChunk)
- private
- {Holds another pointer to the TChunkIHDR}
- Header: TChunkIHDR;
- {Stores temporary image width and height}
- ImageWidth, ImageHeight: Integer;
- {Size in bytes of each line and offset}
- Row_Bytes, Offset : Cardinal;
- {Contains data for the lines}
- Encode_Buffer: Array[0..5] of pByteArray;
- Row_Buffer: Array[Boolean] of pByteArray;
- {Variable to invert the Row_Buffer used}
- RowUsed: Boolean;
- {Ending position for the current IDAT chunk}
- EndPos: Integer;
- {Filter the current line}
- procedure FilterRow;
- {Filter to encode and returns the best filter}
- function FilterToEncode: Byte;
- {Reads ZLIB compressed data}
- function IDATZlibRead(var ZLIBStream: TZStreamRec2; Buffer: Pointer;
- Count: Integer; var EndPos: Integer; var crcfile: Cardinal): Integer;
- {Compress and writes IDAT data}
- procedure IDATZlibWrite(var ZLIBStream: TZStreamRec2; Buffer: Pointer;
- const Length: Cardinal);
- procedure FinishIDATZlib(var ZLIBStream: TZStreamRec2);
- {Prepares the palette}
- procedure PreparePalette;
- protected
- {Decode interlaced image}
- procedure DecodeInterlacedAdam7(Stream: TStream;
- var ZLIBStream: TZStreamRec2; const Size: Integer; var crcfile: Cardinal);
- {Decode non interlaced imaged}
- procedure DecodeNonInterlaced(Stream: TStream;
- var ZLIBStream: TZStreamRec2; const Size: Integer;
- var crcfile: Cardinal);
- protected
- {Encode non interlaced images}
- procedure EncodeNonInterlaced(Stream: TStream;
- var ZLIBStream: TZStreamRec2);
- {Encode interlaced images}
- procedure EncodeInterlacedAdam7(Stream: TStream;
- var ZLIBStream: TZStreamRec2);
- protected
- {Memory copy methods to decode}
- procedure CopyNonInterlacedRGB8(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyNonInterlacedRGB16(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyNonInterlacedPalette148(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyNonInterlacedPalette2(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyNonInterlacedGray2(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyNonInterlacedGrayscale16(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyNonInterlacedRGBAlpha8(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyNonInterlacedRGBAlpha16(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyNonInterlacedGrayscaleAlpha8(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyNonInterlacedGrayscaleAlpha16(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyInterlacedRGB8(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyInterlacedRGB16(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyInterlacedPalette148(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyInterlacedPalette2(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyInterlacedGray2(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyInterlacedGrayscale16(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyInterlacedRGBAlpha8(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyInterlacedRGBAlpha16(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyInterlacedGrayscaleAlpha8(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- procedure CopyInterlacedGrayscaleAlpha16(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- protected
- {Memory copy methods to encode}
- procedure EncodeNonInterlacedRGB8(Src, Dest, Trans: pChar);
- procedure EncodeNonInterlacedRGB16(Src, Dest, Trans: pChar);
- procedure EncodeNonInterlacedGrayscale16(Src, Dest, Trans: pChar);
- procedure EncodeNonInterlacedPalette148(Src, Dest, Trans: pChar);
- procedure EncodeNonInterlacedRGBAlpha8(Src, Dest, Trans: pChar);
- procedure EncodeNonInterlacedRGBAlpha16(Src, Dest, Trans: pChar);
- procedure EncodeNonInterlacedGrayscaleAlpha8(Src, Dest, Trans: pChar);
- procedure EncodeNonInterlacedGrayscaleAlpha16(Src, Dest, Trans: pChar);
- procedure EncodeInterlacedRGB8(const Pass: Byte; Src, Dest, Trans: pChar);
- procedure EncodeInterlacedRGB16(const Pass: Byte; Src, Dest, Trans: pChar);
- procedure EncodeInterlacedPalette148(const Pass: Byte;
- Src, Dest, Trans: pChar);
- procedure EncodeInterlacedGrayscale16(const Pass: Byte;
- Src, Dest, Trans: pChar);
- procedure EncodeInterlacedRGBAlpha8(const Pass: Byte;
- Src, Dest, Trans: pChar);
- procedure EncodeInterlacedRGBAlpha16(const Pass: Byte;
- Src, Dest, Trans: pChar);
- procedure EncodeInterlacedGrayscaleAlpha8(const Pass: Byte;
- Src, Dest, Trans: pChar);
- procedure EncodeInterlacedGrayscaleAlpha16(const Pass: Byte;
- Src, Dest, Trans: pChar);
- public
- {Loads the chunk from a stream}
- function LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean; override;
- {Saves the chunk to a stream}
- function SaveToStream(Stream: TStream): Boolean; override;
- end;
- {Image last modification chunk}
- TChunktIME = class(TChunk)
- private
- {Holds the variables}
- fYear: Word;
- fMonth, fDay, fHour, fMinute, fSecond: Byte;
- public
- {Returns/sets variables}
- property Year: Word read fYear write fYear;
- property Month: Byte read fMonth write fMonth;
- property Day: Byte read fDay write fDay;
- property Hour: Byte read fHour write fHour;
- property Minute: Byte read fMinute write fMinute;
- property Second: Byte read fSecond write fSecond;
- {Loads the chunk from a stream}
- function LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean; override;
- {Saves the chunk to a stream}
- function SaveToStream(Stream: TStream): Boolean; override;
- {Assigns from another TChunk}
- procedure Assign(Source: TChunk); override;
- end;
- {Textual data}
- TChunktEXt = class(TChunk)
- private
- fKeyword, fText: String;
- public
- {Keyword and text}
- property Keyword: String read fKeyword write fKeyword;
- property Text: String read fText write fText;
- {Loads the chunk from a stream}
- function LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean; override;
- {Saves the chunk to a stream}
- function SaveToStream(Stream: TStream): Boolean; override;
- {Assigns from another TChunk}
- procedure Assign(Source: TChunk); override;
- end;
- {zTXT chunk}
- TChunkzTXt = class(TChunktEXt)
- {Loads the chunk from a stream}
- function LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean; override;
- {Saves the chunk to a stream}
- function SaveToStream(Stream: TStream): Boolean; override;
- end;
- {Here we test if it's c++ builder or delphi version 3 or less}
- {$IFDEF VER110}{$DEFINE DelphiBuilder3Less}{$ENDIF}
- {$IFDEF VER100}{$DEFINE DelphiBuilder3Less}{$ENDIF}
- {$IFDEF VER93}{$DEFINE DelphiBuilder3Less}{$ENDIF}
- {$IFDEF VER90}{$DEFINE DelphiBuilder3Less}{$ENDIF}
- {$IFDEF VER80}{$DEFINE DelphiBuilder3Less}{$ENDIF}
- {Registers a new chunk class}
- procedure RegisterChunk(ChunkClass: TChunkClass);
- {Calculates crc}
- function update_crc(crc: {$IFNDEF DelphiBuilder3Less}Cardinal{$ELSE}Integer
- {$ENDIF}; buf: pByteArray; len: Integer): Cardinal;
- {Invert bytes using assembly}
- function ByteSwap(const a: integer): integer;
- implementation
- var
- ChunkClasses: TPngPointerList;
- {Table of CRCs of all 8-bit messages}
- crc_table: Array[0..255] of Cardinal;
- {Flag: has the table been computed? Initially false}
- crc_table_computed: Boolean;
- {Draw transparent image using transparent color}
- procedure DrawTransparentBitmap(dc: HDC; srcBits: Pointer;
- var srcHeader: TBitmapInfoHeader;
- srcBitmapInfo: pBitmapInfo; Rect: TRect; cTransparentColor: COLORREF);
- var
- cColor: COLORREF;
- bmAndBack, bmAndObject, bmAndMem: HBITMAP;
- bmBackOld, bmObjectOld, bmMemOld: HBITMAP;
- hdcMem, hdcBack, hdcObject, hdcTemp: HDC;
- ptSize, orgSize: TPOINT;
- OldBitmap, DrawBitmap: HBITMAP;
- begin
- hdcTemp := CreateCompatibleDC(dc);
- {Select the bitmap}
- DrawBitmap := CreateDIBitmap(dc, srcHeader, CBM_INIT, srcBits, srcBitmapInfo^,
- DIB_RGB_COLORS);
- OldBitmap := SelectObject(hdcTemp, DrawBitmap);
- {Get sizes}
- OrgSize.x := abs(srcHeader.biWidth);
- OrgSize.y := abs(srcHeader.biHeight);
- ptSize.x := Rect.Right - Rect.Left; // Get width of bitmap
- ptSize.y := Rect.Bottom - Rect.Top; // Get height of bitmap
- {Create some DCs to hold temporary data}
- hdcBack := CreateCompatibleDC(dc);
- hdcObject := CreateCompatibleDC(dc);
- hdcMem := CreateCompatibleDC(dc);
- // Create a bitmap for each DC. DCs are required for a number of
- // GDI functions.
- // Monochrome DCs
- bmAndBack := CreateBitmap(ptSize.x, ptSize.y, 1, 1, nil);
- bmAndObject := CreateBitmap(ptSize.x, ptSize.y, 1, 1, nil);
- bmAndMem := CreateCompatibleBitmap(dc, ptSize.x, ptSize.y);
- // Each DC must select a bitmap object to store pixel data.
- bmBackOld := SelectObject(hdcBack, bmAndBack);
- bmObjectOld := SelectObject(hdcObject, bmAndObject);
- bmMemOld := SelectObject(hdcMem, bmAndMem);
- // Set the background color of the source DC to the color.
- // contained in the parts of the bitmap that should be transparent
- cColor := SetBkColor(hdcTemp, cTransparentColor);
- // Create the object mask for the bitmap by performing a BitBlt
- // from the source bitmap to a monochrome bitmap.
- StretchBlt(hdcObject, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0,
- orgSize.x, orgSize.y, SRCCOPY);
- // Set the background color of the source DC back to the original
- // color.
- SetBkColor(hdcTemp, cColor);
- // Create the inverse of the object mask.
- BitBlt(hdcBack, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0,
- NOTSRCCOPY);
- // Copy the background of the main DC to the destination.
- BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, dc, Rect.Left, Rect.Top,
- SRCCOPY);
- // Mask out the places where the bitmap will be placed.
- BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, SRCAND);
- // Mask out the transparent colored pixels on the bitmap.
- // BitBlt(hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcBack, 0, 0, SRCAND);
- StretchBlt(hdcTemp, 0, 0, OrgSize.x, OrgSize.y, hdcBack, 0, 0,
- PtSize.x, PtSize.y, SRCAND);
- // XOR the bitmap with the background on the destination DC.
- StretchBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0,
- OrgSize.x, OrgSize.y, SRCPAINT);
- // Copy the destination to the screen.
- BitBlt(dc, Rect.Left, Rect.Top, ptSize.x, ptSize.y, hdcMem, 0, 0,
- SRCCOPY);
- // Delete the memory bitmaps.
- DeleteObject(SelectObject(hdcBack, bmBackOld));
- DeleteObject(SelectObject(hdcObject, bmObjectOld));
- DeleteObject(SelectObject(hdcMem, bmMemOld));
- DeleteObject(SelectObject(hdcTemp, OldBitmap));
- // Delete the memory DCs.
- DeleteDC(hdcMem);
- DeleteDC(hdcBack);
- DeleteDC(hdcObject);
- DeleteDC(hdcTemp);
- end;
- {Make the table for a fast CRC.}
- procedure make_crc_table;
- var
- c: Cardinal;
- n, k: Integer;
- begin
- {fill the crc table}
- for n := 0 to 255 do
- begin
- c := Cardinal(n);
- for k := 0 to 7 do
- begin
- if Boolean(c and 1) then
- c := $edb88320 xor (c shr 1)
- else
- c := c shr 1;
- end;
- crc_table[n] := c;
- end;
- {The table has already being computated}
- crc_table_computed := true;
- end;
- {Update a running CRC with the bytes buf[0..len-1]--the CRC
- should be initialized to all 1's, and the transmitted value
- is the 1's complement of the final running CRC (see the
- crc() routine below)).}
- function update_crc(crc: {$IFNDEF DelphiBuilder3Less}Cardinal{$ELSE}Integer
- {$ENDIF}; buf: pByteArray; len: Integer): Cardinal;
- var
- c: Cardinal;
- n: Integer;
- begin
- c := crc;
- {Create the crc table in case it has not being computed yet}
- if not crc_table_computed then make_crc_table;
- {Update}
- for n := 0 to len - 1 do
- c := crc_table[(c XOR buf^[n]) and $FF] XOR (c shr 8);
- {Returns}
- Result := c;
- end;
- {$IFNDEF UseDelphi}
- function FileExists(Filename: String): Boolean;
- var
- FindFile: THandle;
- FindData: TWin32FindData;
- begin
- FindFile := FindFirstFile(PChar(Filename), FindData);
- Result := FindFile <> INVALID_HANDLE_VALUE;
- if Result then Windows.FindClose(FindFile);
- end;
- {$ENDIF}
- {$IFNDEF UseDelphi}
- {Exception implementation}
- constructor Exception.Create(Msg: String);
- begin
- end;
- {$ENDIF}
- {Calculates the paeth predictor}
- function PaethPredictor(a, b, c: Byte): Byte;
- var
- pa, pb, pc: Integer;
- begin
- { a = left, b = above, c = upper left }
- pa := abs(b - c); { distances to a, b, c }
- pb := abs(a - c);
- pc := abs(a + b - c * 2);
- { return nearest of a, b, c, breaking ties in order a, b, c }
- if (pa <= pb) and (pa <= pc) then
- Result := a
- else
- if pb <= pc then
- Result := b
- else
- Result := c;
- end;
- {Invert bytes using assembly}
- function ByteSwap(const a: integer): integer;
- asm
- bswap eax
- end;
- function ByteSwap16(inp:word): word;
- asm
- bswap eax
- shr eax, 16
- end;
- {Calculates number of bytes for the number of pixels using the}
- {color mode in the paramenter}
- function BytesForPixels(const Pixels: Integer; const ColorType,
- BitDepth: Byte): Integer;
- begin
- case ColorType of
- {Palette and grayscale contains a single value, for palette}
- {an value of size 2^bitdepth pointing to the palette index}
- {and grayscale the value from 0 to 2^bitdepth with color intesity}
- COLOR_GRAYSCALE, COLOR_PALETTE:
- Result := (Pixels * BitDepth + 7) div 8;
- {RGB contains 3 values R, G, B with size 2^bitdepth each}
- COLOR_RGB:
- Result := (Pixels * BitDepth * 3) div 8;
- {Contains one value followed by alpha value booth size 2^bitdepth}
- COLOR_GRAYSCALEALPHA:
- Result := (Pixels * BitDepth * 2) div 8;
- {Contains four values size 2^bitdepth, Red, Green, Blue and alpha}
- COLOR_RGBALPHA:
- Result := (Pixels * BitDepth * 4) div 8;
- else
- Result := 0;
- end {case ColorType}
- end;
- type
- pChunkClassInfo = ^TChunkClassInfo;
- TChunkClassInfo = record
- ClassName: TChunkClass;
- end;
- {Register a chunk type}
- procedure RegisterChunk(ChunkClass: TChunkClass);
- var
- NewClass: pChunkClassInfo;
- begin
- {In case the list object has not being created yet}
- if ChunkClasses = nil then ChunkClasses := TPngPointerList.Create(nil);
- {Add this new class}
- new(NewClass);
- NewClass^.ClassName := ChunkClass;
- ChunkClasses.Add(NewClass);
- end;
- {Free chunk class list}
- procedure FreeChunkClassList;
- var
- i: Integer;
- begin
- if (ChunkClasses <> nil) then
- begin
- FOR i := 0 TO ChunkClasses.Count - 1 do
- Dispose(pChunkClassInfo(ChunkClasses.Item[i]));
- ChunkClasses.Free;
- end;
- end;
- {Registering of common chunk classes}
- procedure RegisterCommonChunks;
- begin
- {Important chunks}
- RegisterChunk(TChunkIEND);
- RegisterChunk(TChunkIHDR);
- RegisterChunk(TChunkIDAT);
- RegisterChunk(TChunkPLTE);
- RegisterChunk(TChunkgAMA);
- RegisterChunk(TChunktRNS);
- {Not so important chunks}
- RegisterChunk(TChunkpHYs);
- RegisterChunk(TChunktIME);
- RegisterChunk(TChunktEXt);
- RegisterChunk(TChunkzTXt);
- end;
- {Creates a new chunk of this class}
- function CreateClassChunk(Owner: TPngObject; Name: TChunkName): TChunk;
- var
- i : Integer;
- NewChunk: TChunkClass;
- begin
- {Looks for this chunk}
- NewChunk := TChunk; {In case there is no registered class for this}
- {Looks for this class in all registered chunks}
- if Assigned(ChunkClasses) then
- FOR i := 0 TO ChunkClasses.Count - 1 DO
- begin
- if pChunkClassInfo(ChunkClasses.Item[i])^.ClassName.GetName = Name then
- begin
- NewChunk := pChunkClassInfo(ChunkClasses.Item[i])^.ClassName;
- break;
- end;
- end;
- {Returns chunk class}
- Result := NewChunk.Create(Owner);
- Result.fName := Name;
- end;
- {ZLIB support}
- const
- ZLIBAllocate = High(Word);
- {Initializes ZLIB for decompression}
- function ZLIBInitInflate(Stream: TStream): TZStreamRec2;
- begin
- {Fill record}
- Fillchar(Result, SIZEOF(TZStreamRec2), #0);
- {Set internal record information}
- with Result do
- begin
- GetMem(Data, ZLIBAllocate);
- fStream := Stream;
- end;
- {Init decompression}
- InflateInit_(Result.zlib, zlib_version, SIZEOF(TZStreamRec));
- end;
- {Initializes ZLIB for compression}
- function ZLIBInitDeflate(Stream: TStream;
- Level: TCompressionlevel; Size: Cardinal): TZStreamRec2;
- begin
- {Fill record}
- Fillchar(Result, SIZEOF(TZStreamRec2), #0);
- {Set internal record information}
- with Result, ZLIB do
- begin
- GetMem(Data, Size);
- fStream := Stream;
- next_out := Data;
- avail_out := Size;
- end;
- {Inits compression}
- deflateInit_(Result.zlib, Level, zlib_version, sizeof(TZStreamRec));
- end;
- {Terminates ZLIB for compression}
- procedure ZLIBTerminateDeflate(var ZLIBStream: TZStreamRec2);
- begin
- {Terminates decompression}
- DeflateEnd(ZLIBStream.zlib);
- {Free internal record}
- FreeMem(ZLIBStream.Data, ZLIBAllocate);
- end;
- {Terminates ZLIB for decompression}
- procedure ZLIBTerminateInflate(var ZLIBStream: TZStreamRec2);
- begin
- {Terminates decompression}
- InflateEnd(ZLIBStream.zlib);
- {Free internal record}
- FreeMem(ZLIBStream.Data, ZLIBAllocate);
- end;
- {Decompresses ZLIB into a memory address}
- function DecompressZLIB(const Input: Pointer; InputSize: Integer;
- var Output: Pointer; var OutputSize: Integer;
- var ErrorOutput: String): Boolean;
- var
- StreamRec : TZStreamRec;
- Buffer : Array[Byte] of Byte;
- InflateRet: Integer;
- begin
- with StreamRec do
- begin
- {Initializes}
- Result := True;
- OutputSize := 0;
- {Prepares the data to decompress}
- FillChar(StreamRec, SizeOf(TZStreamRec), #0);
- InflateInit_(StreamRec, zlib_version, SIZEOF(TZStreamRec));
- next_in := Input;
- avail_in := InputSize;
- {Decodes data}
- repeat
- {In case it needs an output buffer}
- if (avail_out = 0) then
- begin
- next_out := @Buffer;
- avail_out := SizeOf(Buffer);
- end {if (avail_out = 0)};
- {Decompress and put in output}
- InflateRet := inflate(StreamRec, 0);
- if (InflateRet = Z_STREAM_END) or (InflateRet = 0) then
- begin
- {Reallocates output buffer}
- inc(OutputSize, total_out);
- if Output = nil then
- GetMem(Output, OutputSize) else ReallocMem(Output, OutputSize);
- {Copies the new data}
- CopyMemory(Ptr(Longint(Output) + OutputSize - total_out),
- @Buffer, total_out);
- end {if (InflateRet = Z_STREAM_END) or (InflateRet = 0)}
- {Now tests for errors}
- else if InflateRet < 0 then
- begin
- Result := False;
- ErrorOutput := StreamRec.msg;
- InflateEnd(StreamRec);
- Exit;
- end {if InflateRet < 0}
- until InflateRet = Z_STREAM_END;
- {Terminates decompression}
- InflateEnd(StreamRec);
- end {with StreamRec}
- end;
- {Compresses ZLIB into a memory address}
- function CompressZLIB(Input: Pointer; InputSize, CompressionLevel: Integer;
- var Output: Pointer; var OutputSize: Integer;
- var ErrorOutput: String): Boolean;
- var
- StreamRec : TZStreamRec;
- Buffer : Array[Byte] of Byte;
- DeflateRet: Integer;
- begin
- with StreamRec do
- begin
- Result := True; {By default returns TRUE as everything might have gone ok}
- OutputSize := 0; {Initialize}
- {Prepares the data to compress}
- FillChar(StreamRec, SizeOf(TZStreamRec), #0);
- DeflateInit_(StreamRec, CompressionLevel,zlib_version, SIZEOF(TZStreamRec));
- next_in := Input;
- avail_in := InputSize;
- while avail_in > 0 do
- begin
- {When it needs new buffer to stores the compressed data}
- if avail_out = 0 then
- begin
- {Restore buffer}
- next_out := @Buffer;
- avail_out := SizeOf(Buffer);
- end {if avail_out = 0};
- {Compresses}
- DeflateRet := deflate(StreamRec, Z_FINISH);
- if (DeflateRet = Z_STREAM_END) or (DeflateRet = 0) then
- begin
- {Updates the output memory}
- inc(OutputSize, total_out);
- if Output = nil then
- GetMem(Output, OutputSize) else ReallocMem(Output, OutputSize);
- {Copies the new data}
- CopyMemory(Ptr(Longint(Output) + OutputSize - total_out),
- @Buffer, total_out);
- end {if (InflateRet = Z_STREAM_END) or (InflateRet = 0)}
- {Now tests for errors}
- else if DeflateRet < 0 then
- begin
- Result := False;
- ErrorOutput := StreamRec.msg;
- DeflateEnd(StreamRec);
- Exit;
- end {if InflateRet < 0}
- end {while avail_in > 0};
- {Finishes compressing}
- DeflateEnd(StreamRec);
- end {with StreamRec}
- end;
- {TPngPointerList implementation}
- {Object being created}
- constructor TPngPointerList.Create(AOwner: TPNGObject);
- begin
- inherited Create; {Let ancestor work}
- {Holds owner}
- fOwner := AOwner;
- {Memory pointer not being used yet}
- fMemory := nil;
- {No items yet}
- fCount := 0;
- end;
- {Removes value from the list}
- function TPngPointerList.Remove(Value: Pointer): Pointer;
- var
- I, Position: Integer;
- begin
- {Gets item position}
- Position := -1;
- FOR I := 0 TO Count - 1 DO
- if Value = Item[I] then Position := I;
- {In case a match was found}
- if Position >= 0 then
- begin
- Result := Item[Position]; {Returns pointer}
- {Remove item and move memory}
- Dec(fCount);
- if Position < Integer(FCount) then
- System.Move(fMemory^[Position + 1], fMemory^[Position],
- (Integer(fCount) - Position) * SizeOf(Pointer));
- end {if Position >= 0} else Result := nil
- end;
- {Add a new value in the list}
- procedure TPngPointerList.Add(Value: Pointer);
- begin
- Count := Count + 1;
- Item[Count - 1] := Value;
- end;
- {Object being destroyed}
- destructor TPngPointerList.Destroy;
- begin
- {Release memory if needed}
- if fMemory <> nil then
- FreeMem(fMemory, fCount * sizeof(Pointer));
- {Free things}
- inherited Destroy;
- end;
- {Returns one item from the list}
- function TPngPointerList.GetItem(Index: Cardinal): Pointer;
- begin
- if (Index <= Count - 1) then
- Result := fMemory[Index]
- else
- {In case it's out of bounds}
- Result := nil;
- end;
- {Inserts a new item in the list}
- procedure TPngPointerList.Insert(Value: Pointer; Position: Cardinal);
- begin
- if (Position < Count) or (Count = 0) then
- begin
- {Increase item count}
- SetSize(Count + 1);
- {Move other pointers}
- if Position < Count then
- System.Move(fMemory^[Position], fMemory^[Position + 1],
- (Count - Position - 1) * SizeOf(Pointer));
- {Sets item}
- Item[Position] := Value;
- end;
- end;
- {Sets one item from the list}
- procedure TPngPointerList.SetItem(Index: Cardinal; const Value: Pointer);
- begin
- {If index is in bounds, set value}
- if (Index <= Count - 1) then
- fMemory[Index] := Value
- end;
- {This method resizes the list}
- procedure TPngPointerList.SetSize(const Size: Cardinal);
- begin
- {Sets the size}
- if (fMemory = nil) and (Size > 0) then
- GetMem(fMemory, Size * SIZEOF(Pointer))
- else
- if Size > 0 then {Only realloc if the new size is greater than 0}
- ReallocMem(fMemory, Size * SIZEOF(Pointer))
- else
- {In case user is resize to 0 items}
- begin
- FreeMem(fMemory);
- fMemory := nil;
- end;
- {Update count}
- fCount := Size;
- end;
- {TPNGList implementation}
- {Finds the first chunk of this class}
- function TPNGList.FindChunk(ChunkClass: TChunkClass): TChunk;
- var
- i: Integer;
- begin
- Result := nil;
- for i := 0 to Count - 1 do
- if Item[i] is ChunkClass then
- begin
- Result := Item[i];
- Break
- end
- end;
- {Removes an item}
- procedure TPNGList.RemoveChunk(Chunk: TChunk);
- begin
- Remove(Chunk);
- Chunk.Free
- end;
- {Add a new item}
- function TPNGList.Add(ChunkClass: TChunkClass): TChunk;
- var
- IHDR: TChunkIHDR;
- IEND: TChunkIEND;
- IDAT: TChunkIDAT;
- PLTE: TChunkPLTE;
- begin
- Result := nil; {Default result}
- {Adding these is not allowed}
- if ((ChunkClass = TChunkIHDR) or (ChunkClass = TChunkIDAT) or
- (ChunkClass = TChunkPLTE) or (ChunkClass = TChunkIEND)) and not
- (Owner.BeingCreated) then
- fOwner.RaiseError(EPngError, EPNGCannotAddChunkText)
- {Two of these is not allowed}
- else if ((ChunkClass = TChunkgAMA) and (ItemFromClass(TChunkgAMA) <> nil)) or
- ((ChunkClass = TChunktRNS) and (ItemFromClass(TChunktRNS) <> nil)) or
- ((ChunkClass = TChunkpHYs) and (ItemFromClass(TChunkpHYs) <> nil)) then
- fOwner.RaiseError(EPngError, EPNGCannotAddChunkText)
- {There must have an IEND and IHDR chunk}
- else if ((ItemFromClass(TChunkIEND) = nil) or
- (ItemFromClass(TChunkIHDR) = nil)) and not Owner.BeingCreated then
- fOwner.RaiseError(EPngError, EPNGCannotAddInvalidImageText)
- else
- begin
- {Get common chunks}
- IHDR := ItemFromClass(TChunkIHDR) as TChunkIHDR;
- IEND := ItemFromClass(TChunkIEND) as TChunkIEND;
- {Create new chunk}
- Result := ChunkClass.Create(Owner);
- {Add to the list}
- if (ChunkClass = TChunkgAMA) or (ChunkClass = TChunkpHYs) or
- (ChunkClass = TChunkPLTE) then
- Insert(Result, IHDR.Index + 1)
- {Header and end}
- else if (ChunkClass = TChunkIEND) then
- Insert(Result, Count)
- else if (ChunkClass = TChunkIHDR) then
- Insert(Result, 0)
- {Transparency chunk (fix by Ian Boyd)}
- else if (ChunkClass = TChunktRNS) then
- begin
- {Transparecy chunk must be after PLTE; before IDAT}
- IDAT := ItemFromClass(TChunkIDAT) as TChunkIDAT;
- PLTE := ItemFromClass(TChunkPLTE) as TChunkPLTE;
- if Assigned(PLTE) then
- Insert(Result, PLTE.Index + 1)
- else if Assigned(IDAT) then
- Insert(Result, IDAT.Index)
- else
- Insert(Result, IHDR.Index + 1)
- end
- else {All other chunks}
- Insert(Result, IEND.Index);
- end {if}
- end;
- {Returns item from the list}
- function TPNGList.GetItem(Index: Cardinal): TChunk;
- begin
- Result := inherited GetItem(Index);
- end;
- {Returns first item from the list using the class from parameter}
- function TPNGList.ItemFromClass(ChunkClass: TChunkClass): TChunk;
- var
- i: Integer;
- begin
- Result := nil; {Initial result}
- FOR i := 0 TO Count - 1 DO
- {Test if this item has the same class}
- if Item[i] is ChunkClass then
- begin
- {Returns this item and exit}
- Result := Item[i];
- break;
- end {if}
- end;
- {$IFNDEF UseDelphi}
- {TStream implementation}
- {Copies all from another stream}
- function TStream.CopyFrom(Source: TStream; Count: Cardinal): Cardinal;
- const
- MaxBytes = $f000;
- var
- Buffer: PChar;
- BufSize, N: Cardinal;
- begin
- {If count is zero, copy everything from Source}
- if Count = 0 then
- begin
- Source.Seek(0, soFromBeginning);
- Count := Source.Size;
- end;
- Result := Count; {Returns the number of bytes readed}
- {Allocates memory}
- if Count > MaxBytes then BufSize := MaxBytes else BufSize := Count;
- GetMem(Buffer, BufSize);
- {Copy memory}
- while Count > 0 do
- begin
- if Count > BufSize then N := BufSize else N := Count;
- Source.Read(Buffer^, N);
- Write(Buffer^, N);
- dec(Count, N);
- end;
- {Deallocates memory}
- FreeMem(Buffer, BufSize);
- end;
- {Set current stream position}
- procedure TStream.SetPosition(const Value: Longint);
- begin
- Seek(Value, soFromBeginning);
- end;
- {Returns position}
- function TStream.GetPosition: Longint;
- begin
- Result := Seek(0, soFromCurrent);
- end;
- {Returns stream size}
- function TStream.GetSize: Longint;
- var
- Pos: Cardinal;
- begin
- Pos := Seek(0, soFromCurrent);
- Result := Seek(0, soFromEnd);
- Seek(Pos, soFromBeginning);
- end;
- {TFileStream implementation}
- {Filestream object being created}
- constructor TFileStream.Create(Filename: String; Mode: TFileStreamModeSet);
- {Makes file mode}
- function OpenMode: DWORD;
- begin
- Result := 0;
- if fsmRead in Mode then Result := GENERIC_READ;
- if (fsmWrite in Mode) or (fsmCreate in Mode) then
- Result := Result OR GENERIC_WRITE;
- end;
- const
- IsCreate: Array[Boolean] of Integer = (OPEN_ALWAYS, CREATE_ALWAYS);
- begin
- {Call ancestor}
- inherited Create;
- {Create handle}
- fHandle := CreateFile(PChar(Filename), OpenMode, FILE_SHARE_READ or
- FILE_SHARE_WRITE, nil, IsCreate[fsmCreate in Mode], 0, 0);
- {Store mode}
- FileMode := Mode;
- end;
- {Filestream object being destroyed}
- destructor TFileStream.Destroy;
- begin
- {Terminates file and close}
- if FileMode = [fsmWrite] then
- SetEndOfFile(fHandle);
- CloseHandle(fHandle);
- {Call ancestor}
- inherited Destroy;
- end;
- {Writes data to the file}
- function TFileStream.Write(const Buffer; Count: Longint): Cardinal;
- begin
- if not WriteFile(fHandle, Buffer, Count, Result, nil) then
- Result := 0;
- end;
- {Reads data from the file}
- function TFileStream.Read(var Buffer; Count: Longint): Cardinal;
- begin
- if not ReadFile(fHandle, Buffer, Count, Result, nil) then
- Result := 0;
- end;
- {Seeks the file position}
- function TFileStream.Seek(Offset: Integer; Origin: Word): Longint;
- begin
- Result := SetFilePointer(fHandle, Offset, nil, Origin);
- end;
- {Sets the size of the file}
- procedure TFileStream.SetSize(const Value: Longint);
- begin
- Seek(Value, soFromBeginning);
- SetEndOfFile(fHandle);
- end;
- {TResourceStream implementation}
- {Creates the resource stream}
- constructor TResourceStream.Create(Instance: HInst; const ResName: String;
- ResType: PChar);
- var
- ResID: HRSRC;
- ResGlobal: HGlobal;
- begin
- {Obtains the resource ID}
- ResID := FindResource(hInstance, PChar(ResName), RT_RCDATA);
- if ResID = 0 then raise EPNGError.Create('');
- {Obtains memory and size}
- ResGlobal := LoadResource(hInstance, ResID);
- Size := SizeOfResource(hInstance, ResID);
- Memory := LockResource(ResGlobal);
- if (ResGlobal = 0) or (Memory = nil) then EPNGError.Create('');
- end;
- {Setting resource stream size is not supported}
- procedure TResourceStream.SetSize(const Value: Integer);
- begin
- end;
- {Writing into a resource stream is not supported}
- function TResourceStream.Write(const Buffer; Count: Integer): Cardinal;
- begin
- Result := 0;
- end;
- {Reads data from the stream}
- function TResourceStream.Read(var Buffer; Count: Integer): Cardinal;
- begin
- //Returns data
- CopyMemory(@Buffer, Ptr(Longint(Memory) + Position), Count);
- //Update position
- inc(Position, Count);
- //Returns
- Result := Count;
- end;
- {Seeks data}
- function TResourceStream.Seek(Offset: Integer; Origin: Word): Longint;
- begin
- {Move depending on the origin}
- case Origin of
- soFromBeginning: Position := Offset;
- soFromCurrent: inc(Position, Offset);
- soFromEnd: Position := Size + Offset;
- end;
- {Returns the current position}
- Result := Position;
- end;
- {$ENDIF}
- {TChunk implementation}
- {Resizes the data}
- procedure TChunk.ResizeData(const NewSize: Cardinal);
- begin
- fDataSize := NewSize;
- ReallocMem(fData, NewSize + 1);
- end;
- {Returns index from list}
- function TChunk.GetIndex: Integer;
- var
- i: Integer;
- begin
- Result := -1; {Avoiding warnings}
- {Searches in the list}
- FOR i := 0 TO Owner.Chunks.Count - 1 DO
- if Owner.Chunks.Item[i] = Self then
- begin
- {Found match}
- Result := i;
- exit;
- end {for i}
- end;
- {Returns pointer to the TChunkIHDR}
- function TChunk.GetHeader: TChunkIHDR;
- begin
- Result := Owner.Chunks.Item[0] as TChunkIHDR;
- end;
- {Assigns from another TChunk}
- procedure TChunk.Assign(Source: TChunk);
- begin
- {Copy properties}
- fName := Source.fName;
- {Set data size and realloc}
- ResizeData(Source.fDataSize);
- {Copy data (if there's any)}
- if fDataSize > 0 then CopyMemory(fData, Source.fData, fDataSize);
- end;
- {Chunk being created}
- constructor TChunk.Create(Owner: TPngObject);
- var
- ChunkName: String;
- begin
- {Ancestor create}
- inherited Create;
- {If it's a registered class, set the chunk name based on the class}
- {name. For instance, if the class name is TChunkgAMA, the GAMA part}
- {will become the chunk name}
- ChunkName := Copy(ClassName, Length('TChunk') + 1, Length(ClassName));
- if Length(ChunkName) = 4 then CopyMemory(@fName[0], @ChunkName[1], 4);
- {Initialize data holder}
- GetMem(fData, 1);
- fDataSize := 0;
- {Record owner}
- fOwner := Owner;
- end;
- {Chunk being destroyed}
- destructor TChunk.Destroy;
- begin
- {Free data holder}
- FreeMem(fData, fDataSize + 1);
- {Let ancestor destroy}
- inherited Destroy;
- end;
- {Returns the chunk name 1}
- function TChunk.GetChunkName: String;
- begin
- Result := fName
- end;
- {Returns the chunk name 2}
- class function TChunk.GetName: String;
- begin
- {For avoid writing GetName for each TChunk descendent, by default for}
- {classes which don't declare GetName, it will look for the class name}
- {to extract the chunk kind. Example, if the class name is TChunkIEND }
- {this method extracts and returns IEND}
- Result := Copy(ClassName, Length('TChunk') + 1, Length(ClassName));
- end;
- {Saves the data to the stream}
- function TChunk.SaveData(Stream: TStream): Boolean;
- var
- ChunkSize, ChunkCRC: Cardinal;
- begin
- {First, write the size for the following data in the chunk}
- ChunkSize := ByteSwap(DataSize);
- Stream.Write(ChunkSize, 4);
- {The chunk name}
- Stream.Write(fName, 4);
- {If there is data for the chunk, write it}
- if DataSize > 0 then Stream.Write(Data^, DataSize);
- {Calculates and write CRC}
- ChunkCRC := update_crc($ffffffff, @fName[0], 4);
- ChunkCRC := Byteswap(update_crc(ChunkCRC, Data, DataSize) xor $ffffffff);
- Stream.Write(ChunkCRC, 4);
- {Returns that everything went ok}
- Result := TRUE;
- end;
- {Saves the chunk to the stream}
- function TChunk.SaveToStream(Stream: TStream): Boolean;
- begin
- Result := SaveData(Stream)
- end;
- {Loads the chunk from a stream}
- function TChunk.LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean;
- var
- CheckCRC: Cardinal;
- {$IFDEF CheckCRC}RightCRC: Cardinal;{$ENDIF}
- begin
- {Copies data from source}
- ResizeData(Size);
- if Size > 0 then Stream.Read(fData^, Size);
- {Reads CRC}
- Stream.Read(CheckCRC, 4);
- CheckCrc := ByteSwap(CheckCRC);
- {Check if crc readed is valid}
- {$IFDEF CheckCRC}
- RightCRC := update_crc($ffffffff, @ChunkName[0], 4);
- RightCRC := update_crc(RightCRC, fData, Size) xor $ffffffff;
- Result := RightCRC = CheckCrc;
- {Handle CRC error}
- if not Result then
- begin
- {In case it coult not load chunk}
- Owner.RaiseError(EPngInvalidCRC, EPngInvalidCRCText);
- exit;
- end
- {$ELSE}Result := TRUE; {$ENDIF}
- end;
- {TChunktIME implementation}
- {Chunk being loaded from a stream}
- function TChunktIME.LoadFromStream(Stream: TStream;
- const ChunkName: TChunkName; Size: Integer): Boolean;
- begin
- {Let ancestor load the data}
- Result := inherited LoadFromStream(Stream, ChunkName, Size);
- if not Result or (Size <> 7) then exit; {Size must be 7}
- {Reads data}
- fYear := ((pByte(Longint(Data) )^) * 256)+ (pByte(Longint(Data) + 1)^);
- fMonth := pByte(Longint(Data) + 2)^;
- fDay := pByte(Longint(Data) + 3)^;
- fHour := pByte(Longint(Data) + 4)^;
- fMinute := pByte(Longint(Data) + 5)^;
- fSecond := pByte(Longint(Data) + 6)^;
- end;
- {Assigns from another TChunk}
- procedure TChunktIME.Assign(Source: TChunk);
- begin
- fYear := TChunktIME(Source).fYear;
- fMonth := TChunktIME(Source).fMonth;
- fDay := TChunktIME(Source).fDay;
- fHour := TChunktIME(Source).fHour;
- fMinute := TChunktIME(Source).fMinute;
- fSecond := TChunktIME(Source).fSecond;
- end;
- {Saving the chunk to a stream}
- function TChunktIME.SaveToStream(Stream: TStream): Boolean;
- begin
- {Update data}
- ResizeData(7); {Make sure the size is 7}
- pWord(Data)^ := ByteSwap16(Year);
- pByte(Longint(Data) + 2)^ := Month;
- pByte(Longint(Data) + 3)^ := Day;
- pByte(Longint(Data) + 4)^ := Hour;
- pByte(Longint(Data) + 5)^ := Minute;
- pByte(Longint(Data) + 6)^ := Second;
- {Let inherited save data}
- Result := inherited SaveToStream(Stream);
- end;
- {TChunkztXt implementation}
- {Loading the chunk from a stream}
- function TChunkzTXt.LoadFromStream(Stream: TStream;
- const ChunkName: TChunkName; Size: Integer): Boolean;
- var
- ErrorOutput: String;
- CompressionMethod: Byte;
- Output: Pointer;
- OutputSize: Integer;
- begin
- {Load data from stream and validate}
- Result := inherited LoadFromStream(Stream, ChunkName, Size);
- if not Result or (Size < 4) then exit;
- fKeyword := PChar(Data); {Get keyword and compression method bellow}
- if Longint(fKeyword) = 0 then
- CompressionMethod := pByte(Data)^
- else
- CompressionMethod := pByte(Longint(fKeyword) + Length(fKeyword))^;
- fText := '';
- {In case the compression is 0 (only one accepted by specs), reads it}
- if CompressionMethod = 0 then
- begin
- Output := nil;
- if DecompressZLIB(PChar(Longint(Data) + Length(fKeyword) + 2),
- Size - Length(fKeyword) - 2, Output, OutputSize, ErrorOutput) then
- begin
- SetLength(fText, OutputSize);
- CopyMemory(@fText[1], Output, OutputSize);
- end {if DecompressZLIB(...};
- FreeMem(Output);
- end {if CompressionMethod = 0}
- end;
- {Saving the chunk to a stream}
- function TChunkztXt.SaveToStream(Stream: TStream): Boolean;
- var
- Output: Pointer;
- OutputSize: Integer;
- ErrorOutput: String;
- begin
- Output := nil; {Initializes output}
- if fText = '' then fText := ' ';
- {Compresses the data}
- if CompressZLIB(@fText[1], Length(fText), Owner.CompressionLevel, Output,
- OutputSize, ErrorOutput) then
- begin
- {Size is length from keyword, plus a null character to divide}
- {plus the compression method, plus the length of the text (zlib compressed)}
- ResizeData(Length(fKeyword) + 2 + OutputSize);
- Fillchar(Data^, DataSize, #0);
- {Copies the keyword data}
- if Keyword <> '' then
- CopyMemory(Data, @fKeyword[1], Length(Keyword));
- {Compression method 0 (inflate/deflate)}
- pByte(Ptr(Longint(Data) + Length(Keyword) + 1))^ := 0;
- if OutputSize > 0 then
- CopyMemory(Ptr(Longint(Data) + Length(Keyword) + 2), Output, OutputSize);
- {Let ancestor calculate crc and save}
- Result := SaveData(Stream);
- end {if CompressZLIB(...} else Result := False;
- {Frees output}
- if Output <> nil then FreeMem(Output)
- end;
- {TChunktEXt implementation}
- {Assigns from another text chunk}
- procedure TChunktEXt.Assign(Source: TChunk);
- begin
- fKeyword := TChunktEXt(Source).fKeyword;
- fText := TChunktEXt(Source).fText;
- end;
- {Loading the chunk from a stream}
- function TChunktEXt.LoadFromStream(Stream: TStream;
- const ChunkName: TChunkName; Size: Integer): Boolean;
- begin
- {Load data from stream and validate}
- Result := inherited LoadFromStream(Stream, ChunkName, Size);
- if not Result or (Size < 3) then exit;
- {Get text}
- fKeyword := PChar(Data);
- SetLength(fText, Size - Length(fKeyword) - 1);
- CopyMemory(@fText[1], Ptr(Longint(Data) + Length(fKeyword) + 1),
- Length(fText));
- end;
- {Saving the chunk to a stream}
- function TChunktEXt.SaveToStream(Stream: TStream): Boolean;
- begin
- {Size is length from keyword, plus a null character to divide}
- {plus the length of the text}
- ResizeData(Length(fKeyword) + 1 + Length(fText));
- Fillchar(Data^, DataSize, #0);
- {Copy data}
- if Keyword <> '' then
- CopyMemory(Data, @fKeyword[1], Length(Keyword));
- if Text <> '' then
- CopyMemory(Ptr(Longint(Data) + Length(Keyword) + 1), @fText[1],
- Length(Text));
- {Let ancestor calculate crc and save}
- Result := inherited SaveToStream(Stream);
- end;
- {TChunkIHDR implementation}
- {Chunk being created}
- constructor TChunkIHDR.Create(Owner: TPngObject);
- begin
- {Prepare pointers}
- ImageHandle := 0;
- ImagePalette := 0;
- ImageDC := 0;
- {Call inherited}
- inherited Create(Owner);
- end;
- {Chunk being destroyed}
- destructor TChunkIHDR.Destroy;
- begin
- {Free memory}
- FreeImageData();
- {Calls TChunk destroy}
- inherited Destroy;
- end;
- {Copies the palette}
- procedure CopyPalette(Source: HPALETTE; Destination: HPALETTE);
- var
- PaletteSize: Integer;
- Entries: Array[Byte] of TPaletteEntry;
- begin
- PaletteSize := 0;
- if GetObject(Source, SizeOf(PaletteSize), @PaletteSize) = 0 then Exit;
- if PaletteSize = 0 then Exit;
- ResizePalette(Destination, PaletteSize);
- GetPaletteEntries(Source, 0, PaletteSize, Entries);
- SetPaletteEntries(Destination, 0, PaletteSize, Entries);
- end;
- {Assigns from another IHDR chunk}
- procedure TChunkIHDR.Assign(Source: TChunk);
- begin
- {Copy the IHDR data}
- if Source is TChunkIHDR then
- begin
- {Copy IHDR values}
- IHDRData := TChunkIHDR(Source).IHDRData;
- {Prepare to hold data by filling BitmapInfo structure and}
- {resizing ImageData and ImageAlpha memory allocations}
- PrepareImageData();
- {Copy image data}
- CopyMemory(ImageData, TChunkIHDR(Source).ImageData,
- BytesPerRow * Integer(Height));
- CopyMemory(ImageAlpha, TChunkIHDR(Source).ImageAlpha,
- Integer(Width) * Integer(Height));
- {Copy palette colors}
- BitmapInfo.bmiColors := TChunkIHDR(Source).BitmapInfo.bmiColors;
- {Copy palette also}
- CopyPalette(TChunkIHDR(Source).ImagePalette, ImagePalette);
- end
- else
- Owner.RaiseError(EPNGError, EPNGCannotAssignChunkText);
- end;
- {Release allocated image data}
- procedure TChunkIHDR.FreeImageData;
- begin
- {Free old image data}
- if ImageHandle <> 0 then DeleteObject(ImageHandle);
- if ImageDC <> 0 then DeleteDC(ImageDC);
- if ImageAlpha <> nil then FreeMem(ImageAlpha);
- if ImagePalette <> 0 then DeleteObject(ImagePalette);
- {$IFDEF Store16bits}
- if ExtraImageData <> nil then FreeMem(ExtraImageData);
- {$ENDIF}
- ImageHandle := 0; ImageDC := 0; ImageAlpha := nil; ImageData := nil;
- ImagePalette := 0; ExtraImageData := nil;
- end;
- {Chunk being loaded from a stream}
- function TChunkIHDR.LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean;
- begin
- {Let TChunk load it}
- Result := inherited LoadFromStream(Stream, ChunkName, Size);
- if not Result then Exit;
- {Now check values}
- {Note: It's recommended by png specification to make sure that the size}
- {must be 13 bytes to be valid, but some images with 14 bytes were found}
- {which could be loaded by internet explorer and other tools}
- if (fDataSize < SIZEOF(TIHdrData)) then
- begin
- {Ihdr must always have at least 13 bytes}
- Result := False;
- Owner.RaiseError(EPNGInvalidIHDR, EPNGInvalidIHDRText);
- exit;
- end;
- {Everything ok, reads IHDR}
- IHDRData := pIHDRData(fData)^;
- IHDRData.Width := ByteSwap(IHDRData.Width);
- IHDRData.Height := ByteSwap(IHDRData.Height);
- {The width and height must not be larger than 65535 pixels}
- if (IHDRData.Width > High(Word)) or (IHDRData.Height > High(Word)) then
- begin
- Result := False;
- Owner.RaiseError(EPNGSizeExceeds, EPNGSizeExceedsText);
- exit;
- end {if IHDRData.Width > High(Word)};
- {Compression method must be 0 (inflate/deflate)}
- if (IHDRData.CompressionMethod <> 0) then
- begin
- Result := False;
- Owner.RaiseError(EPNGUnknownCompression, EPNGUnknownCompressionText);
- exit;
- end;
- {Interlace must be either 0 (none) or 7 (adam7)}
- if (IHDRData.InterlaceMethod <> 0) and (IHDRData.InterlaceMethod <> 1) then
- begin
- Result := False;
- Owner.RaiseError(EPNGUnknownInterlace, EPNGUnknownInterlaceText);
- exit;
- end;
- {Updates owner properties}
- Owner.InterlaceMethod := TInterlaceMethod(IHDRData.InterlaceMethod);
- {Prepares data to hold image}
- PrepareImageData();
- end;
- {Saving the IHDR chunk to a stream}
- function TChunkIHDR.SaveToStream(Stream: TStream): Boolean;
- begin
- {Ignore 2 bits images}
- if BitDepth = 2 then BitDepth := 4;
- {It needs to do is update the data with the IHDR data}
- {structure containing the write values}
- ResizeData(SizeOf(TIHDRData));
- pIHDRData(fData)^ := IHDRData;
- {..byteswap 4 byte types}
- pIHDRData(fData)^.Width := ByteSwap(pIHDRData(fData)^.Width);
- pIHDRData(fData)^.Height := ByteSwap(pIHDRData(fData)^.Height);
- {..update interlace method}
- pIHDRData(fData)^.InterlaceMethod := Byte(Owner.InterlaceMethod);
- {..and then let the ancestor SaveToStream do the hard work}
- Result := inherited SaveToStream(Stream);
- end;
- {Creates a grayscale palette}
- function TChunkIHDR.CreateGrayscalePalette(Bitdepth: Integer): HPalette;
- var
- j: Integer;
- palEntries: TMaxLogPalette;
- begin
- {Prepares and fills the strucutre}
- if Bitdepth = 16 then Bitdepth := 8;
- fillchar(palEntries, sizeof(palEntries), 0);
- palEntries.palVersion := $300;
- palEntries.palNumEntries := 1 shl Bitdepth;
- {Fill it with grayscale colors}
- for j := 0 to palEntries.palNumEntries - 1 do
- begin
- palEntries.palPalEntry[j].peRed :=
- fOwner.GammaTable[MulDiv(j, 255, palEntries.palNumEntries - 1)];
- palEntries.palPalEntry[j].peGreen := palEntries.palPalEntry[j].peRed;
- palEntries.palPalEntry[j].peBlue := palEntries.palPalEntry[j].peRed;
- end;
- {Creates and returns the palette}
- Result := CreatePalette(pLogPalette(@palEntries)^);
- end;
- {Copies the palette to the Device Independent bitmap header}
- procedure TChunkIHDR.PaletteToDIB(Palette: HPalette);
- var
- j: Integer;
- palEntries: TMaxLogPalette;
- begin
- {Copy colors}
- Fillchar(palEntries, sizeof(palEntries), #0);
- BitmapInfo.bmiHeader.biClrUsed := GetPaletteEntries(Palette, 0, 256, palEntries.palPalEntry[0]);
- for j := 0 to BitmapInfo.bmiHeader.biClrUsed - 1 do
- begin
- BitmapInfo.bmiColors[j].rgbBlue := palEntries.palPalEntry[j].peBlue;
- BitmapInfo.bmiColors[j].rgbRed := palEntries.palPalEntry[j].peRed;
- BitmapInfo.bmiColors[j].rgbGreen := palEntries.palPalEntry[j].peGreen;
- end;
- end;
- {Resizes the image data to fill the color type, bit depth, }
- {width and height parameters}
- procedure TChunkIHDR.PrepareImageData();
- {Set the bitmap info}
- procedure SetInfo(const Bitdepth: Integer; const Palette: Boolean);
- begin
- {Copy if the bitmap contain palette entries}
- HasPalette := Palette;
- {Fill the strucutre}
- with BitmapInfo.bmiHeader do
- begin
- biSize := sizeof(TBitmapInfoHeader);
- biHeight := Height;
- biWidth := Width;
- biPlanes := 1;
- biBitCount := BitDepth;
- biCompression := BI_RGB;
- end {with BitmapInfo.bmiHeader}
- end;
- begin
- {Prepare bitmap info header}
- Fillchar(BitmapInfo, sizeof(TMaxBitmapInfo), #0);
- {Release old image data}
- FreeImageData();
- {Obtain number of bits for each pixel}
- case ColorType of
- COLOR_GRAYSCALE, COLOR_PALETTE, COLOR_GRAYSCALEALPHA:
- case BitDepth of
- {These are supported by windows}
- 1, 4, 8: SetInfo(BitDepth, TRUE);
- {2 bits for each pixel is not supported by windows bitmap}
- 2 : SetInfo(4, TRUE);
- {Also 16 bits (2 bytes) for each pixel is not supported}
- {and should be transormed into a 8 bit grayscale}
- 16 : SetInfo(8, TRUE);
- end;
- {Only 1 byte (8 bits) is supported}
- COLOR_RGB, COLOR_RGBALPHA: SetInfo(24, FALSE);
- end {case ColorType};
- {Number of bytes for each scanline}
- BytesPerRow := (((BitmapInfo.bmiHeader.biBitCount * Width) + 31)
- and not 31) div 8;
- {Build array for alpha information, if necessary}
- if (ColorType = COLOR_RGBALPHA) or (ColorType = COLOR_GRAYSCALEALPHA) then
- begin
- GetMem(ImageAlpha, Integer(Width) * Integer(Height));
- FillChar(ImageAlpha^, Integer(Width) * Integer(Height), #0);
- end;
- {Build array for extra byte information}
- {$IFDEF Store16bits}
- if (BitDepth = 16) then
- begin
- GetMem(ExtraImageData, BytesPerRow * Integer(Height));
- FillChar(ExtraImageData^, BytesPerRow * Integer(Height), #0);
- end;
- {$ENDIF}
- {Creates the image to hold the data, CreateDIBSection does a better}
- {work in allocating necessary memory}
- ImageDC := CreateCompatibleDC(0);
- {$IFDEF UseDelphi}Self.Owner.Canvas.Handle := ImageDC;{$ENDIF}
- {In case it is a palette image, create the palette}
- if HasPalette then
- begin
- {Create a standard palette}
- if ColorType = COLOR_PALETTE then
- ImagePalette := CreateHalfTonePalette(ImageDC)
- else
- ImagePalette := CreateGrayscalePalette(Bitdepth);
- ResizePalette(ImagePalette, 1 shl BitmapInfo.bmiHeader.biBitCount);
- BitmapInfo.bmiHeader.biClrUsed := 1 shl BitmapInfo.bmiHeader.biBitCount;
- SelectPalette(ImageDC, ImagePalette, False);
- RealizePalette(ImageDC);
- PaletteTODIB(ImagePalette);
- end;
- {Create the device independent bitmap}
- ImageHandle := CreateDIBSection(ImageDC, pBitmapInfo(@BitmapInfo)^,
- DIB_RGB_COLORS, ImageData, 0, 0);
- SelectObject(ImageDC, ImageHandle);
- {Build array and allocate bytes for each row}
- fillchar(ImageData^, BytesPerRow * Integer(Height), 0);
- end;
- {TChunktRNS implementation}
- {$IFNDEF UseDelphi}
- function CompareMem(P1, P2: pByte; const Size: Integer): Boolean;
- var i: Integer;
- begin
- Result := True;
- for i := 1 to Size do
- begin
- if P1^ <> P2^ then Result := False;
- inc(P1); inc(P2);
- end {for i}
- end;
- {$ENDIF}
- {Sets the transpararent color}
- procedure TChunktRNS.SetTransparentColor(const Value: ColorRef);
- var
- i: Byte;
- LookColor: TRGBQuad;
- begin
- {Clears the palette values}
- Fillchar(PaletteValues, SizeOf(PaletteValues), #0);
- {Sets that it uses bit transparency}
- fBitTransparency := True;
- {Depends on the color type}
- with Header do
- case ColorType of
- COLOR_GRAYSCALE:
- begin
- Self.ResizeData(2);
- pWord(@PaletteValues[0])^ := ByteSwap16(GetRValue(Value));
- end;
- COLOR_RGB:
- begin
- Self.ResizeData(6);
- pWord(@PaletteValues[0])^ := ByteSwap16(GetRValue(Value));
- pWord(@PaletteValues[2])^ := ByteSwap16(GetGValue(Value));
- pWord(@PaletteValues[4])^ := ByteSwap16(GetBValue(Value));
- end;
- COLOR_PALETTE:
- begin
- {Creates a RGBQuad to search for the color}
- LookColor.rgbRed := GetRValue(Value);
- LookColor.rgbGreen := GetGValue(Value);
- LookColor.rgbBlue := GetBValue(Value);
- {Look in the table for the entry}
- for i := 0 to BitmapInfo.bmiHeader.biClrUsed - 1 do
- if CompareMem(@BitmapInfo.bmiColors[i], @LookColor, 3) then
- Break;
- {Fill the transparency table}
- Fillchar(PaletteValues, i, 255);
- Self.ResizeData(i + 1)
- end
- end {case / with};
- end;
- {Returns the transparent color for the image}
- function TChunktRNS.GetTransparentColor: ColorRef;
- var
- PaletteChunk: TChunkPLTE;
- i: Integer;
- Value: Byte;
- begin
- Result := 0; {Default: Unknown transparent color}
- {Depends on the color type}
- with Header do
- case ColorType of
- COLOR_GRAYSCALE:
- begin
- Value := BitmapInfo.bmiColors[PaletteValues[1]].rgbRed;
- Result := RGB(Value, Value, Value);
- end;
- COLOR_RGB:
- Result := RGB(fOwner.GammaTable[PaletteValues[1]],
- fOwner.GammaTable[PaletteValues[3]],
- fOwner.GammaTable[PaletteValues[5]]);
- COLOR_PALETTE:
- begin
- {Obtains the palette chunk}
- PaletteChunk := Owner.Chunks.ItemFromClass(TChunkPLTE) as TChunkPLTE;
- {Looks for an entry with 0 transparency meaning that it is the}
- {full transparent entry}
- for i := 0 to Self.DataSize - 1 do
- if PaletteValues[i] = 0 then
- with PaletteChunk.GetPaletteItem(i) do
- begin
- Result := RGB(rgbRed, rgbGreen, rgbBlue);
- break
- end
- end {COLOR_PALETTE}
- end {case Header.ColorType};
- end;
- {Saving the chunk to a stream}
- function TChunktRNS.SaveToStream(Stream: TStream): Boolean;
- begin
- {Copy palette into data buffer}
- if DataSize <= 256 then
- CopyMemory(fData, @PaletteValues[0], DataSize);
- Result := inherited SaveToStream(Stream);
- end;
- {Assigns from another chunk}
- procedure TChunktRNS.Assign(Source: TChunk);
- begin
- CopyMemory(@PaletteValues[0], @TChunkTrns(Source).PaletteValues[0], 256);
- fBitTransparency := TChunkTrns(Source).fBitTransparency;
- inherited Assign(Source);
- end;
- {Loads the chunk from a stream}
- function TChunktRNS.LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean;
- var
- i, Differ255: Integer;
- begin
- {Let inherited load}
- Result := inherited LoadFromStream(Stream, ChunkName, Size);
- if not Result then Exit;
- {Make sure size is correct}
- if Size > 256 then Owner.RaiseError(EPNGInvalidPalette,
- EPNGInvalidPaletteText);
- {The unset items should have value 255}
- Fillchar(PaletteValues[0], 256, 255);
- {Copy the other values}
- CopyMemory(@PaletteValues[0], fData, Size);
- {Create the mask if needed}
- case Header.ColorType of
- {Mask for grayscale and RGB}
- COLOR_RGB, COLOR_GRAYSCALE: fBitTransparency := True;
- COLOR_PALETTE:
- begin
- Differ255 := 0; {Count the entries with a value different from 255}
- {Tests if it uses bit transparency}
- for i := 0 to Size - 1 do
- if PaletteValues[i] <> 255 then inc(Differ255);
- {If it has one value different from 255 it is a bit transparency}
- fBitTransparency := (Differ255 = 1);
- end {COLOR_PALETTE}
- end {case Header.ColorType};
- end;
- {Prepares the image palette}
- procedure TChunkIDAT.PreparePalette;
- var
- Entries: Word;
- j : Integer;
- palEntries: TMaxLogPalette;
- begin
- {In case the image uses grayscale, build a grayscale palette}
- with Header do
- if (ColorType = COLOR_GRAYSCALE) or (ColorType = COLOR_GRAYSCALEALPHA) then
- begin
- {Calculate total number of palette entries}
- Entries := (1 shl Byte(BitmapInfo.bmiHeader.biBitCount));
- Fillchar(palEntries, sizeof(palEntries), #0);
- palEntries.palVersion := $300;
- palEntries.palNumEntries := Entries;
- FOR j := 0 TO Entries - 1 DO
- with palEntries.palPalEntry[j] do
- begin
- {Calculate each palette entry}
- peRed := fOwner.GammaTable[MulDiv(j, 255, Entries - 1)];
- peGreen := peRed;
- peBlue := peRed;
- end {with BitmapInfo.bmiColors[j]};
- Owner.SetPalette(CreatePalette(pLogPalette(@palEntries)^));
- end {if ColorType = COLOR_GRAYSCALE..., with Header}
- end;
- {Reads from ZLIB}
- function TChunkIDAT.IDATZlibRead(var ZLIBStream: TZStreamRec2;
- Buffer: Pointer; Count: Integer; var EndPos: Integer;
- var crcfile: Cardinal): Integer;
- var
- ProcResult : Integer;
- IDATHeader : Array[0..3] of char;
- IDATCRC : Cardinal;
- begin
- {Uses internal record pointed by ZLIBStream to gather information}
- with ZLIBStream, ZLIBStream.zlib do
- begin
- {Set the buffer the zlib will read into}
- next_out := Buffer;
- avail_out := Count;
- {Decode until it reach the Count variable}
- while avail_out > 0 do
- begin
- {In case it needs more data and it's in the end of a IDAT chunk,}
- {it means that there are more IDAT chunks}
- if (fStream.Position = EndPos) and (avail_out > 0) and
- (avail_in = 0) then
- begin
- {End this chunk by reading and testing the crc value}
- fStream.Read(IDATCRC, 4);
- {$IFDEF CheckCRC}
- if crcfile xor $ffffffff <> Cardinal(ByteSwap(IDATCRC)) then
- begin
- Result := -1;
- Owner.RaiseError(EPNGInvalidCRC, EPNGInvalidCRCText);
- exit;
- end;
- {$ENDIF}
- {Start reading the next chunk}
- fStream.Read(EndPos, 4); {Reads next chunk size}
- fStream.Read(IDATHeader[0], 4); {Next chunk header}
- {It must be a IDAT chunk since image data is required and PNG}
- {specification says that multiple IDAT chunks must be consecutive}
- if IDATHeader <> 'IDAT' then
- begin
- Owner.RaiseError(EPNGMissingMultipleIDAT, EPNGMissingMultipleIDATText);
- result := -1;
- exit;
- end;
- {Calculate chunk name part of the crc}
- {$IFDEF CheckCRC}
- crcfile := update_crc($ffffffff, @IDATHeader[0], 4);
- {$ENDIF}
- EndPos := fStream.Position + ByteSwap(EndPos);
- end;
- {In case it needs compressed data to read from}
- if avail_in = 0 then
- begin
- {In case it's trying to read more than it is avaliable}
- if fStream.Position + ZLIBAllocate > EndPos then
- avail_in := fStream.Read(Data^, EndPos - fStream.Position)
- else
- avail_in := fStream.Read(Data^, ZLIBAllocate);
- {Update crc}
- {$IFDEF CheckCRC}
- crcfile := update_crc(crcfile, Data, avail_in);
- {$ENDIF}
- {In case there is no more compressed data to read from}
- if avail_in = 0 then
- begin
- Result := Count - avail_out;
- Exit;
- end;
- {Set next buffer to read and record current position}
- next_in := Data;
- end {if avail_in = 0};
- ProcResult := inflate(zlib, 0);
- {In case the result was not sucessfull}
- if (ProcResult < 0) then
- begin
- Result := -1;
- Owner.RaiseError(EPNGZLIBError,
- EPNGZLIBErrorText + zliberrors[procresult]);
- exit;
- end;
- end {while avail_out > 0};
- end {with};
- {If everything gone ok, it returns the count bytes}
- Result := Count;
- end;
- {TChunkIDAT implementation}
- const
- {Adam 7 interlacing values}
- RowStart: array[0..6] of Integer = (0, 0, 4, 0, 2, 0, 1);
- ColumnStart: array[0..6] of Integer = (0, 4, 0, 2, 0, 1, 0);
- RowIncrement: array[0..6] of Integer = (8, 8, 8, 4, 4, 2, 2);
- ColumnIncrement: array[0..6] of Integer = (8, 8, 4, 4, 2, 2, 1);
- {Copy interlaced images with 1 byte for R, G, B}
- procedure TChunkIDAT.CopyInterlacedRGB8(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- Col: Integer;
- begin
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- Dest := pChar(Longint(Dest) + Col * 3);
- repeat
- {Copy this row}
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest);
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Dest);
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) )^]; inc(Dest);
- {Move to next column}
- inc(Src, 3);
- inc(Dest, ColumnIncrement[Pass] * 3 - 3);
- inc(Col, ColumnIncrement[Pass]);
- until Col >= ImageWidth;
- end;
- {Copy interlaced images with 2 bytes for R, G, B}
- procedure TChunkIDAT.CopyInterlacedRGB16(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- Col: Integer;
- begin
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- Dest := pChar(Longint(Dest) + Col * 3);
- repeat
- {Copy this row}
- Byte(Dest^) := Owner.GammaTable[pByte(Longint(Src) + 4)^]; inc(Dest);
- Byte(Dest^) := Owner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest);
- Byte(Dest^) := Owner.GammaTable[pByte(Longint(Src) )^]; inc(Dest);
- {$IFDEF Store16bits}
- {Copy extra pixel values}
- Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 5)^]; inc(Extra);
- Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 3)^]; inc(Extra);
- Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Extra);
- {$ENDIF}
- {Move to next column}
- inc(Src, 6);
- inc(Dest, ColumnIncrement[Pass] * 3 - 3);
- inc(Col, ColumnIncrement[Pass]);
- until Col >= ImageWidth;
- end;
- {Copy ímages with palette using bit depths 1, 4 or 8}
- procedure TChunkIDAT.CopyInterlacedPalette148(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- const
- BitTable: Array[1..8] of Integer = ($1, $3, 0, $F, 0, 0, 0, $FF);
- StartBit: Array[1..8] of Integer = (7 , 0 , 0, 4, 0, 0, 0, 0);
- var
- CurBit, Col: Integer;
- Dest2: PChar;
- begin
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- repeat
- {Copy data}
- CurBit := StartBit[Header.BitDepth];
- repeat
- {Adjust pointer to pixel byte bounds}
- Dest2 := pChar(Longint(Dest) + (Header.BitDepth * Col) div 8);
- {Copy data}
- Byte(Dest2^) := Byte(Dest2^) or
- ( ((Byte(Src^) shr CurBit) and BitTable[Header.BitDepth])
- shl (StartBit[Header.BitDepth] - (Col * Header.BitDepth mod 8)));
- {Move to next column}
- inc(Col, ColumnIncrement[Pass]);
- {Will read next bits}
- dec(CurBit, Header.BitDepth);
- until CurBit < 0;
- {Move to next byte in source}
- inc(Src);
- until Col >= ImageWidth;
- end;
- {Copy ímages with palette using bit depth 2}
- procedure TChunkIDAT.CopyInterlacedPalette2(const Pass: Byte; Src, Dest,
- Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- CurBit, Col: Integer;
- Dest2: PChar;
- begin
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- repeat
- {Copy data}
- CurBit := 6;
- repeat
- {Adjust pointer to pixel byte bounds}
- Dest2 := pChar(Longint(Dest) + Col div 2);
- {Copy data}
- Byte(Dest2^) := Byte(Dest2^) or (((Byte(Src^) shr CurBit) and $3)
- shl (4 - (4 * Col) mod 8));
- {Move to next column}
- inc(Col, ColumnIncrement[Pass]);
- {Will read next bits}
- dec(CurBit, 2);
- until CurBit < 0;
- {Move to next byte in source}
- inc(Src);
- until Col >= ImageWidth;
- end;
- {Copy ímages with grayscale using bit depth 2}
- procedure TChunkIDAT.CopyInterlacedGray2(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- CurBit, Col: Integer;
- Dest2: PChar;
- begin
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- repeat
- {Copy data}
- CurBit := 6;
- repeat
- {Adjust pointer to pixel byte bounds}
- Dest2 := pChar(Longint(Dest) + Col div 2);
- {Copy data}
- Byte(Dest2^) := Byte(Dest2^) or ((((Byte(Src^) shr CurBit) shl 2) and $F)
- shl (4 - (Col*4) mod 8));
- {Move to next column}
- inc(Col, ColumnIncrement[Pass]);
- {Will read next bits}
- dec(CurBit, 2);
- until CurBit < 0;
- {Move to next byte in source}
- inc(Src);
- until Col >= ImageWidth;
- end;
- {Copy ímages with palette using 2 bytes for each pixel}
- procedure TChunkIDAT.CopyInterlacedGrayscale16(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- Col: Integer;
- begin
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- Dest := pChar(Longint(Dest) + Col);
- repeat
- {Copy this row}
- Dest^ := Src^; inc(Dest);
- {$IFDEF Store16bits}
- Extra^ := pChar(Longint(Src) + 1)^; inc(Extra);
- {$ENDIF}
- {Move to next column}
- inc(Src, 2);
- inc(Dest, ColumnIncrement[Pass] - 1);
- inc(Col, ColumnIncrement[Pass]);
- until Col >= ImageWidth;
- end;
- {Decodes interlaced RGB alpha with 1 byte for each sample}
- procedure TChunkIDAT.CopyInterlacedRGBAlpha8(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- Col: Integer;
- begin
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- Dest := pChar(Longint(Dest) + Col * 3);
- Trans := pChar(Longint(Trans) + Col);
- repeat
- {Copy this row and alpha value}
- Trans^ := pChar(Longint(Src) + 3)^;
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest);
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Dest);
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) )^]; inc(Dest);
- {Move to next column}
- inc(Src, 4);
- inc(Dest, ColumnIncrement[Pass] * 3 - 3);
- inc(Trans, ColumnIncrement[Pass]);
- inc(Col, ColumnIncrement[Pass]);
- until Col >= ImageWidth;
- end;
- {Decodes interlaced RGB alpha with 2 bytes for each sample}
- procedure TChunkIDAT.CopyInterlacedRGBAlpha16(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- Col: Integer;
- begin
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- Dest := pChar(Longint(Dest) + Col * 3);
- Trans := pChar(Longint(Trans) + Col);
- repeat
- {Copy this row and alpha value}
- Trans^ := pChar(Longint(Src) + 6)^;
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 4)^]; inc(Dest);
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest);
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) )^]; inc(Dest);
- {$IFDEF Store16bits}
- {Copy extra pixel values}
- Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 5)^]; inc(Extra);
- Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 3)^]; inc(Extra);
- Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Extra);
- {$ENDIF}
- {Move to next column}
- inc(Src, 8);
- inc(Dest, ColumnIncrement[Pass] * 3 - 3);
- inc(Trans, ColumnIncrement[Pass]);
- inc(Col, ColumnIncrement[Pass]);
- until Col >= ImageWidth;
- end;
- {Decodes 8 bit grayscale image followed by an alpha sample}
- procedure TChunkIDAT.CopyInterlacedGrayscaleAlpha8(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- Col: Integer;
- begin
- {Get first column, pointers to the data and enter in loop}
- Col := ColumnStart[Pass];
- Dest := pChar(Longint(Dest) + Col);
- Trans := pChar(Longint(Trans) + Col);
- repeat
- {Copy this grayscale value and alpha}
- Dest^ := Src^; inc(Src);
- Trans^ := Src^; inc(Src);
- {Move to next column}
- inc(Dest, ColumnIncrement[Pass]);
- inc(Trans, ColumnIncrement[Pass]);
- inc(Col, ColumnIncrement[Pass]);
- until Col >= ImageWidth;
- end;
- {Decodes 16 bit grayscale image followed by an alpha sample}
- procedure TChunkIDAT.CopyInterlacedGrayscaleAlpha16(const Pass: Byte;
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- Col: Integer;
- begin
- {Get first column, pointers to the data and enter in loop}
- Col := ColumnStart[Pass];
- Dest := pChar(Longint(Dest) + Col);
- Trans := pChar(Longint(Trans) + Col);
- repeat
- {$IFDEF Store16bits}
- Extra^ := pChar(Longint(Src) + 1)^; inc(Extra);
- {$ENDIF}
- {Copy this grayscale value and alpha, transforming 16 bits into 8}
- Dest^ := Src^; inc(Src, 2);
- Trans^ := Src^; inc(Src, 2);
- {Move to next column}
- inc(Dest, ColumnIncrement[Pass]);
- inc(Trans, ColumnIncrement[Pass]);
- inc(Col, ColumnIncrement[Pass]);
- until Col >= ImageWidth;
- end;
- {Decodes an interlaced image}
- procedure TChunkIDAT.DecodeInterlacedAdam7(Stream: TStream;
- var ZLIBStream: TZStreamRec2; const Size: Integer; var crcfile: Cardinal);
- var
- CurrentPass: Byte;
- PixelsThisRow: Integer;
- CurrentRow: Integer;
- Trans, Data{$IFDEF Store16bits}, Extra{$ENDIF}: pChar;
- CopyProc: procedure(const Pass: Byte; Src, Dest,
- Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar) of object;
- begin
- CopyProc := nil; {Initialize}
- {Determine method to copy the image data}
- case Header.ColorType of
- {R, G, B values for each pixel}
- COLOR_RGB:
- case Header.BitDepth of
- 8: CopyProc := CopyInterlacedRGB8;
- 16: CopyProc := CopyInterlacedRGB16;
- end {case Header.BitDepth};
- {Palette}
- COLOR_PALETTE, COLOR_GRAYSCALE:
- case Header.BitDepth of
- 1, 4, 8: CopyProc := CopyInterlacedPalette148;
- 2 : if Header.ColorType = COLOR_PALETTE then
- CopyProc := CopyInterlacedPalette2
- else
- CopyProc := CopyInterlacedGray2;
- 16 : CopyProc := CopyInterlacedGrayscale16;
- end;
- {RGB followed by alpha}
- COLOR_RGBALPHA:
- case Header.BitDepth of
- 8: CopyProc := CopyInterlacedRGBAlpha8;
- 16: CopyProc := CopyInterlacedRGBAlpha16;
- end;
- {Grayscale followed by alpha}
- COLOR_GRAYSCALEALPHA:
- case Header.BitDepth of
- 8: CopyProc := CopyInterlacedGrayscaleAlpha8;
- 16: CopyProc := CopyInterlacedGrayscaleAlpha16;
- end;
- end {case Header.ColorType};
- {Adam7 method has 7 passes to make the final image}
- FOR CurrentPass := 0 TO 6 DO
- begin
- {Calculates the number of pixels and bytes for this pass row}
- PixelsThisRow := (ImageWidth - ColumnStart[CurrentPass] +
- ColumnIncrement[CurrentPass] - 1) div ColumnIncrement[CurrentPass];
- Row_Bytes := BytesForPixels(PixelsThisRow, Header.ColorType,
- Header.BitDepth);
- {Clear buffer for this pass}
- ZeroMemory(Row_Buffer[not RowUsed], Row_Bytes);
- {Get current row index}
- CurrentRow := RowStart[CurrentPass];
- {Get a pointer to the current row image data}
- Data := Ptr(Longint(Header.ImageData) + Header.BytesPerRow *
- (ImageHeight - 1 - CurrentRow));
- Trans := Ptr(Longint(Header.ImageAlpha) + ImageWidth * CurrentRow);
- {$IFDEF Store16bits}
- Extra := Ptr(Longint(Header.ExtraImageData) + Header.BytesPerRow *
- (ImageHeight - 1 - CurrentRow));
- {$ENDIF}
- if Row_Bytes > 0 then {There must have bytes for this interlaced pass}
- while CurrentRow < ImageHeight do
- begin
- {Reads this line and filter}
- if IDATZlibRead(ZLIBStream, @Row_Buffer[RowUsed][0], Row_Bytes + 1,
- EndPos, CRCFile) = 0 then break;
- FilterRow;
- {Copy image data}
- CopyProc(CurrentPass, @Row_Buffer[RowUsed][1], Data, Trans
- {$IFDEF Store16bits}, Extra{$ENDIF});
- {Use the other RowBuffer item}
- RowUsed := not RowUsed;
- {Move to the next row}
- inc(CurrentRow, RowIncrement[CurrentPass]);
- {Move pointer to the next line}
- dec(Data, RowIncrement[CurrentPass] * Header.BytesPerRow);
- inc(Trans, RowIncrement[CurrentPass] * ImageWidth);
- {$IFDEF Store16bits}
- dec(Extra, RowIncrement[CurrentPass] * Header.BytesPerRow);
- {$ENDIF}
- end {while CurrentRow < ImageHeight};
- end {FOR CurrentPass};
- end;
- {Copy 8 bits RGB image}
- procedure TChunkIDAT.CopyNonInterlacedRGB8(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- I: Integer;
- begin
- FOR I := 1 TO ImageWidth DO
- begin
- {Copy pixel values}
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest);
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Dest);
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) )^]; inc(Dest);
- {Move to next pixel}
- inc(Src, 3);
- end {for I}
- end;
- {Copy 16 bits RGB image}
- procedure TChunkIDAT.CopyNonInterlacedRGB16(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- I: Integer;
- begin
- FOR I := 1 TO ImageWidth DO
- begin
- //Since windows does not supports 2 bytes for
- //each R, G, B value, the method will read only 1 byte from it
- {Copy pixel values}
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 4)^]; inc(Dest);
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest);
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) )^]; inc(Dest);
- {$IFDEF Store16bits}
- {Copy extra pixel values}
- Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 5)^]; inc(Extra);
- Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 3)^]; inc(Extra);
- Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Extra);
- {$ENDIF}
- {Move to next pixel}
- inc(Src, 6);
- end {for I}
- end;
- {Copy types using palettes (1, 4 or 8 bits per pixel)}
- procedure TChunkIDAT.CopyNonInterlacedPalette148(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- begin
- {It's simple as copying the data}
- CopyMemory(Dest, Src, Row_Bytes);
- end;
- {Copy grayscale types using 2 bits for each pixel}
- procedure TChunkIDAT.CopyNonInterlacedGray2(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- i: Integer;
- begin
- {2 bits is not supported, this routine will converted into 4 bits}
- FOR i := 1 TO Row_Bytes do
- begin
- Byte(Dest^) := ((Byte(Src^) shr 2) and $F) or ((Byte(Src^)) and $F0);
- inc(Dest);
- Byte(Dest^) := ((Byte(Src^) shl 2) and $F) or ((Byte(Src^) shl 4) and $F0);
- inc(Dest);
- inc(Src);
- end {FOR i}
- end;
- {Copy types using palette with 2 bits for each pixel}
- procedure TChunkIDAT.CopyNonInterlacedPalette2(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- i: Integer;
- begin
- {2 bits is not supported, this routine will converted into 4 bits}
- FOR i := 1 TO Row_Bytes do
- begin
- Byte(Dest^) := ((Byte(Src^) shr 4) and $3) or ((Byte(Src^) shr 2) and $30);
- inc(Dest);
- Byte(Dest^) := (Byte(Src^) and $3) or ((Byte(Src^) shl 2) and $30);
- inc(Dest);
- inc(Src);
- end {FOR i}
- end;
- {Copy grayscale images with 16 bits}
- procedure TChunkIDAT.CopyNonInterlacedGrayscale16(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- I: Integer;
- begin
- FOR I := 1 TO ImageWidth DO
- begin
- {Windows does not supports 16 bits for each pixel in grayscale}
- {mode, so reduce to 8}
- Dest^ := Src^; inc(Dest);
- {$IFDEF Store16bits}
- Extra^ := pChar(Longint(Src) + 1)^; inc(Extra);
- {$ENDIF}
- {Move to next pixel}
- inc(Src, 2);
- end {for I}
- end;
- {Copy 8 bits per sample RGB images followed by an alpha byte}
- procedure TChunkIDAT.CopyNonInterlacedRGBAlpha8(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- i: Integer;
- begin
- FOR I := 1 TO ImageWidth DO
- begin
- {Copy pixel values and transparency}
- Trans^ := pChar(Longint(Src) + 3)^;
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest);
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Dest);
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) )^]; inc(Dest);
- {Move to next pixel}
- inc(Src, 4); inc(Trans);
- end {for I}
- end;
- {Copy 16 bits RGB image with alpha using 2 bytes for each sample}
- procedure TChunkIDAT.CopyNonInterlacedRGBAlpha16(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- I: Integer;
- begin
- FOR I := 1 TO ImageWidth DO
- begin
- //Copy rgb and alpha values (transforming from 16 bits to 8 bits)
- {Copy pixel values}
- Trans^ := pChar(Longint(Src) + 6)^;
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 4)^]; inc(Dest);
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) + 2)^]; inc(Dest);
- Byte(Dest^) := fOwner.GammaTable[pByte(Longint(Src) )^]; inc(Dest);
- {$IFDEF Store16bits}
- {Copy extra pixel values}
- Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 5)^]; inc(Extra);
- Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 3)^]; inc(Extra);
- Byte(Extra^) := fOwner.GammaTable[pByte(Longint(Src) + 1)^]; inc(Extra);
- {$ENDIF}
- {Move to next pixel}
- inc(Src, 8); inc(Trans);
- end {for I}
- end;
- {Copy 8 bits per sample grayscale followed by alpha}
- procedure TChunkIDAT.CopyNonInterlacedGrayscaleAlpha8(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- I: Integer;
- begin
- FOR I := 1 TO ImageWidth DO
- begin
- {Copy alpha value and then gray value}
- Dest^ := Src^; inc(Src);
- Trans^ := Src^; inc(Src);
- inc(Dest); inc(Trans);
- end;
- end;
- {Copy 16 bits per sample grayscale followed by alpha}
- procedure TChunkIDAT.CopyNonInterlacedGrayscaleAlpha16(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar);
- var
- I: Integer;
- begin
- FOR I := 1 TO ImageWidth DO
- begin
- {Copy alpha value and then gray value}
- {$IFDEF Store16bits}
- Extra^ := pChar(Longint(Src) + 1)^; inc(Extra);
- {$ENDIF}
- Dest^ := Src^; inc(Src, 2);
- Trans^ := Src^; inc(Src, 2);
- inc(Dest); inc(Trans);
- end;
- end;
- {Decode non interlaced image}
- procedure TChunkIDAT.DecodeNonInterlaced(Stream: TStream;
- var ZLIBStream: TZStreamRec2; const Size: Integer; var crcfile: Cardinal);
- var
- j: Cardinal;
- Trans, Data{$IFDEF Store16bits}, Extra{$ENDIF}: pChar;
- CopyProc: procedure(
- Src, Dest, Trans{$IFDEF Store16bits}, Extra{$ENDIF}: pChar) of object;
- begin
- CopyProc := nil; {Initialize}
- {Determines the method to copy the image data}
- case Header.ColorType of
- {R, G, B values}
- COLOR_RGB:
- case Header.BitDepth of
- 8: CopyProc := CopyNonInterlacedRGB8;
- 16: CopyProc := CopyNonInterlacedRGB16;
- end;
- {Types using palettes}
- COLOR_PALETTE, COLOR_GRAYSCALE:
- case Header.BitDepth of
- 1, 4, 8: CopyProc := CopyNonInterlacedPalette148;
- 2 : if Header.ColorType = COLOR_PALETTE then
- CopyProc := CopyNonInterlacedPalette2
- else
- CopyProc := CopyNonInterlacedGray2;
- 16 : CopyProc := CopyNonInterlacedGrayscale16;
- end;
- {R, G, B followed by alpha}
- COLOR_RGBALPHA:
- case Header.BitDepth of
- 8 : CopyProc := CopyNonInterlacedRGBAlpha8;
- 16 : CopyProc := CopyNonInterlacedRGBAlpha16;
- end;
- {Grayscale followed by alpha}
- COLOR_GRAYSCALEALPHA:
- case Header.BitDepth of
- 8 : CopyProc := CopyNonInterlacedGrayscaleAlpha8;
- 16 : CopyProc := CopyNonInterlacedGrayscaleAlpha16;
- end;
- end;
- {Get the image data pointer}
- Longint(Data) := Longint(Header.ImageData) +
- Header.BytesPerRow * (ImageHeight - 1);
- Trans := Header.ImageAlpha;
- {$IFDEF Store16bits}
- Longint(Extra) := Longint(Header.ExtraImageData) +
- Header.BytesPerRow * (ImageHeight - 1);
- {$ENDIF}
- {Reads each line}
- FOR j := 0 to ImageHeight - 1 do
- begin
- {Read this line Row_Buffer[RowUsed][0] if the filter type for this line}
- if IDATZlibRead(ZLIBStream, @Row_Buffer[RowUsed][0], Row_Bytes + 1, EndPos,
- CRCFile) = 0 then break;
- {Filter the current row}
- FilterRow;
- {Copies non interlaced row to image}
- CopyProc(@Row_Buffer[RowUsed][1], Data, Trans{$IFDEF Store16bits}, Extra
- {$ENDIF});
- {Invert line used}
- RowUsed := not RowUsed;
- dec(Data, Header.BytesPerRow);
- {$IFDEF Store16bits}dec(Extra, Header.BytesPerRow);{$ENDIF}
- inc(Trans, ImageWidth);
- end {for I};
- end;
- {Filter the current line}
- procedure TChunkIDAT.FilterRow;
- var
- pp: Byte;
- vv, left, above, aboveleft: Integer;
- Col: Cardinal;
- begin
- {Test the filter}
- case Row_Buffer[RowUsed]^[0] of
- {No filtering for this line}
- FILTER_NONE: begin end;
- {AND 255 serves only to never let the result be larger than one byte}
- {Sub filter}
- FILTER_SUB:
- FOR Col := Offset + 1 to Row_Bytes DO
- Row_Buffer[RowUsed][Col] := (Row_Buffer[RowUsed][Col] +
- Row_Buffer[RowUsed][Col - Offset]) and 255;
- {Up filter}
- FILTER_UP:
- FOR Col := 1 to Row_Bytes DO
- Row_Buffer[RowUsed][Col] := (Row_Buffer[RowUsed][Col] +
- Row_Buffer[not RowUsed][Col]) and 255;
- {Average filter}
- FILTER_AVERAGE:
- FOR Col := 1 to Row_Bytes DO
- begin
- {Obtains up and left pixels}
- above := Row_Buffer[not RowUsed][Col];
- if col - 1 < Offset then
- left := 0
- else
- Left := Row_Buffer[RowUsed][Col - Offset];
- {Calculates}
- Row_Buffer[RowUsed][Col] := (Row_Buffer[RowUsed][Col] +
- (left + above) div 2) and 255;
- end;
- {Paeth filter}
- FILTER_PAETH:
- begin
- {Initialize}
- left := 0;
- aboveleft := 0;
- {Test each byte}
- FOR Col := 1 to Row_Bytes DO
- begin
- {Obtains above pixel}
- above := Row_Buffer[not RowUsed][Col];
- {Obtains left and top-left pixels}
- if (col - 1 >= offset) Then
- begin
- left := row_buffer[RowUsed][col - offset];
- aboveleft := row_buffer[not RowUsed][col - offset];
- end;
- {Obtains current pixel and paeth predictor}
- vv := row_buffer[RowUsed][Col];
- pp := PaethPredictor(left, above, aboveleft);
- {Calculates}
- Row_Buffer[RowUsed][Col] := (pp + vv) and $FF;
- end {for};
- end;
-
- end {case};
- end;
- {Reads the image data from the stream}
- function TChunkIDAT.LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean;
- var
- ZLIBStream: TZStreamRec2;
- CRCCheck,
- CRCFile : Cardinal;
- begin
- {Get pointer to the header chunk}
- Header := Owner.Chunks.Item[0] as TChunkIHDR;
- {Build palette if necessary}
- if Header.HasPalette then PreparePalette();
- {Copy image width and height}
- ImageWidth := Header.Width;
- ImageHeight := Header.Height;
- {Initialize to calculate CRC}
- {$IFDEF CheckCRC}
- CRCFile := update_crc($ffffffff, @ChunkName[0], 4);
- {$ENDIF}
- Owner.GetPixelInfo(Row_Bytes, Offset); {Obtain line information}
- ZLIBStream := ZLIBInitInflate(Stream); {Initializes decompression}
- {Calculate ending position for the current IDAT chunk}
- EndPos := Stream.Position + Size;
- {Allocate memory}
- GetMem(Row_Buffer[false], Row_Bytes + 1);
- GetMem(Row_Buffer[true], Row_Bytes + 1);
- ZeroMemory(Row_Buffer[false], Row_bytes + 1);
- {Set the variable to alternate the Row_Buffer item to use}
- RowUsed := TRUE;
- {Call special methods for the different interlace methods}
- case Owner.InterlaceMethod of
- imNone: DecodeNonInterlaced(stream, ZLIBStream, Size, crcfile);
- imAdam7: DecodeInterlacedAdam7(stream, ZLIBStream, size, crcfile);
- end;
- {Free memory}
- ZLIBTerminateInflate(ZLIBStream); {Terminates decompression}
- FreeMem(Row_Buffer[False], Row_Bytes + 1);
- FreeMem(Row_Buffer[True], Row_Bytes + 1);
- {Now checks CRC}
- Stream.Read(CRCCheck, 4);
- {$IFDEF CheckCRC}
- CRCFile := CRCFile xor $ffffffff;
- CRCCheck := ByteSwap(CRCCheck);
- Result := CRCCheck = CRCFile;
- {Handle CRC error}
- if not Result then
- begin
- {In case it coult not load chunk}
- Owner.RaiseError(EPngInvalidCRC, EPngInvalidCRCText);
- exit;
- end;
- {$ELSE}Result := TRUE; {$ENDIF}
- end;
- const
- IDATHeader: Array[0..3] of char = ('I', 'D', 'A', 'T');
- BUFFER = 5;
- {Saves the IDAT chunk to a stream}
- function TChunkIDAT.SaveToStream(Stream: TStream): Boolean;
- var
- ZLIBStream : TZStreamRec2;
- begin
- {Get pointer to the header chunk}
- Header := Owner.Chunks.Item[0] as TChunkIHDR;
- {Copy image width and height}
- ImageWidth := Header.Width;
- ImageHeight := Header.Height;
- Owner.GetPixelInfo(Row_Bytes, Offset); {Obtain line information}
- {Allocate memory}
- GetMem(Encode_Buffer[BUFFER], Row_Bytes);
- ZeroMemory(Encode_Buffer[BUFFER], Row_Bytes);
- {Allocate buffers for the filters selected}
- {Filter none will always be calculated to the other filters to work}
- GetMem(Encode_Buffer[FILTER_NONE], Row_Bytes);
- ZeroMemory(Encode_Buffer[FILTER_NONE], Row_Bytes);
- if pfSub in Owner.Filters then
- GetMem(Encode_Buffer[FILTER_SUB], Row_Bytes);
- if pfUp in Owner.Filters then
- GetMem(Encode_Buffer[FILTER_UP], Row_Bytes);
- if pfAverage in Owner.Filters then
- GetMem(Encode_Buffer[FILTER_AVERAGE], Row_Bytes);
- if pfPaeth in Owner.Filters then
- GetMem(Encode_Buffer[FILTER_PAETH], Row_Bytes);
- {Initialize ZLIB}
- ZLIBStream := ZLIBInitDeflate(Stream, Owner.fCompressionLevel,
- Owner.MaxIdatSize);
- {Write data depending on the interlace method}
- case Owner.InterlaceMethod of
- imNone: EncodeNonInterlaced(stream, ZLIBStream);
- imAdam7: EncodeInterlacedAdam7(stream, ZLIBStream);
- end;
- {Terminates ZLIB}
- ZLIBTerminateDeflate(ZLIBStream);
- {Release allocated memory}
- FreeMem(Encode_Buffer[BUFFER], Row_Bytes);
- FreeMem(Encode_Buffer[FILTER_NONE], Row_Bytes);
- if pfSub in Owner.Filters then
- FreeMem(Encode_Buffer[FILTER_SUB], Row_Bytes);
- if pfUp in Owner.Filters then
- FreeMem(Encode_Buffer[FILTER_UP], Row_Bytes);
- if pfAverage in Owner.Filters then
- FreeMem(Encode_Buffer[FILTER_AVERAGE], Row_Bytes);
- if pfPaeth in Owner.Filters then
- FreeMem(Encode_Buffer[FILTER_PAETH], Row_Bytes);
- {Everything went ok}
- Result := True;
- end;
- {Writes the IDAT using the settings}
- procedure WriteIDAT(Stream: TStream; Data: Pointer; const Length: Cardinal);
- var
- ChunkLen, CRC: Cardinal;
- begin
- {Writes IDAT header}
- ChunkLen := ByteSwap(Length);
- Stream.Write(ChunkLen, 4); {Chunk length}
- Stream.Write(IDATHeader[0], 4); {Idat header}
- CRC := update_crc($ffffffff, @IDATHeader[0], 4); {Crc part for header}
- {Writes IDAT data and calculates CRC for data}
- Stream.Write(Data^, Length);
- CRC := Byteswap(update_crc(CRC, Data, Length) xor $ffffffff);
- {Writes final CRC}
- Stream.Write(CRC, 4);
- end;
- {Compress and writes IDAT chunk data}
- procedure TChunkIDAT.IDATZlibWrite(var ZLIBStream: TZStreamRec2;
- Buffer: Pointer; const Length: Cardinal);
- begin
- with ZLIBStream, ZLIBStream.ZLIB do
- begin
- {Set data to be compressed}
- next_in := Buffer;
- avail_in := Length;
- {Compress all the data avaliable to compress}
- while avail_in > 0 do
- begin
- deflate(ZLIB, Z_NO_FLUSH);
- {The whole buffer was used, save data to stream and restore buffer}
- if avail_out = 0 then
- begin
- {Writes this IDAT chunk}
- WriteIDAT(fStream, Data, Owner.MaxIdatSize);
- {Restore buffer}
- next_out := Data;
- avail_out := Owner.MaxIdatSize;
- end {if avail_out = 0};
- end {while avail_in};
- end {with ZLIBStream, ZLIBStream.ZLIB}
- end;
- {Finishes compressing data to write IDAT chunk}
- procedure TChunkIDAT.FinishIDATZlib(var ZLIBStream: TZStreamRec2);
- begin
- with ZLIBStream, ZLIBStream.ZLIB do
- begin
- {Set data to be compressed}
- next_in := nil;
- avail_in := 0;
- while deflate(ZLIB,Z_FINISH) <> Z_STREAM_END do
- begin
- {Writes this IDAT chunk}
- WriteIDAT(fStream, Data, Owner.MaxIdatSize - avail_out);
- {Re-update buffer}
- next_out := Data;
- avail_out := Owner.MaxIdatSize;
- end;
- if avail_out < Owner.MaxIdatSize then
- {Writes final IDAT}
- WriteIDAT(fStream, Data, Owner.MaxIdatSize - avail_out);
- end {with ZLIBStream, ZLIBStream.ZLIB};
- end;
- {Copy memory to encode RGB image with 1 byte for each color sample}
- procedure TChunkIDAT.EncodeNonInterlacedRGB8(Src, Dest, Trans: pChar);
- var
- I: Integer;
- begin
- FOR I := 1 TO ImageWidth DO
- begin
- {Copy pixel values}
- Byte(Dest^) := fOwner.InverseGamma[pByte(Longint(Src) + 2)^]; inc(Dest);
- Byte(Dest^) := fOwner.InverseGamma[pByte(Longint(Src) + 1)^]; inc(Dest);
- Byte(Dest^) := fOwner.InverseGamma[pByte(Longint(Src) )^]; inc(Dest);
- {Move to next pixel}
- inc(Src, 3);
- end {for I}
- end;
- {Copy memory to encode RGB images with 16 bits for each color sample}
- procedure TChunkIDAT.EncodeNonInterlacedRGB16(Src, Dest, Trans: pChar);
- var
- I: Integer;
- begin
- FOR I := 1 TO ImageWidth DO
- begin
- //Now we copy from 1 byte for each sample stored to a 2 bytes (or 1 word)
- //for sample
- {Copy pixel values}
- pWORD(Dest)^ := fOwner.InverseGamma[pByte(Longint(Src) + 2)^]; inc(Dest, 2);
- pWORD(Dest)^ := fOwner.InverseGamma[pByte(Longint(Src) + 1)^]; inc(Dest, 2);
- pWORD(Dest)^ := fOwner.InverseGamma[pByte(Longint(Src) )^]; inc(Dest, 2);
- {Move to next pixel}
- inc(Src, 3);
- end {for I}
- end;
- {Copy memory to encode types using palettes (1, 4 or 8 bits per pixel)}
- procedure TChunkIDAT.EncodeNonInterlacedPalette148(Src, Dest, Trans: pChar);
- begin
- {It's simple as copying the data}
- CopyMemory(Dest, Src, Row_Bytes);
- end;
- {Copy memory to encode grayscale images with 2 bytes for each sample}
- procedure TChunkIDAT.EncodeNonInterlacedGrayscale16(Src, Dest, Trans: pChar);
- var
- I: Integer;
- begin
- FOR I := 1 TO ImageWidth DO
- begin
- //Now we copy from 1 byte for each sample stored to a 2 bytes (or 1 word)
- //for sample
- pWORD(Dest)^ := pByte(Longint(Src))^; inc(Dest, 2);
- {Move to next pixel}
- inc(Src);
- end {for I}
- end;
- {Encode images using RGB followed by an alpha value using 1 byte for each}
- procedure TChunkIDAT.EncodeNonInterlacedRGBAlpha8(Src, Dest, Trans: pChar);
- var
- i: Integer;
- begin
- {Copy the data to the destination, including data from Trans pointer}
- FOR i := 1 TO ImageWidth do
- begin
- Byte(Dest^) := Owner.InverseGamma[PByte(Longint(Src) + 2)^]; inc(Dest);
- Byte(Dest^) := Owner.InverseGamma[PByte(Longint(Src) + 1)^]; inc(Dest);
- Byte(Dest^) := Owner.InverseGamma[PByte(Longint(Src) )^]; inc(Dest);
- Dest^ := Trans^; inc(Dest);
- inc(Src, 3); inc(Trans);
- end {for i};
- end;
- {Encode images using RGB followed by an alpha value using 2 byte for each}
- procedure TChunkIDAT.EncodeNonInterlacedRGBAlpha16(Src, Dest, Trans: pChar);
- var
- i: Integer;
- begin
- {Copy the data to the destination, including data from Trans pointer}
- FOR i := 1 TO ImageWidth do
- begin
- pWord(Dest)^ := Owner.InverseGamma[PByte(Longint(Src) + 2)^]; inc(Dest, 2);
- pWord(Dest)^ := Owner.InverseGamma[PByte(Longint(Src) + 1)^]; inc(Dest, 2);
- pWord(Dest)^ := Owner.InverseGamma[PByte(Longint(Src) )^]; inc(Dest, 2);
- pWord(Dest)^ := PByte(Longint(Trans) )^; inc(Dest, 2);
- inc(Src, 3); inc(Trans);
- end {for i};
- end;
- {Encode grayscale images followed by an alpha value using 1 byte for each}
- procedure TChunkIDAT.EncodeNonInterlacedGrayscaleAlpha8(
- Src, Dest, Trans: pChar);
- var
- i: Integer;
- begin
- {Copy the data to the destination, including data from Trans pointer}
- FOR i := 1 TO ImageWidth do
- begin
- Dest^ := Src^; inc(Dest);
- Dest^ := Trans^; inc(Dest);
- inc(Src); inc(Trans);
- end {for i};
- end;
- {Encode grayscale images followed by an alpha value using 2 byte for each}
- procedure TChunkIDAT.EncodeNonInterlacedGrayscaleAlpha16(
- Src, Dest, Trans: pChar);
- var
- i: Integer;
- begin
- {Copy the data to the destination, including data from Trans pointer}
- FOR i := 1 TO ImageWidth do
- begin
- pWord(Dest)^ := pByte(Src)^; inc(Dest, 2);
- pWord(Dest)^ := pByte(Trans)^; inc(Dest, 2);
- inc(Src); inc(Trans);
- end {for i};
- end;
- {Encode non interlaced images}
- procedure TChunkIDAT.EncodeNonInterlaced(Stream: TStream;
- var ZLIBStream: TZStreamRec2);
- var
- {Current line}
- j: Cardinal;
- {Pointers to image data}
- Data, Trans: PChar;
- {Filter used for this line}
- Filter: Byte;
- {Method which will copy the data into the buffer}
- CopyProc: procedure(Src, Dest, Trans: pChar) of object;
- begin
- CopyProc := nil; {Initialize to avoid warnings}
- {Defines the method to copy the data to the buffer depending on}
- {the image parameters}
- case Header.ColorType of
- {R, G, B values}
- COLOR_RGB:
- case Header.BitDepth of
- 8: CopyProc := EncodeNonInterlacedRGB8;
- 16: CopyProc := EncodeNonInterlacedRGB16;
- end;
- {Palette and grayscale values}
- COLOR_GRAYSCALE, COLOR_PALETTE:
- case Header.BitDepth of
- 1, 4, 8: CopyProc := EncodeNonInterlacedPalette148;
- 16: CopyProc := EncodeNonInterlacedGrayscale16;
- end;
- {RGB with a following alpha value}
- COLOR_RGBALPHA:
- case Header.BitDepth of
- 8: CopyProc := EncodeNonInterlacedRGBAlpha8;
- 16: CopyProc := EncodeNonInterlacedRGBAlpha16;
- end;
- {Grayscale images followed by an alpha}
- COLOR_GRAYSCALEALPHA:
- case Header.BitDepth of
- 8: CopyProc := EncodeNonInterlacedGrayscaleAlpha8;
- 16: CopyProc := EncodeNonInterlacedGrayscaleAlpha16;
- end;
- end {case Header.ColorType};
- {Get the image data pointer}
- Longint(Data) := Longint(Header.ImageData) +
- Header.BytesPerRow * (ImageHeight - 1);
- Trans := Header.ImageAlpha;
- {Writes each line}
- FOR j := 0 to ImageHeight - 1 do
- begin
- {Copy data into buffer}
- CopyProc(Data, @Encode_Buffer[BUFFER][0], Trans);
- {Filter data}
- Filter := FilterToEncode;
- {Compress data}
- IDATZlibWrite(ZLIBStream, @Filter, 1);
- IDATZlibWrite(ZLIBStream, @Encode_Buffer[Filter][0], Row_Bytes);
- {Adjust pointers to the actual image data}
- dec(Data, Header.BytesPerRow);
- inc(Trans, ImageWidth);
- end;
- {Compress and finishes copying the remaining data}
- FinishIDATZlib(ZLIBStream);
- end;
- {Copy memory to encode interlaced images using RGB value with 1 byte for}
- {each color sample}
- procedure TChunkIDAT.EncodeInterlacedRGB8(const Pass: Byte;
- Src, Dest, Trans: pChar);
- var
- Col: Integer;
- begin
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- Src := pChar(Longint(Src) + Col * 3);
- repeat
- {Copy this row}
- Byte(Dest^) := fOwner.InverseGamma[pByte(Longint(Src) + 2)^]; inc(Dest);
- Byte(Dest^) := fOwner.InverseGamma[pByte(Longint(Src) + 1)^]; inc(Dest);
- Byte(Dest^) := fOwner.InverseGamma[pByte(Longint(Src) )^]; inc(Dest);
- {Move to next column}
- inc(Src, ColumnIncrement[Pass] * 3);
- inc(Col, ColumnIncrement[Pass]);
- until Col >= ImageWidth;
- end;
- {Copy memory to encode interlaced RGB images with 2 bytes each color sample}
- procedure TChunkIDAT.EncodeInterlacedRGB16(const Pass: Byte;
- Src, Dest, Trans: pChar);
- var
- Col: Integer;
- begin
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- Src := pChar(Longint(Src) + Col * 3);
- repeat
- {Copy this row}
- pWord(Dest)^ := Owner.InverseGamma[pByte(Longint(Src) + 2)^]; inc(Dest, 2);
- pWord(Dest)^ := Owner.InverseGamma[pByte(Longint(Src) + 1)^]; inc(Dest, 2);
- pWord(Dest)^ := Owner.InverseGamma[pByte(Longint(Src) )^]; inc(Dest, 2);
- {Move to next column}
- inc(Src, ColumnIncrement[Pass] * 3);
- inc(Col, ColumnIncrement[Pass]);
- until Col >= ImageWidth;
- end;
- {Copy memory to encode interlaced images using palettes using bit depths}
- {1, 4, 8 (each pixel in the image)}
- procedure TChunkIDAT.EncodeInterlacedPalette148(const Pass: Byte;
- Src, Dest, Trans: pChar);
- const
- BitTable: Array[1..8] of Integer = ($1, $3, 0, $F, 0, 0, 0, $FF);
- StartBit: Array[1..8] of Integer = (7 , 0 , 0, 4, 0, 0, 0, 0);
- var
- CurBit, Col: Integer;
- Src2: PChar;
- begin
- {Clean the line}
- fillchar(Dest^, Row_Bytes, #0);
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- with Header.BitmapInfo.bmiHeader do
- repeat
- {Copy data}
- CurBit := StartBit[biBitCount];
- repeat
- {Adjust pointer to pixel byte bounds}
- Src2 := pChar(Longint(Src) + (biBitCount * Col) div 8);
- {Copy data}
- Byte(Dest^) := Byte(Dest^) or
- (((Byte(Src2^) shr (StartBit[Header.BitDepth] - (biBitCount * Col)
- mod 8))) and (BitTable[biBitCount])) shl CurBit;
- {Move to next column}
- inc(Col, ColumnIncrement[Pass]);
- {Will read next bits}
- dec(CurBit, biBitCount);
- until CurBit < 0;
- {Move to next byte in source}
- inc(Dest);
- until Col >= ImageWidth;
- end;
- {Copy to encode interlaced grayscale images using 16 bits for each sample}
- procedure TChunkIDAT.EncodeInterlacedGrayscale16(const Pass: Byte;
- Src, Dest, Trans: pChar);
- var
- Col: Integer;
- begin
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- Src := pChar(Longint(Src) + Col);
- repeat
- {Copy this row}
- pWord(Dest)^ := Byte(Src^); inc(Dest, 2);
- {Move to next column}
- inc(Src, ColumnIncrement[Pass]);
- inc(Col, ColumnIncrement[Pass]);
- until Col >= ImageWidth;
- end;
- {Copy to encode interlaced rgb images followed by an alpha value, all using}
- {one byte for each sample}
- procedure TChunkIDAT.EncodeInterlacedRGBAlpha8(const Pass: Byte;
- Src, Dest, Trans: pChar);
- var
- Col: Integer;
- begin
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- Src := pChar(Longint(Src) + Col * 3);
- Trans := pChar(Longint(Trans) + Col);
- repeat
- {Copy this row}
- Byte(Dest^) := Owner.InverseGamma[pByte(Longint(Src) + 2)^]; inc(Dest);
- Byte(Dest^) := Owner.InverseGamma[pByte(Longint(Src) + 1)^]; inc(Dest);
- Byte(Dest^) := Owner.InverseGamma[pByte(Longint(Src) )^]; inc(Dest);
- Dest^ := Trans^; inc(Dest);
- {Move to next column}
- inc(Src, ColumnIncrement[Pass] * 3);
- inc(Trans, ColumnIncrement[Pass]);
- inc(Col, ColumnIncrement[Pass]);
- until Col >= ImageWidth;
- end;
- {Copy to encode interlaced rgb images followed by an alpha value, all using}
- {two byte for each sample}
- procedure TChunkIDAT.EncodeInterlacedRGBAlpha16(const Pass: Byte;
- Src, Dest, Trans: pChar);
- var
- Col: Integer;
- begin
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- Src := pChar(Longint(Src) + Col * 3);
- Trans := pChar(Longint(Trans) + Col);
- repeat
- {Copy this row}
- pWord(Dest)^ := pByte(Longint(Src) + 2)^; inc(Dest, 2);
- pWord(Dest)^ := pByte(Longint(Src) + 1)^; inc(Dest, 2);
- pWord(Dest)^ := pByte(Longint(Src) )^; inc(Dest, 2);
- pWord(Dest)^ := pByte(Trans)^; inc(Dest, 2);
- {Move to next column}
- inc(Src, ColumnIncrement[Pass] * 3);
- inc(Trans, ColumnIncrement[Pass]);
- inc(Col, ColumnIncrement[Pass]);
- until Col >= ImageWidth;
- end;
- {Copy to encode grayscale interlaced images followed by an alpha value, all}
- {using 1 byte for each sample}
- procedure TChunkIDAT.EncodeInterlacedGrayscaleAlpha8(const Pass: Byte;
- Src, Dest, Trans: pChar);
- var
- Col: Integer;
- begin
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- Src := pChar(Longint(Src) + Col);
- Trans := pChar(Longint(Trans) + Col);
- repeat
- {Copy this row}
- Dest^ := Src^; inc(Dest);
- Dest^ := Trans^; inc(Dest);
- {Move to next column}
- inc(Src, ColumnIncrement[Pass]);
- inc(Trans, ColumnIncrement[Pass]);
- inc(Col, ColumnIncrement[Pass]);
- until Col >= ImageWidth;
- end;
- {Copy to encode grayscale interlaced images followed by an alpha value, all}
- {using 2 bytes for each sample}
- procedure TChunkIDAT.EncodeInterlacedGrayscaleAlpha16(const Pass: Byte;
- Src, Dest, Trans: pChar);
- var
- Col: Integer;
- begin
- {Get first column and enter in loop}
- Col := ColumnStart[Pass];
- Src := pChar(Longint(Src) + Col);
- Trans := pChar(Longint(Trans) + Col);
- repeat
- {Copy this row}
- pWord(Dest)^ := pByte(Src)^; inc(Dest, 2);
- pWord(Dest)^ := pByte(Trans)^; inc(Dest, 2);
- {Move to next column}
- inc(Src, ColumnIncrement[Pass]);
- inc(Trans, ColumnIncrement[Pass]);
- inc(Col, ColumnIncrement[Pass]);
- until Col >= ImageWidth;
- end;
- {Encode interlaced images}
- procedure TChunkIDAT.EncodeInterlacedAdam7(Stream: TStream;
- var ZLIBStream: TZStreamRec2);
- var
- CurrentPass, Filter: Byte;
- PixelsThisRow: Integer;
- CurrentRow : Integer;
- Trans, Data: pChar;
- CopyProc: procedure(const Pass: Byte;
- Src, Dest, Trans: pChar) of object;
- begin
- CopyProc := nil; {Initialize to avoid warnings}
- {Defines the method to copy the data to the buffer depending on}
- {the image parameters}
- case Header.ColorType of
- {R, G, B values}
- COLOR_RGB:
- case Header.BitDepth of
- 8: CopyProc := EncodeInterlacedRGB8;
- 16: CopyProc := EncodeInterlacedRGB16;
- end;
- {Grayscale and palette}
- COLOR_PALETTE, COLOR_GRAYSCALE:
- case Header.BitDepth of
- 1, 4, 8: CopyProc := EncodeInterlacedPalette148;
- 16: CopyProc := EncodeInterlacedGrayscale16;
- end;
- {RGB followed by alpha}
- COLOR_RGBALPHA:
- case Header.BitDepth of
- 8: CopyProc := EncodeInterlacedRGBAlpha8;
- 16: CopyProc := EncodeInterlacedRGBAlpha16;
- end;
- COLOR_GRAYSCALEALPHA:
- {Grayscale followed by alpha}
- case Header.BitDepth of
- 8: CopyProc := EncodeInterlacedGrayscaleAlpha8;
- 16: CopyProc := EncodeInterlacedGrayscaleAlpha16;
- end;
- end {case Header.ColorType};
- {Compress the image using the seven passes for ADAM 7}
- FOR CurrentPass := 0 TO 6 DO
- begin
- {Calculates the number of pixels and bytes for this pass row}
- PixelsThisRow := (ImageWidth - ColumnStart[CurrentPass] +
- ColumnIncrement[CurrentPass] - 1) div ColumnIncrement[CurrentPass];
- Row_Bytes := BytesForPixels(PixelsThisRow, Header.ColorType,
- Header.BitDepth);
- ZeroMemory(Encode_Buffer[FILTER_NONE], Row_Bytes);
- {Get current row index}
- CurrentRow := RowStart[CurrentPass];
- {Get a pointer to the current row image data}
- Data := Ptr(Longint(Header.ImageData) + Header.BytesPerRow *
- (ImageHeight - 1 - CurrentRow));
- Trans := Ptr(Longint(Header.ImageAlpha) + ImageWidth * CurrentRow);
- {Process all the image rows}
- if Row_Bytes > 0 then
- while CurrentRow < ImageHeight do
- begin
- {Copy data into buffer}
- CopyProc(CurrentPass, Data, @Encode_Buffer[BUFFER][0], Trans);
- {Filter data}
- Filter := FilterToEncode;
- {Compress data}
- IDATZlibWrite(ZLIBStream, @Filter, 1);
- IDATZlibWrite(ZLIBStream, @Encode_Buffer[Filter][0], Row_Bytes);
- {Move to the next row}
- inc(CurrentRow, RowIncrement[CurrentPass]);
- {Move pointer to the next line}
- dec(Data, RowIncrement[CurrentPass] * Header.BytesPerRow);
- inc(Trans, RowIncrement[CurrentPass] * ImageWidth);
- end {while CurrentRow < ImageHeight}
- end {CurrentPass};
- {Compress and finishes copying the remaining data}
- FinishIDATZlib(ZLIBStream);
- end;
- {Filters the row to be encoded and returns the best filter}
- function TChunkIDAT.FilterToEncode: Byte;
- var
- Run, LongestRun, ii, jj: Cardinal;
- Last, Above, LastAbove: Byte;
- begin
- {Selecting more filters using the Filters property from TPngObject}
- {increases the chances to the file be much smaller, but decreases}
- {the performace}
- {This method will creates the same line data using the different}
- {filter methods and select the best}
- {Sub-filter}
- if pfSub in Owner.Filters then
- for ii := 0 to Row_Bytes - 1 do
- begin
- {There is no previous pixel when it's on the first pixel, so}
- {set last as zero when in the first}
- if (ii >= Offset) then
- last := Encode_Buffer[BUFFER]^[ii - Offset]
- else
- last := 0;
- Encode_Buffer[FILTER_SUB]^[ii] := Encode_Buffer[BUFFER]^[ii] - last;
- end;
- {Up filter}
- if pfUp in Owner.Filters then
- for ii := 0 to Row_Bytes - 1 do
- Encode_Buffer[FILTER_UP]^[ii] := Encode_Buffer[BUFFER]^[ii] -
- Encode_Buffer[FILTER_NONE]^[ii];
- {Average filter}
- if pfAverage in Owner.Filters then
- for ii := 0 to Row_Bytes - 1 do
- begin
- {Get the previous pixel, if the current pixel is the first, the}
- {previous is considered to be 0}
- if (ii >= Offset) then
- last := Encode_Buffer[BUFFER]^[ii - Offset]
- else
- last := 0;
- {Get the pixel above}
- above := Encode_Buffer[FILTER_NONE]^[ii];
- {Calculates formula to the average pixel}
- Encode_Buffer[FILTER_AVERAGE]^[ii] := Encode_Buffer[BUFFER]^[ii] -
- (above + last) div 2 ;
- end;
- {Paeth filter (the slower)}
- if pfPaeth in Owner.Filters then
- begin
- {Initialize}
- last := 0;
- lastabove := 0;
- for ii := 0 to Row_Bytes - 1 do
- begin
- {In case this pixel is not the first in the line obtains the}
- {previous one and the one above the previous}
- if (ii >= Offset) then
- begin
- last := Encode_Buffer[BUFFER]^[ii - Offset];
- lastabove := Encode_Buffer[FILTER_NONE]^[ii - Offset];
- end;
- {Obtains the pixel above}
- above := Encode_Buffer[FILTER_NONE]^[ii];
- {Calculate paeth filter for this byte}
- Encode_Buffer[FILTER_PAETH]^[ii] := Encode_Buffer[BUFFER]^[ii] -
- PaethPredictor(last, above, lastabove);
- end;
- end;
- {Now calculates the same line using no filter, which is necessary}
- {in order to have data to the filters when the next line comes}
- CopyMemory(@Encode_Buffer[FILTER_NONE]^[0],
- @Encode_Buffer[BUFFER]^[0], Row_Bytes);
- {If only filter none is selected in the filter list, we don't need}
- {to proceed and further}
- if (Owner.Filters = [pfNone]) or (Owner.Filters = []) then
- begin
- Result := FILTER_NONE;
- exit;
- end {if (Owner.Filters = [pfNone...};
- {Check which filter is the best by checking which has the larger}
- {sequence of the same byte, since they are best compressed}
- LongestRun := 0; Result := FILTER_NONE;
- for ii := FILTER_NONE TO FILTER_PAETH do
- {Check if this filter was selected}
- if TFilter(ii) in Owner.Filters then
- begin
- Run := 0;
- {Check if it's the only filter}
- if Owner.Filters = [TFilter(ii)] then
- begin
- Result := ii;
- exit;
- end;
- {Check using a sequence of four bytes}
- for jj := 2 to Row_Bytes - 1 do
- if (Encode_Buffer[ii]^[jj] = Encode_Buffer [ii]^[jj-1]) or
- (Encode_Buffer[ii]^[jj] = Encode_Buffer [ii]^[jj-2]) then
- inc(Run); {Count the number of sequences}
- {Check if this one is the best so far}
- if (Run > LongestRun) then
- begin
- Result := ii;
- LongestRun := Run;
- end {if (Run > LongestRun)};
- end {if TFilter(ii) in Owner.Filters};
- end;
- {TChunkPLTE implementation}
- {Returns an item in the palette}
- function TChunkPLTE.GetPaletteItem(Index: Byte): TRGBQuad;
- begin
- {Test if item is valid, if not raise error}
- if Index > Count - 1 then
- Owner.RaiseError(EPNGError, EPNGUnknownPalEntryText)
- else
- {Returns the item}
- Result := Header.BitmapInfo.bmiColors[Index];
- end;
- {Loads the palette chunk from a stream}
- function TChunkPLTE.LoadFromStream(Stream: TStream;
- const ChunkName: TChunkName; Size: Integer): Boolean;
- type
- pPalEntry = ^PalEntry;
- PalEntry = record
- r, g, b: Byte;
- end;
- var
- j : Integer; {For the FOR}
- PalColor : pPalEntry;
- palEntries: TMaxLogPalette;
- begin
- {Let ancestor load data and check CRC}
- Result := inherited LoadFromStream(Stream, ChunkName, Size);
- if not Result then exit;
- {This chunk must be divisible by 3 in order to be valid}
- if (Size mod 3 <> 0) or (Size div 3 > 256) then
- begin
- {Raise error}
- Result := FALSE;
- Owner.RaiseError(EPNGInvalidPalette, EPNGInvalidPaletteText);
- exit;
- end {if Size mod 3 <> 0};
- {Fill array with the palette entries}
- fCount := Size div 3;
- Fillchar(palEntries, sizeof(palEntries), #0);
- palEntries.palVersion := $300;
- palEntries.palNumEntries := fCount;
- PalColor := Data;
- FOR j := 0 TO fCount - 1 DO
- with palEntries.palPalEntry[j] do
- begin
- peRed := Owner.GammaTable[PalColor.r];
- peGreen := Owner.GammaTable[PalColor.g];
- peBlue := Owner.GammaTable[PalColor.b];
- peFlags := 0;
- {Move to next palette entry}
- inc(PalColor);
- end;
- Owner.SetPalette(CreatePalette(pLogPalette(@palEntries)^));
- end;
- {Saves the PLTE chunk to a stream}
- function TChunkPLTE.SaveToStream(Stream: TStream): Boolean;
- var
- J: Integer;
- DataPtr: pByte;
- BitmapInfo: TMAXBITMAPINFO;
- palEntries: TMaxLogPalette;
- begin
- {Adjust size to hold all the palette items}
- if fCount = 0 then fCount := Header.BitmapInfo.bmiHeader.biClrUsed;
- ResizeData(fCount * 3);
- {Get all the palette entries}
- fillchar(palEntries, sizeof(palEntries), #0);
- GetPaletteEntries(Header.ImagePalette, 0, 256, palEntries.palPalEntry[0]);
- {Copy pointer to data}
- DataPtr := fData;
- {Copy palette items}
- BitmapInfo := Header.BitmapInfo;
- FOR j := 0 TO fCount - 1 DO
- with palEntries.palPalEntry[j] do
- begin
- DataPtr^ := Owner.InverseGamma[peRed]; inc(DataPtr);
- DataPtr^ := Owner.InverseGamma[peGreen]; inc(DataPtr);
- DataPtr^ := Owner.InverseGamma[peBlue]; inc(DataPtr);
- end {with BitmapInfo};
- {Let ancestor do the rest of the work}
- Result := inherited SaveToStream(Stream);
- end;
- {Assigns from another PLTE chunk}
- procedure TChunkPLTE.Assign(Source: TChunk);
- begin
- {Copy the number of palette items}
- if Source is TChunkPLTE then
- fCount := TChunkPLTE(Source).fCount
- else
- Owner.RaiseError(EPNGError, EPNGCannotAssignChunkText);
- end;
- {TChunkgAMA implementation}
- {Assigns from another chunk}
- procedure TChunkgAMA.Assign(Source: TChunk);
- begin
- {Copy the gamma value}
- if Source is TChunkgAMA then
- Gamma := TChunkgAMA(Source).Gamma
- else
- Owner.RaiseError(EPNGError, EPNGCannotAssignChunkText);
- end;
- {Gamma chunk being created}
- constructor TChunkgAMA.Create(Owner: TPngObject);
- begin
- {Call ancestor}
- inherited Create(Owner);
- Gamma := 1; {Initial value}
- end;
- {Returns gamma value}
- function TChunkgAMA.GetValue: Cardinal;
- begin
- {Make sure that the size is four bytes}
- if DataSize <> 4 then
- begin
- {Adjust size and returns 1}
- ResizeData(4);
- Result := 1;
- end
- {If it's right, read the value}
- else Result := Cardinal(ByteSwap(pCardinal(Data)^))
- end;
- function Power(Base, Exponent: Extended): Extended;
- begin
- if Exponent = 0.0 then
- Result := 1.0 {Math rule}
- else if (Base = 0) or (Exponent = 0) then Result := 0
- else
- Result := Exp(Exponent * Ln(Base));
- end;
- {Loading the chunk from a stream}
- function TChunkgAMA.LoadFromStream(Stream: TStream;
- const ChunkName: TChunkName; Size: Integer): Boolean;
- var
- i: Integer;
- Value: Cardinal;
- begin
- {Call ancestor and test if it went ok}
- Result := inherited LoadFromStream(Stream, ChunkName, Size);
- if not Result then exit;
- Value := Gamma;
- {Build gamma table and inverse table for saving}
- if Value <> 0 then
- with Owner do
- FOR i := 0 TO 255 DO
- begin
- GammaTable[I] := Round(Power((I / 255), 1 /
- (Value / 100000 * 2.2)) * 255);
- InverseGamma[Round(Power((I / 255), 1 /
- (Value / 100000 * 2.2)) * 255)] := I;
- end
- end;
- {Sets the gamma value}
- procedure TChunkgAMA.SetValue(const Value: Cardinal);
- begin
- {Make sure that the size is four bytes}
- if DataSize <> 4 then ResizeData(4);
- {If it's right, set the value}
- pCardinal(Data)^ := ByteSwap(Value);
- end;
- {TPngObject implementation}
- {Assigns from another object}
- procedure TPngObject.Assign(Source: TPersistent);
- begin
- {Being cleared}
- if Source = nil then
- ClearChunks
- {Assigns contents from another TPNGObject}
- else if Source is TPNGObject then
- AssignPNG(Source as TPNGObject)
- {Copy contents from a TBitmap}
- {$IFDEF UseDelphi}else if Source is TBitmap then
- with Source as TBitmap do
- AssignHandle(Handle, Transparent,
- ColorToRGB(TransparentColor)){$ENDIF}
- {Unknown source, let ancestor deal with it}
- else
- inherited;
- end;
- {Clear all the chunks in the list}
- procedure TPngObject.ClearChunks;
- var
- i: Integer;
- begin
- {Initialize gamma}
- InitializeGamma();
- {Free all the objects and memory (0 chunks Bug fixed by Noel Sharpe)}
- for i := 0 TO Integer(Chunks.Count) - 1 do
- TChunk(Chunks.Item[i]).Free;
- Chunks.Count := 0;
- end;
- {Portable Network Graphics object being created as a blank image}
- constructor TPNGObject.CreateBlank(ColorType, BitDepth: Cardinal;
- cx, cy: Integer);
- var NewIHDR: TChunkIHDR;
- begin
- {Calls creator}
- Create;
- {Checks if the parameters are ok}
- if not (ColorType in [COLOR_GRAYSCALE, COLOR_RGB, COLOR_PALETTE,
- COLOR_GRAYSCALEALPHA, COLOR_RGBALPHA]) or not (BitDepth in
- [1,2,4,8, 16]) or ((ColorType = COLOR_PALETTE) and (BitDepth = 16)) or
- ((ColorType = COLOR_RGB) and (BitDepth < 8)) then
- begin
- RaiseError(EPNGInvalidSpec, EInvalidSpec);
- exit;
- end;
- if Bitdepth = 2 then Bitdepth := 4;
- {Add the basis chunks}
- InitializeGamma;
- BeingCreated := True;
- Chunks.Add(TChunkIEND);
- NewIHDR := Chunks.Add(TChunkIHDR) as TChunkIHDR;
- NewIHDR.IHDRData.ColorType := ColorType;
- NewIHDR.IHDRData.BitDepth := BitDepth;
- NewIHDR.IHDRData.Width := cx;
- NewIHDR.IHDRData.Height := cy;
- NewIHDR.PrepareImageData;
- if NewIHDR.HasPalette then
- TChunkPLTE(Chunks.Add(TChunkPLTE)).fCount := 1 shl BitDepth;
- Chunks.Add(TChunkIDAT);
- BeingCreated := False;
- end;
- {Portable Network Graphics object being created}
- constructor TPngObject.Create;
- begin
- {Let it be created}
- inherited Create;
- {Initial properties}
- {$IFDEF UseDelphi}fCanvas := TCanvas.Create;{$ENDIF}
- fFilters := [pfSub];
- fCompressionLevel := 7;
- fInterlaceMethod := imNone;
- fMaxIdatSize := High(Word);
- {Create chunklist object}
- fChunkList := TPngList.Create(Self);
- end;
- {Portable Network Graphics object being destroyed}
- destructor TPngObject.Destroy;
- begin
- {Free object list}
- ClearChunks;
- fChunkList.Free;
- {$IFDEF UseDelphi}if fCanvas <> nil then
- fCanvas.Free;{$ENDIF}
- {Call ancestor destroy}
- inherited Destroy;
- end;
- {Returns linesize and byte offset for pixels}
- procedure TPngObject.GetPixelInfo(var LineSize, Offset: Cardinal);
- begin
- {There must be an Header chunk to calculate size}
- if HeaderPresent then
- begin
- {Calculate number of bytes for each line}
- LineSize := BytesForPixels(Header.Width, Header.ColorType, Header.BitDepth);
- {Calculates byte offset}
- Case Header.ColorType of
- {Grayscale}
- COLOR_GRAYSCALE:
- If Header.BitDepth = 16 Then
- Offset := 2
- Else
- Offset := 1 ;
- {It always smaller or equal one byte, so it occupes one byte}
- COLOR_PALETTE:
- offset := 1;
- {It might be 3 or 6 bytes}
- COLOR_RGB:
- offset := 3 * Header.BitDepth Div 8;
- {It might be 2 or 4 bytes}
- COLOR_GRAYSCALEALPHA:
- offset := 2 * Header.BitDepth Div 8;
- {4 or 8 bytes}
- COLOR_RGBALPHA:
- offset := 4 * Header.BitDepth Div 8;
- else
- Offset := 0;
- End ;
- end
- else
- begin
- {In case if there isn't any Header chunk}
- Offset := 0;
- LineSize := 0;
- end;
- end;
- {Returns image height}
- function TPngObject.GetHeight: Integer;
- begin
- {There must be a Header chunk to get the size, otherwise returns 0}
- if HeaderPresent then
- Result := TChunkIHDR(Chunks.Item[0]).Height
- else Result := 0;
- end;
- {Returns image width}
- function TPngObject.GetWidth: Integer;
- begin
- {There must be a Header chunk to get the size, otherwise returns 0}
- if HeaderPresent then
- Result := Header.Width
- else Result := 0;
- end;
- {Returns if the image is empty}
- function TPngObject.GetEmpty: Boolean;
- begin Result := (Chunks.Count = 0);
- end;
- {Raises an error}
- procedure TPngObject.RaiseError(ExceptionClass: ExceptClass; Text: String);
- begin
- raise ExceptionClass.Create(Text);
- end;
- {Set the maximum size for IDAT chunk}
- procedure TPngObject.SetMaxIdatSize(const Value: Integer);
- begin
- {Make sure the size is at least 65535}
- if Value < High(Word) then
- fMaxIdatSize := High(Word) else fMaxIdatSize := Value;
- end;
- {Draws the image using pixel information from TChunkpHYs}
- procedure TPNGObject.DrawUsingPixelInformation(Canvas: TCanvas; Point: TPoint);
- function Rect(Left, Top, Right, Bottom: Integer): TRect;
- begin
- Result.Left := Left;
- Result.Top := Top;
- Result.Right := Right;
- Result.Bottom := Bottom;
- end;
- var
- PPMeterY, PPMeterX: Double;
- NewSizeX, NewSizeY: Integer;
- DC: HDC;
- begin
- {Get system information}
- DC := GetDC(0);
- PPMeterY := GetDeviceCaps(DC, LOGPIXELSY) / 0.0254;
- PPMeterX := GetDeviceCaps(DC, LOGPIXELSX) / 0.0254;
- ReleaseDC(0, DC);
- {In case it does not has pixel information}
- if not HasPixelInformation then
- Draw(Canvas, Rect(Point.X, Point.Y, Point.X + Width,
- Point.Y + Height))
- else
- with PixelInformation do
- begin
- NewSizeX := Trunc(Self.Width / (PPUnitX / PPMeterX));
- NewSizeY := Trunc(Self.Height / (PPUnitY / PPMeterY));
- Draw(Canvas, Rect(Point.X, Point.Y, Point.X + NewSizeX,
- Point.Y + NewSizeY));
- end;
- end;
- {$IFNDEF UseDelphi}
- {Creates a file stream reading from the filename in the parameter and load}
- procedure TPngObject.LoadFromFile(const Filename: String);
- var
- FileStream: TFileStream;
- begin
- {Test if the file exists}
- if not FileExists(Filename) then
- begin
- {In case it does not exists, raise error}
- RaiseError(EPNGNotExists, EPNGNotExistsText);
- exit;
- end;
- {Creates the file stream to read}
- FileStream := TFileStream.Create(Filename, [fsmRead]);
- LoadFromStream(FileStream); {Loads the data}
- FileStream.Free; {Free file stream}
- end;
- {Saves the current png image to a file}
- procedure TPngObject.SaveToFile(const Filename: String);
- var
- FileStream: TFileStream;
- begin
- {Creates the file stream to write}
- FileStream := TFileStream.Create(Filename, [fsmWrite]);
- SaveToStream(FileStream); {Saves the data}
- FileStream.Free; {Free file stream}
- end;
- {$ENDIF}
- {Returns if it has the pixel information chunk}
- function TPngObject.HasPixelInformation: Boolean;
- begin
- Result := (Chunks.ItemFromClass(TChunkpHYs) as tChunkpHYs) <> nil;
- end;
- {Returns the pixel information chunk}
- function TPngObject.GetPixelInformation: TChunkpHYs;
- begin
- Result := Chunks.ItemFromClass(TChunkpHYs) as tChunkpHYs;
- if not Assigned(Result) then
- begin
- Result := Chunks.Add(tChunkpHYs) as tChunkpHYs;
- Result.fUnit := utMeter;
- end;
- end;
- {Returns pointer to the chunk TChunkIHDR which should be the first}
- function TPngObject.GetHeader: TChunkIHDR;
- begin
- {If there is a TChunkIHDR returns it, otherwise returns nil}
- if (Chunks.Count <> 0) and (Chunks.Item[0] is TChunkIHDR) then
- Result := Chunks.Item[0] as TChunkIHDR
- else
- begin
- {No header, throw error message}
- RaiseError(EPNGHeaderNotPresent, EPNGHeaderNotPresentText);
- Result := nil
- end
- end;
- {Draws using partial transparency}
- procedure TPngObject.DrawPartialTrans(DC: HDC; Rect: TRect);
- {Adjust the rectangle structure}
- procedure AdjustRect(var Rect: TRect);
- var
- t: Integer;
- begin
- if Rect.Right < Rect.Left then
- begin
- t := Rect.Right;
- Rect.Right := Rect.Left;
- Rect.Left := t;
- end;
- if Rect.Bottom < Rect.Top then
- begin
- t := Rect.Bottom;
- Rect.Bottom := Rect.Top;
- Rect.Top := t;
- end
- end;
- type
- {Access to pixels}
- TPixelLine = Array[Word] of TRGBQuad;
- pPixelLine = ^TPixelLine;
- const
- {Structure used to create the bitmap}
- BitmapInfoHeader: TBitmapInfoHeader =
- (biSize: sizeof(TBitmapInfoHeader);
- biWidth: 100;
- biHeight: 100;
- biPlanes: 1;
- biBitCount: 32;
- biCompression: BI_RGB;
- biSizeImage: 0;
- biXPelsPerMeter: 0;
- biYPelsPerMeter: 0;
- biClrUsed: 0;
- biClrImportant: 0);
- var
- {Buffer bitmap creation}
- BitmapInfo : TBitmapInfo;
- BufferDC : HDC;
- BufferBits : Pointer;
- OldBitmap,
- BufferBitmap: HBitmap;
- Header: TChunkIHDR;
- {Transparency/palette chunks}
- TransparencyChunk: TChunktRNS;
- PaletteChunk: TChunkPLTE;
- TransValue, PaletteIndex: Byte;
- CurBit: Integer;
- Data: PByte;
- {Buffer bitmap modification}
- BytesPerRowDest,
- BytesPerRowSrc,
- BytesPerRowAlpha: Integer;
- ImageSource, ImageSourceOrg,
- AlphaSource : pByteArray;
- ImageData : pPixelLine;
- i, j, i2, j2 : Integer;
- {For bitmap stretching}
- W, H : Cardinal;
- Stretch : Boolean;
- FactorX, FactorY: Double;
- begin
- {Prepares the rectangle structure to stretch draw}
- if (Rect.Right = Rect.Left) or (Rect.Bottom = Rect.Top) then exit;
- AdjustRect(Rect);
- {Gets the width and height}
- W := Rect.Right - Rect.Left;
- H := Rect.Bottom - Rect.Top;
- Header := Self.Header; {Fast access to header}
- Stretch := (W <> Header.Width) or (H <> Header.Height);
- if Stretch then FactorX := W / Header.Width else FactorX := 1;
- if Stretch then FactorY := H / Header.Height else FactorY := 1;
- {Prepare to create the bitmap}
- Fillchar(BitmapInfo, sizeof(BitmapInfo), #0);
- BitmapInfoHeader.biWidth := W;
- BitmapInfoHeader.biHeight := -Integer(H);
- BitmapInfo.bmiHeader := BitmapInfoHeader;
- {Create the bitmap which will receive the background, the applied}
- {alpha blending and then will be painted on the background}
- BufferDC := CreateCompatibleDC(0);
- {In case BufferDC could not be created}
- if (BufferDC = 0) then RaiseError(EPNGOutMemory, EPNGOutMemoryText);
- BufferBitmap := CreateDIBSection(BufferDC, BitmapInfo, DIB_RGB_COLORS,
- BufferBits, 0, 0);
- {In case buffer bitmap could not be created}
- if (BufferBitmap = 0) or (BufferBits = Nil) then
- begin
- if BufferBitmap <> 0 then DeleteObject(BufferBitmap);
- DeleteDC(BufferDC);
- RaiseError(EPNGOutMemory, EPNGOutMemoryText);
- end;
- {Selects new bitmap and release old bitmap}
- OldBitmap := SelectObject(BufferDC, BufferBitmap);
- {Draws the background on the buffer image}
- BitBlt(BufferDC, 0, 0, W, H, DC, Rect.Left, Rect.Top, SRCCOPY);
- {Obtain number of bytes for each row}
- BytesPerRowAlpha := Header.Width;
- BytesPerRowDest := (((BitmapInfo.bmiHeader.biBitCount * W) + 31)
- and not 31) div 8; {Number of bytes for each image row in destination}
- BytesPerRowSrc := (((Header.BitmapInfo.bmiHeader.biBitCount * Header.Width) +
- 31) and not 31) div 8; {Number of bytes for each image row in source}
- {Obtains image pointers}
- ImageData := BufferBits;
- AlphaSource := Header.ImageAlpha;
- Longint(ImageSource) := Longint(Header.ImageData) +
- Header.BytesPerRow * Longint(Header.Height - 1);
- ImageSourceOrg := ImageSource;
- case Header.BitmapInfo.bmiHeader.biBitCount of
- {R, G, B images}
- 24:
- FOR j := 1 TO H DO
- begin
- {Process all the pixels in this line}
- FOR i := 0 TO W - 1 DO
- begin
- if Stretch then i2 := trunc(i / FactorX) else i2 := i;
- {Optmize when we don´t have transparency}
- if (AlphaSource[i2] <> 0) then
- if (AlphaSource[i2] = 255) then
- ImageData[i] := pRGBQuad(@ImageSource[i2 * 3])^
- else
- with ImageData[i] do
- begin
- rgbRed := (255+ImageSource[2+i2*3] * AlphaSource[i2] + rgbRed *
- (not AlphaSource[i2])) shr 8;
- rgbGreen := (255+ImageSource[1+i2*3] * AlphaSource[i2] +
- rgbGreen * (not AlphaSource[i2])) shr 8;
- rgbBlue := (255+ImageSource[i2*3] * AlphaSource[i2] + rgbBlue *
- (not AlphaSource[i2])) shr 8;
- end;
- end;
- {Move pointers}
- inc(Longint(ImageData), BytesPerRowDest);
- if Stretch then j2 := trunc(j / FactorY) else j2 := j;
- Longint(ImageSource) := Longint(ImageSourceOrg) - BytesPerRowSrc * j2;
- Longint(AlphaSource) := Longint(Header.ImageAlpha) +
- BytesPerRowAlpha * j2;
- end;
- {Palette images with 1 byte for each pixel}
- 1,4,8: if Header.ColorType = COLOR_GRAYSCALEALPHA then
- FOR j := 1 TO H DO
- begin
- {Process all the pixels in this line}
- FOR i := 0 TO W - 1 DO
- with ImageData[i], Header.BitmapInfo do begin
- if Stretch then i2 := trunc(i / FactorX) else i2 := i;
- rgbRed := (255 + ImageSource[i2] * AlphaSource[i2] +
- rgbRed * (255 - AlphaSource[i2])) shr 8;
- rgbGreen := (255 + ImageSource[i2] * AlphaSource[i2] +
- rgbGreen * (255 - AlphaSource[i2])) shr 8;
- rgbBlue := (255 + ImageSource[i2] * AlphaSource[i2] +
- rgbBlue * (255 - AlphaSource[i2])) shr 8;
- end;
- {Move pointers}
- Longint(ImageData) := Longint(ImageData) + BytesPerRowDest;
- if Stretch then j2 := trunc(j / FactorY) else j2 := j;
- Longint(ImageSource) := Longint(ImageSourceOrg) - BytesPerRowSrc * j2;
- Longint(AlphaSource) := Longint(Header.ImageAlpha) +
- BytesPerRowAlpha * j2;
- end
- else {Palette images}
- begin
- {Obtain pointer to the transparency chunk}
- TransparencyChunk := TChunktRNS(Chunks.ItemFromClass(TChunktRNS));
- PaletteChunk := TChunkPLTE(Chunks.ItemFromClass(TChunkPLTE));
- FOR j := 1 TO H DO
- begin
- {Process all the pixels in this line}
- i := 0;
- repeat
- CurBit := 0;
- if Stretch then i2 := trunc(i / FactorX) else i2 := i;
- Data := @ImageSource[i2];
- repeat
- {Obtains the palette index}
- case Header.BitDepth of
- 1: PaletteIndex := (Data^ shr (7-(I Mod 8))) and 1;
- 2,4: PaletteIndex := (Data^ shr ((1-(I Mod 2))*4)) and $0F;
- else PaletteIndex := Data^;
- end;
- {Updates the image with the new pixel}
- with ImageData[i] do
- begin
- TransValue := TransparencyChunk.PaletteValues[PaletteIndex];
- rgbRed := (255 + PaletteChunk.Item[PaletteIndex].rgbRed *
- TransValue + rgbRed * (255 - TransValue)) shr 8;
- rgbGreen := (255 + PaletteChunk.Item[PaletteIndex].rgbGreen *
- TransValue + rgbGreen * (255 - TransValue)) shr 8;
- rgbBlue := (255 + PaletteChunk.Item[PaletteIndex].rgbBlue *
- TransValue + rgbBlue * (255 - TransValue)) shr 8;
- end;
- {Move to next data}
- inc(i); inc(CurBit, Header.BitmapInfo.bmiHeader.biBitCount);
- until CurBit >= 8;
- {Move to next source data}
- //inc(Data);
- until i >= Integer(W);
- {Move pointers}
- Longint(ImageData) := Longint(ImageData) + BytesPerRowDest;
- if Stretch then j2 := trunc(j / FactorY) else j2 := j;
- Longint(ImageSource) := Longint(ImageSourceOrg) - BytesPerRowSrc * j2;
- end
- end {Palette images}
- end {case Header.BitmapInfo.bmiHeader.biBitCount};
- {Draws the new bitmap on the foreground}
- BitBlt(DC, Rect.Left, Rect.Top, W, H, BufferDC, 0, 0, SRCCOPY);
- {Free bitmap}
- SelectObject(BufferDC, OldBitmap);
- DeleteObject(BufferBitmap);
- DeleteDC(BufferDC);
- end;
- {Draws the image into a canvas}
- procedure TPngObject.Draw(ACanvas: TCanvas; const Rect: TRect);
- var
- Header: TChunkIHDR;
- begin
- {Quit in case there is no header, otherwise obtain it}
- if Empty then Exit;
- Header := Chunks.GetItem(0) as TChunkIHDR;
- {Copy the data to the canvas}
- case Self.TransparencyMode of
- {$IFDEF PartialTransparentDraw}
- ptmPartial:
- DrawPartialTrans(ACanvas{$IFDEF UseDelphi}.Handle{$ENDIF}, Rect);
- {$ENDIF}
- ptmBit: DrawTransparentBitmap(ACanvas{$IFDEF UseDelphi}.Handle{$ENDIF},
- Header.ImageData, Header.BitmapInfo.bmiHeader,
- pBitmapInfo(@Header.BitmapInfo), Rect,
- {$IFDEF UseDelphi}ColorToRGB({$ENDIF}TransparentColor)
- {$IFDEF UseDelphi}){$ENDIF}
- else
- begin
- SetStretchBltMode(ACanvas{$IFDEF UseDelphi}.Handle{$ENDIF}, COLORONCOLOR);
- StretchDiBits(ACanvas{$IFDEF UseDelphi}.Handle{$ENDIF}, Rect.Left,
- Rect.Top, Rect.Right - Rect.Left, Rect.Bottom - Rect.Top, 0, 0,
- Header.Width, Header.Height, Header.ImageData,
- pBitmapInfo(@Header.BitmapInfo)^, DIB_RGB_COLORS, SRCCOPY)
- end
- end {case}
- end;
- {Characters for the header}
- const
- PngHeader: Array[0..7] of Char = (#137, #80, #78, #71, #13, #10, #26, #10);
- {Loads the image from a stream of data}
- procedure TPngObject.LoadFromStream(Stream: TStream);
- var
- Header : Array[0..7] of Char;
- HasIDAT : Boolean;
- {Chunks reading}
- ChunkCount : Cardinal;
- ChunkLength: Cardinal;
- ChunkName : TChunkName;
- begin
- {Initialize before start loading chunks}
- ChunkCount := 0;
- ClearChunks();
- {Reads the header}
- Stream.Read(Header[0], 8);
- {Test if the header matches}
- if Header <> PngHeader then
- begin
- RaiseError(EPNGInvalidFileHeader, EPNGInvalidFileHeaderText);
- Exit;
- end;
- HasIDAT := FALSE;
- Chunks.Count := 10;
- {Load chunks}
- repeat
- inc(ChunkCount); {Increment number of chunks}
- if Chunks.Count < ChunkCount then {Resize the chunks list if needed}
- Chunks.Count := Chunks.Count + 10;
- {Reads chunk length and invert since it is in network order}
- {also checks the Read method return, if it returns 0, it}
- {means that no bytes was readed, probably because it reached}
- {the end of the file}
- if Stream.Read(ChunkLength, 4) = 0 then
- begin
- {In case it found the end of the file here}
- Chunks.Count := ChunkCount - 1;
- RaiseError(EPNGUnexpectedEnd, EPNGUnexpectedEndText);
- end;
- ChunkLength := ByteSwap(ChunkLength);
- {Reads chunk name}
- Stream.Read(Chunkname, 4);
- {Here we check if the first chunk is the Header which is necessary}
- {to the file in order to be a valid Portable Network Graphics image}
- if (ChunkCount = 1) and (ChunkName <> 'IHDR') then
- begin
- Chunks.Count := ChunkCount - 1;
- RaiseError(EPNGIHDRNotFirst, EPNGIHDRNotFirstText);
- exit;
- end;
- {Has a previous IDAT}
- if (HasIDAT and (ChunkName = 'IDAT')) or (ChunkName = 'cHRM') then
- begin
- dec(ChunkCount);
- Stream.Seek(ChunkLength + 4, soFromCurrent);
- Continue;
- end;
- {Tell it has an IDAT chunk}
- if ChunkName = 'IDAT' then HasIDAT := TRUE;
- {Creates object for this chunk}
- Chunks.SetItem(ChunkCount - 1, CreateClassChunk(Self, ChunkName));
- {Check if the chunk is critical and unknown}
- {$IFDEF ErrorOnUnknownCritical}
- if (TChunk(Chunks.Item[ChunkCount - 1]).ClassType = TChunk) and
- ((Byte(ChunkName[0]) AND $20) = 0) and (ChunkName <> '') then
- begin
- Chunks.Count := ChunkCount;
- RaiseError(EPNGUnknownCriticalChunk, EPNGUnknownCriticalChunkText);
- end;
- {$ENDIF}
- {Loads it}
- try if not TChunk(Chunks.Item[ChunkCount - 1]).LoadFromStream(Stream,
- ChunkName, ChunkLength) then break;
- except
- Chunks.Count := ChunkCount;
- raise;
- end;
- {Terminates when it reaches the IEND chunk}
- until (ChunkName = 'IEND');
- {Resize the list to the appropriate size}
- Chunks.Count := ChunkCount;
- {Check if there is data}
- if not HasIDAT then
- RaiseError(EPNGNoImageData, EPNGNoImageDataText);
- end;
- {Changing height is not supported}
- procedure TPngObject.SetHeight(Value: Integer);
- begin
- Resize(Width, Value)
- end;
- {Changing width is not supported}
- procedure TPngObject.SetWidth(Value: Integer);
- begin
- Resize(Value, Height)
- end;
- {$IFDEF UseDelphi}
- {Saves to clipboard format (thanks to Antoine Pottern)}
- procedure TPNGObject.SaveToClipboardFormat(var AFormat: Word;
- var AData: THandle; var APalette: HPalette);
- begin
- with TBitmap.Create do
- try
- Width := Self.Width;
- Height := Self.Height;
- Self.Draw(Canvas, Rect(0, 0, Width, Height));
- SaveToClipboardFormat(AFormat, AData, APalette);
- finally
- Free;
- end {try}
- end;
- {Loads data from clipboard}
- procedure TPngObject.LoadFromClipboardFormat(AFormat: Word;
- AData: THandle; APalette: HPalette);
- begin
- with TBitmap.Create do
- try
- LoadFromClipboardFormat(AFormat, AData, APalette);
- Self.AssignHandle(Handle, False, 0);
- finally
- Free;
- end {try}
- end;
- {Returns if the image is transparent}
- function TPngObject.GetTransparent: Boolean;
- begin
- Result := (TransparencyMode <> ptmNone);
- end;
- {$ENDIF}
- {Saving the PNG image to a stream of data}
- procedure TPngObject.SaveToStream(Stream: TStream);
- var
- j: Integer;
- begin
- {Reads the header}
- Stream.Write(PNGHeader[0], 8);
- {Write each chunk}
- FOR j := 0 TO Chunks.Count - 1 DO
- Chunks.Item[j].SaveToStream(Stream)
- end;
- {Prepares the Header chunk}
- procedure BuildHeader(Header: TChunkIHDR; Handle: HBitmap; Info: pBitmap);
- var
- DC: HDC;
- begin
- {Set width and height}
- Header.Width := Info.bmWidth;
- Header.Height := abs(Info.bmHeight);
- {Set bit depth}
- if Info.bmBitsPixel >= 16 then
- Header.BitDepth := 8 else Header.BitDepth := Info.bmBitsPixel;
- {Set color type}
- if Info.bmBitsPixel >= 16 then
- Header.ColorType := COLOR_RGB else Header.ColorType := COLOR_PALETTE;
- {Set other info}
- Header.CompressionMethod := 0; {deflate/inflate}
- Header.InterlaceMethod := 0; {no interlace}
- {Prepares bitmap headers to hold data}
- Header.PrepareImageData();
- {Copy image data}
- DC := CreateCompatibleDC(0);
- GetDIBits(DC, Handle, 0, Header.Height, Header.ImageData,
- pBitmapInfo(@Header.BitmapInfo)^, DIB_RGB_COLORS);
- DeleteDC(DC);
- end;
- {Loads the image from a resource}
- procedure TPngObject.LoadFromResourceName(Instance: HInst;
- const Name: String);
- var
- ResStream: TResourceStream;
- begin
- {Creates an especial stream to load from the resource}
- try ResStream := TResourceStream.Create(Instance, Name, RT_RCDATA);
- except RaiseError(EPNGCouldNotLoadResource, EPNGCouldNotLoadResourceText);
- exit; end;
- {Loads the png image from the resource}
- try
- LoadFromStream(ResStream);
- finally
- ResStream.Free;
- end;
- end;
- {Loads the png from a resource ID}
- procedure TPngObject.LoadFromResourceID(Instance: HInst; ResID: Integer);
- begin
- LoadFromResourceName(Instance, String(ResID));
- end;
- {Assigns this tpngobject to another object}
- procedure TPngObject.AssignTo(Dest: TPersistent);
- {$IFDEF UseDelphi}
- function DetectPixelFormat: TPixelFormat;
- begin
- with Header do
- begin
- {Always use 24bits for partial transparency}
- if TransparencyMode = ptmPartial then
- DetectPixelFormat := pf24bit
- else
- case BitDepth of
- {Only supported by COLOR_PALETTE}
- 1: DetectPixelFormat := pf1bit;
- 2, 4: DetectPixelFormat := pf4bit;
- {8 may be palette or r, g, b values}
- 8, 16:
- case ColorType of
- COLOR_RGB, COLOR_GRAYSCALE: DetectPixelFormat := pf24bit;
- COLOR_PALETTE: DetectPixelFormat := pf8bit;
- else raise Exception.Create('');
- end {case ColorFormat of}
- else raise Exception.Create('');
- end {case BitDepth of}
- end {with Header}
- end;
- var
- TRNS: TChunkTRNS;
- {$ENDIF}
- begin
- {If the destination is also a TPNGObject make it assign}
- {this one}
- if Dest is TPNGObject then
- TPNGObject(Dest).AssignPNG(Self)
- {$IFDEF UseDelphi}
- {In case the destination is a bitmap}
- else if (Dest is TBitmap) and HeaderPresent then
- begin
- {Copies the handle using CopyImage API}
- TBitmap(Dest).PixelFormat := DetectPixelFormat;
- TBitmap(Dest).Width := Width;
- TBitmap(Dest).Height := Height;
- TBitmap(Dest).Canvas.Draw(0, 0, Self);
- {Copy transparency mode}
- if (TransparencyMode = ptmBit) then
- begin
- TRNS := Chunks.ItemFromClass(TChunkTRNS) as TChunkTRNS;
- TBitmap(Dest).TransparentColor := TRNS.TransparentColor;
- TBitmap(Dest).Transparent := True
- end {if (TransparencyMode = ptmBit)}
- end
- else
- {Unknown destination kind}
- inherited AssignTo(Dest);
- {$ENDIF}
- end;
- {Assigns from a bitmap object}
- procedure TPngObject.AssignHandle(Handle: HBitmap; Transparent: Boolean;
- TransparentColor: ColorRef);
- var
- BitmapInfo: Windows.TBitmap;
- {Chunks}
- Header: TChunkIHDR;
- PLTE: TChunkPLTE;
- IDAT: TChunkIDAT;
- IEND: TChunkIEND;
- TRNS: TChunkTRNS;
- i: Integer;
- palEntries : TMaxLogPalette;
- begin
- {Obtain bitmap info}
- GetObject(Handle, SizeOf(BitmapInfo), @BitmapInfo);
- {Clear old chunks and prepare}
- ClearChunks();
- {Create the chunks}
- Header := TChunkIHDR.Create(Self);
- {This method will fill the Header chunk with bitmap information}
- {and copy the image data}
- BuildHeader(Header, Handle, @BitmapInfo);
- if Header.HasPalette then PLTE := TChunkPLTE.Create(Self) else PLTE := nil;
- if Transparent then TRNS := TChunkTRNS.Create(Self) else TRNS := nil;
- IDAT := TChunkIDAT.Create(Self);
- IEND := TChunkIEND.Create(Self);
- {Add chunks}
- TPNGPointerList(Chunks).Add(Header);
- if Header.HasPalette then TPNGPointerList(Chunks).Add(PLTE);
- if Transparent then TPNGPointerList(Chunks).Add(TRNS);
- TPNGPointerList(Chunks).Add(IDAT);
- TPNGPointerList(Chunks).Add(IEND);
- {In case there is a image data, set the PLTE chunk fCount variable}
- {to the actual number of palette colors which is 2^(Bits for each pixel)}
- if Header.HasPalette then
- begin
- PLTE.fCount := 1 shl BitmapInfo.bmBitsPixel;
- {Create and set palette}
- fillchar(palEntries, sizeof(palEntries), 0);
- palEntries.palVersion := $300;
- palEntries.palNumEntries := 1 shl BitmapInfo.bmBitsPixel;
- for i := 0 to palEntries.palNumEntries - 1 do
- begin
- palEntries.palPalEntry[i].peRed := Header.BitmapInfo.bmiColors[i].rgbRed;
- palEntries.palPalEntry[i].peGreen := Header.BitmapInfo.bmiColors[i].rgbGreen;
- palEntries.palPalEntry[i].peBlue := Header.BitmapInfo.bmiColors[i].rgbBlue;
- end;
- DoSetPalette(CreatePalette(pLogPalette(@palEntries)^), false);
- end;
- {In case it is a transparent bitmap, prepares it}
- if Transparent then TRNS.TransparentColor := TransparentColor;
- end;
- {Assigns from another PNG}
- procedure TPngObject.AssignPNG(Source: TPNGObject);
- var
- J: Integer;
- begin
- {Copy properties}
- InterlaceMethod := Source.InterlaceMethod;
- MaxIdatSize := Source.MaxIdatSize;
- CompressionLevel := Source.CompressionLevel;
- Filters := Source.Filters;
- {Clear old chunks and prepare}
- ClearChunks();
- Chunks.Count := Source.Chunks.Count;
- {Create chunks and makes a copy from the source}
- FOR J := 0 TO Chunks.Count - 1 DO
- with Source.Chunks do
- begin
- Chunks.SetItem(J, TChunkClass(TChunk(Item[J]).ClassType).Create(Self));
- TChunk(Chunks.Item[J]).Assign(TChunk(Item[J]));
- end {with};
- end;
- {Returns a alpha data scanline}
- function TPngObject.GetAlphaScanline(const LineIndex: Integer): pByteArray;
- begin
- with Header do
- if (ColorType = COLOR_RGBALPHA) or (ColorType = COLOR_GRAYSCALEALPHA) then
- Longint(Result) := Longint(ImageAlpha) + (LineIndex * Longint(Width))
- else Result := nil; {In case the image does not use alpha information}
- end;
- {$IFDEF Store16bits}
- {Returns a png data extra scanline}
- function TPngObject.GetExtraScanline(const LineIndex: Integer): Pointer;
- begin
- with Header do
- Longint(Result) := (Longint(ExtraImageData) + ((Longint(Height) - 1) *
- BytesPerRow)) - (LineIndex * BytesPerRow);
- end;
- {$ENDIF}
- {Returns a png data scanline}
- function TPngObject.GetScanline(const LineIndex: Integer): Pointer;
- begin
- with Header do
- Longint(Result) := (Longint(ImageData) + ((Longint(Height) - 1) *
- BytesPerRow)) - (LineIndex * BytesPerRow);
- end;
- {Initialize gamma table}
- procedure TPngObject.InitializeGamma;
- var
- i: Integer;
- begin
- {Build gamma table as if there was no gamma}
- FOR i := 0 to 255 do
- begin
- GammaTable[i] := i;
- InverseGamma[i] := i;
- end {for i}
- end;
- {Returns the transparency mode used by this png}
- function TPngObject.GetTransparencyMode: TPNGTransparencyMode;
- var
- TRNS: TChunkTRNS;
- begin
- with Header do
- begin
- Result := ptmNone; {Default result}
- {Gets the TRNS chunk pointer}
- TRNS := Chunks.ItemFromClass(TChunkTRNS) as TChunkTRNS;
- {Test depending on the color type}
- case ColorType of
- {This modes are always partial}
- COLOR_RGBALPHA, COLOR_GRAYSCALEALPHA: Result := ptmPartial;
- {This modes support bit transparency}
- COLOR_RGB, COLOR_GRAYSCALE: if TRNS <> nil then Result := ptmBit;
- {Supports booth translucid and bit}
- COLOR_PALETTE:
- {A TRNS chunk must be present, otherwise it won't support transparency}
- if TRNS <> nil then
- if TRNS.BitTransparency then
- Result := ptmBit else Result := ptmPartial
- end {case}
- end {with Header}
- end;
- {Add a text chunk}
- procedure TPngObject.AddtEXt(const Keyword, Text: String);
- var
- TextChunk: TChunkTEXT;
- begin
- TextChunk := Chunks.Add(TChunkText) as TChunkTEXT;
- TextChunk.Keyword := Keyword;
- TextChunk.Text := Text;
- end;
- {Add a text chunk}
- procedure TPngObject.AddzTXt(const Keyword, Text: String);
- var
- TextChunk: TChunkzTXt;
- begin
- TextChunk := Chunks.Add(TChunkzTXt) as TChunkzTXt;
- TextChunk.Keyword := Keyword;
- TextChunk.Text := Text;
- end;
- {Removes the image transparency}
- procedure TPngObject.RemoveTransparency;
- var
- TRNS: TChunkTRNS;
- begin
- {Removes depending on the color type}
- with Header do
- case ColorType of
- {Palette uses the TChunktRNS to store alpha}
- COLOR_PALETTE:
- begin
- TRNS := Chunks.ItemFromClass(TChunkTRNS) as TChunkTRNS;
- if TRNS <> nil then Chunks.RemoveChunk(TRNS)
- end;
- {Png allocates different memory space to hold alpha information}
- {for these types}
- COLOR_GRAYSCALEALPHA, COLOR_RGBALPHA:
- begin
- {Transform into the appropriate color type}
- if ColorType = COLOR_GRAYSCALEALPHA then
- ColorType := COLOR_GRAYSCALE
- else ColorType := COLOR_RGB;
- {Free the pointer data}
- if ImageAlpha <> nil then FreeMem(ImageAlpha);
- ImageAlpha := nil
- end
- end
- end;
- {Generates alpha information}
- procedure TPngObject.CreateAlpha;
- var
- TRNS: TChunkTRNS;
- begin
- {Generates depending on the color type}
- with Header do
- case ColorType of
- {Png allocates different memory space to hold alpha information}
- {for these types}
- COLOR_GRAYSCALE, COLOR_RGB:
- begin
- {Transform into the appropriate color type}
- if ColorType = COLOR_GRAYSCALE then
- ColorType := COLOR_GRAYSCALEALPHA
- else ColorType := COLOR_RGBALPHA;
- {Allocates memory to hold alpha information}
- GetMem(ImageAlpha, Integer(Width) * Integer(Height));
- FillChar(ImageAlpha^, Integer(Width) * Integer(Height), #255);
- end;
- {Palette uses the TChunktRNS to store alpha}
- COLOR_PALETTE:
- begin
- {Gets/creates TRNS chunk}
- if Chunks.ItemFromClass(TChunkTRNS) = nil then
- TRNS := Chunks.Add(TChunkTRNS) as TChunkTRNS
- else
- TRNS := Chunks.ItemFromClass(TChunkTRNS) as TChunkTRNS;
- {Prepares the TRNS chunk}
- with TRNS do
- begin
- ResizeData(256);
- Fillchar(PaletteValues[0], 256, 255);
- fDataSize := 1 shl Header.BitDepth;
- fBitTransparency := False
- end {with Chunks.Add};
- end;
- end {case Header.ColorType}
- end;
- {Returns transparent color}
- function TPngObject.GetTransparentColor: TColor;
- var
- TRNS: TChunkTRNS;
- begin
- TRNS := Chunks.ItemFromClass(TChunkTRNS) as TChunkTRNS;
- {Reads the transparency chunk to get this info}
- if Assigned(TRNS) then Result := TRNS.TransparentColor
- else Result := 0
- end;
- {$OPTIMIZATION OFF}
- procedure TPngObject.SetTransparentColor(const Value: TColor);
- var
- TRNS: TChunkTRNS;
- begin
- if HeaderPresent then
- {Tests the ColorType}
- case Header.ColorType of
- {Not allowed for this modes}
- COLOR_RGBALPHA, COLOR_GRAYSCALEALPHA: Self.RaiseError(
- EPNGCannotChangeTransparent, EPNGCannotChangeTransparentText);
- {Allowed}
- COLOR_PALETTE, COLOR_RGB, COLOR_GRAYSCALE:
- begin
- TRNS := Chunks.ItemFromClass(TChunkTRNS) as TChunkTRNS;
- if not Assigned(TRNS) then TRNS := Chunks.Add(TChunkTRNS) as TChunkTRNS;
- {Sets the transparency value from TRNS chunk}
- TRNS.TransparentColor := {$IFDEF UseDelphi}ColorToRGB({$ENDIF}Value
- {$IFDEF UseDelphi}){$ENDIF}
- end {COLOR_PALETTE, COLOR_RGB, COLOR_GRAYSCALE)}
- end {case}
- end;
- {Returns if header is present}
- function TPngObject.HeaderPresent: Boolean;
- begin
- Result := ((Chunks.Count <> 0) and (Chunks.Item[0] is TChunkIHDR))
- end;
- {Returns pixel for png using palette and grayscale}
- function GetByteArrayPixel(const png: TPngObject; const X, Y: Integer): TColor;
- var
- ByteData: Byte;
- DataDepth: Byte;
- begin
- with png, Header do
- begin
- {Make sure the bitdepth is not greater than 8}
- DataDepth := BitDepth;
- if DataDepth > 8 then DataDepth := 8;
- {Obtains the byte containing this pixel}
- ByteData := pByteArray(png.Scanline[Y])^[X div (8 div DataDepth)];
- {Moves the bits we need to the right}
- ByteData := (ByteData shr ((8 - DataDepth) -
- (X mod (8 div DataDepth)) * DataDepth));
- {Discard the unwanted pixels}
- ByteData:= ByteData and ($FF shr (8 - DataDepth));
- {For palette mode map the palette entry and for grayscale convert and
- returns the intensity}
- case ColorType of
- COLOR_PALETTE:
- with TChunkPLTE(png.Chunks.ItemFromClass(TChunkPLTE)).Item[ByteData] do
- Result := rgb(GammaTable[rgbRed], GammaTable[rgbGreen],
- GammaTable[rgbBlue]);
- COLOR_GRAYSCALE:
- begin
- if BitDepth = 1
- then ByteData := GammaTable[Byte(ByteData * 255)]
- else ByteData := GammaTable[Byte(ByteData * ((1 shl DataDepth) + 1))];
- Result := rgb(ByteData, ByteData, ByteData);
- end;
- else Result := 0;
- end {case};
- end {with}
- end;
- {In case vcl units are not being used}
- {$IFNDEF UseDelphi}
- function ColorToRGB(const Color: TColor): COLORREF;
- begin
- Result := Color
- end;
- {$ENDIF}
- {Sets a pixel for grayscale and palette pngs}
- procedure SetByteArrayPixel(const png: TPngObject; const X, Y: Integer;
- const Value: TColor);
- const
- ClearFlag: Array[1..8] of Integer = (1, 3, 0, 15, 0, 0, 0, $FF);
- var
- ByteData: pByte;
- DataDepth: Byte;
- ValEntry: Byte;
- begin
- with png.Header do
- begin
- {Map into a palette entry}
- ValEntry := GetNearestPaletteIndex(Png.Palette, ColorToRGB(Value));
- {16 bits grayscale extra bits are discarted}
- DataDepth := BitDepth;
- if DataDepth > 8 then DataDepth := 8;
- {Gets a pointer to the byte we intend to change}
- ByteData := @pByteArray(png.Scanline[Y])^[X div (8 div DataDepth)];
- {Clears the old pixel data}
- ByteData^ := ByteData^ and not (ClearFlag[DataDepth] shl ((8 - DataDepth) -
- (X mod (8 div DataDepth)) * DataDepth));
- {Setting the new pixel}
- ByteData^ := ByteData^ or (ValEntry shl ((8 - DataDepth) -
- (X mod (8 div DataDepth)) * DataDepth));
- end {with png.Header}
- end;
- {Returns pixel when png uses RGB}
- function GetRGBLinePixel(const png: TPngObject;
- const X, Y: Integer): TColor;
- begin
- with pRGBLine(png.Scanline[Y])^[X] do
- Result := RGB(rgbtRed, rgbtGreen, rgbtBlue)
- end;
- {Sets pixel when png uses RGB}
- procedure SetRGBLinePixel(const png: TPngObject;
- const X, Y: Integer; Value: TColor);
- begin
- with pRGBLine(png.Scanline[Y])^[X] do
- begin
- rgbtRed := GetRValue(Value);
- rgbtGreen := GetGValue(Value);
- rgbtBlue := GetBValue(Value)
- end
- end;
- {Returns pixel when png uses grayscale}
- function GetGrayLinePixel(const png: TPngObject;
- const X, Y: Integer): TColor;
- var
- B: Byte;
- begin
- B := PByteArray(png.Scanline[Y])^[X];
- Result := RGB(B, B, B);
- end;
- {Sets pixel when png uses grayscale}
- procedure SetGrayLinePixel(const png: TPngObject;
- const X, Y: Integer; Value: TColor);
- begin
- PByteArray(png.Scanline[Y])^[X] := GetRValue(Value);
- end;
- {Resizes the PNG image}
- procedure TPngObject.Resize(const CX, CY: Integer);
- function Min(const A, B: Integer): Integer;
- begin
- if A < B then Result := A else Result := B;
- end;
- var
- Header: TChunkIHDR;
- Line, NewBytesPerRow: Integer;
- NewHandle: HBitmap;
- NewDC: HDC;
- NewImageData: Pointer;
- NewImageAlpha: Pointer;
- NewImageExtra: Pointer;
- begin
- if (CX > 0) and (CY > 0) then
- begin
- {Gets some actual information}
- Header := Self.Header;
- {Creates the new image}
- NewDC := CreateCompatibleDC(Header.ImageDC);
- Header.BitmapInfo.bmiHeader.biWidth := cx;
- Header.BitmapInfo.bmiHeader.biHeight := cy;
- NewHandle := CreateDIBSection(NewDC, pBitmapInfo(@Header.BitmapInfo)^,
- DIB_RGB_COLORS, NewImageData, 0, 0);
- SelectObject(NewDC, NewHandle);
- {$IFDEF UseDelphi}Canvas.Handle := NewDC;{$ENDIF}
- NewBytesPerRow := (((Header.BitmapInfo.bmiHeader.biBitCount * cx) + 31)
- and not 31) div 8;
- {Copies the image data}
- for Line := 0 to Min(CY - 1, Height - 1) do
- CopyMemory(Ptr(Longint(NewImageData) + (Longint(CY) - 1) *
- NewBytesPerRow - (Line * NewBytesPerRow)), Scanline[Line],
- Min(NewBytesPerRow, Header.BytesPerRow));
- {Build array for alpha information, if necessary}
- if (Header.ColorType = COLOR_RGBALPHA) or
- (Header.ColorType = COLOR_GRAYSCALEALPHA) then
- begin
- GetMem(NewImageAlpha, CX * CY);
- Fillchar(NewImageAlpha^, CX * CY, 255);
- for Line := 0 to Min(CY - 1, Height - 1) do
- CopyMemory(Ptr(Longint(NewImageAlpha) + (Line * CX)),
- AlphaScanline[Line], Min(CX, Width));
- FreeMem(Header.ImageAlpha);
- Header.ImageAlpha := NewImageAlpha;
- end;
- {$IFDEF Store16bits}
- if (Header.BitDepth = 16) then
- begin
- GetMem(NewImageExtra, CX * CY);
- Fillchar(NewImageExtra^, CX * CY, 0);
- for Line := 0 to Min(CY - 1, Height - 1) do
- CopyMemory(Ptr(Longint(NewImageExtra) + (Line * CX)),
- ExtraScanline[Line], Min(CX, Width));
- FreeMem(Header.ExtraImageData);
- Header.ExtraImageData := NewImageExtra;
- end;
- {$ENDIF}
- {Deletes the old image}
- DeleteObject(Header.ImageHandle);
- DeleteDC(Header.ImageDC);
- {Prepares the header to get the new image}
- Header.BytesPerRow := NewBytesPerRow;
- Header.IHDRData.Width := CX;
- Header.IHDRData.Height := CY;
- Header.ImageData := NewImageData;
- {Replaces with the new image}
- Header.ImageHandle := NewHandle;
- Header.ImageDC := NewDC;
- end
- else
- {The new size provided is invalid}
- RaiseError(EPNGInvalidNewSize, EInvalidNewSize)
- end;
- {Sets a pixel}
- procedure TPngObject.SetPixels(const X, Y: Integer; const Value: TColor);
- begin
- if ((X >= 0) and (X <= Width - 1)) and
- ((Y >= 0) and (Y <= Height - 1)) then
- with Header do
- begin
- if ColorType in [COLOR_GRAYSCALE, COLOR_PALETTE] then
- SetByteArrayPixel(Self, X, Y, Value)
- else if ColorType in [COLOR_GRAYSCALEALPHA] then
- SetGrayLinePixel(Self, X, Y, Value)
- else
- SetRGBLinePixel(Self, X, Y, Value)
- end {with}
- end;
- {Returns a pixel}
- function TPngObject.GetPixels(const X, Y: Integer): TColor;
- begin
- if ((X >= 0) and (X <= Width - 1)) and
- ((Y >= 0) and (Y <= Height - 1)) then
- with Header do
- begin
- if ColorType in [COLOR_GRAYSCALE, COLOR_PALETTE] then
- Result := GetByteArrayPixel(Self, X, Y)
- else if ColorType in [COLOR_GRAYSCALEALPHA] then
- Result := GetGrayLinePixel(Self, X, Y)
- else
- Result := GetRGBLinePixel(Self, X, Y)
- end {with}
- else Result := 0
- end;
- {Returns the image palette}
- function TPngObject.GetPalette: HPALETTE;
- begin
- Result := Header.ImagePalette;
- end;
- {Assigns from another TChunk}
- procedure TChunkpHYs.Assign(Source: TChunk);
- begin
- fPPUnitY := TChunkpHYs(Source).fPPUnitY;
- fPPUnitX := TChunkpHYs(Source).fPPUnitX;
- fUnit := TChunkpHYs(Source).fUnit;
- end;
- {Loads the chunk from a stream}
- function TChunkpHYs.LoadFromStream(Stream: TStream; const ChunkName: TChunkName;
- Size: Integer): Boolean;
- begin
- {Let ancestor load the data}
- Result := inherited LoadFromStream(Stream, ChunkName, Size);
- if not Result or (Size <> 9) then exit; {Size must be 9}
- {Reads data}
- fPPUnitX := ByteSwap(pCardinal(Longint(Data))^);
- fPPUnitY := ByteSwap(pCardinal(Longint(Data) + 4)^);
- fUnit := pUnitType(Longint(Data) + 8)^;
- end;
- {Saves the chunk to a stream}
- function TChunkpHYs.SaveToStream(Stream: TStream): Boolean;
- begin
- {Update data}
- ResizeData(9); {Make sure the size is 9}
- pCardinal(Data)^ := ByteSwap(fPPUnitX);
- pCardinal(Longint(Data) + 4)^ := ByteSwap(fPPUnitY);
- pUnitType(Longint(Data) + 8)^ := fUnit;
- {Let inherited save data}
- Result := inherited SaveToStream(Stream);
- end;
- procedure TPngObject.DoSetPalette(Value: HPALETTE; const UpdateColors: boolean);
- begin
- if (Header.HasPalette) then
- begin
- {Update the palette entries}
- if UpdateColors then
- Header.PaletteToDIB(Value);
- {Resize the new palette}
- SelectPalette(Header.ImageDC, Value, False);
- RealizePalette(Header.ImageDC);
- {Replaces}
- DeleteObject(Header.ImagePalette);
- Header.ImagePalette := Value;
- end
- end;
- {Set palette based on a windows palette handle}
- procedure TPngObject.SetPalette(Value: HPALETTE);
- begin
- DoSetPalette(Value, true);
- end;
- {Returns the library version}
- function TPNGObject.GetLibraryVersion: String;
- begin
- Result := LibraryVersion
- end;
- initialization
- {Initialize}
- ChunkClasses := nil;
- {crc table has not being computed yet}
- crc_table_computed := FALSE;
- {Register the necessary chunks for png}
- RegisterCommonChunks;
- {Registers TPNGObject to use with TPicture}
- {$IFDEF UseDelphi}{$IFDEF RegisterGraphic}
- TPicture.RegisterFileFormat('PNG', 'Portable Network Graphics', TPNGObject);
- {$ENDIF}{$ENDIF}
- finalization
- {$IFDEF UseDelphi}{$IFDEF RegisterGraphic}
- TPicture.UnregisterGraphicClass(TPNGObject);
- {$ENDIF}{$ENDIF}
- {Free chunk classes}
- FreeChunkClassList;
- end.
|