TGASharpLib.cs 215 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977
  1. //
  2. // Copyright 2020 Electronic Arts Inc.
  3. //
  4. // The Command & Conquer Map Editor and corresponding source code is free
  5. // software: you can redistribute it and/or modify it under the terms of
  6. // the GNU General Public License as published by the Free Software Foundation,
  7. // either version 3 of the License, or (at your option) any later version.
  8. // The Command & Conquer Map Editor and corresponding source code is distributed
  9. // in the hope that it will be useful, but with permitted additional restrictions
  10. // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
  11. // distributed with this program. You should have received a copy of the
  12. // GNU General Public License along with permitted additional restrictions
  13. // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
  14. /* MIT License
  15. Copyright (c) 2017 TGASharpLib
  16. Permission is hereby granted, free of charge, to any person obtaining a copy
  17. of this software and associated documentation files (the "Software"), to deal
  18. in the Software without restriction, including without limitation the rights
  19. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  20. copies of the Software, and to permit persons to whom the Software is
  21. furnished to do so, subject to the following conditions:
  22. The above copyright notice and this permission notice shall be included in all
  23. copies or substantial portions of the Software.
  24. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  25. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  26. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  27. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  28. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  29. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  30. SOFTWARE.
  31. */
  32. using System;
  33. using System.Collections.Generic;
  34. using System.Text;
  35. using System.Drawing;
  36. using System.Drawing.Imaging;
  37. using System.IO;
  38. using System.Runtime.InteropServices;
  39. namespace TGASharpLib
  40. {
  41. #region Enums
  42. /// <summary>
  43. /// <para>The first 128 Color Map Type codes are reserved for use by Truevision,
  44. /// while the second set of 128 Color Map Type codes(128 to 255) may be used for
  45. /// developer applications.</para>
  46. /// True-Color images do not normally make use of the color map field, but some current
  47. /// applications store palette information or developer-defined information in this field.
  48. /// It is best to check Field 3, Image Type, to make sure you have a file which can use the
  49. /// data stored in the Color Map Field.
  50. /// Otherwise ignore the information. When saving or creating files for True-Color
  51. /// images do not use this field and set it to Zero to ensure compatibility. Please refer
  52. /// to the Developer Area specification for methods of storing developer defined information.
  53. /// </summary>
  54. public enum TgaColorMapType : byte
  55. {
  56. NoColorMap = 0,
  57. ColorMap = 1,
  58. Truevision_2,
  59. Truevision_3,
  60. Truevision_4,
  61. Truevision_5,
  62. Truevision_6,
  63. Truevision_7,
  64. Truevision_8,
  65. Truevision_9,
  66. Truevision_10,
  67. Truevision_11,
  68. Truevision_12,
  69. Truevision_13,
  70. Truevision_14,
  71. Truevision_15,
  72. Truevision_16,
  73. Truevision_17,
  74. Truevision_18,
  75. Truevision_19,
  76. Truevision_20,
  77. Truevision_21,
  78. Truevision_22,
  79. Truevision_23,
  80. Truevision_24,
  81. Truevision_25,
  82. Truevision_26,
  83. Truevision_27,
  84. Truevision_28,
  85. Truevision_29,
  86. Truevision_30,
  87. Truevision_31,
  88. Truevision_32,
  89. Truevision_33,
  90. Truevision_34,
  91. Truevision_35,
  92. Truevision_36,
  93. Truevision_37,
  94. Truevision_38,
  95. Truevision_39,
  96. Truevision_40,
  97. Truevision_41,
  98. Truevision_42,
  99. Truevision_43,
  100. Truevision_44,
  101. Truevision_45,
  102. Truevision_46,
  103. Truevision_47,
  104. Truevision_48,
  105. Truevision_49,
  106. Truevision_50,
  107. Truevision_51,
  108. Truevision_52,
  109. Truevision_53,
  110. Truevision_54,
  111. Truevision_55,
  112. Truevision_56,
  113. Truevision_57,
  114. Truevision_58,
  115. Truevision_59,
  116. Truevision_60,
  117. Truevision_61,
  118. Truevision_62,
  119. Truevision_63,
  120. Truevision_64,
  121. Truevision_65,
  122. Truevision_66,
  123. Truevision_67,
  124. Truevision_68,
  125. Truevision_69,
  126. Truevision_70,
  127. Truevision_71,
  128. Truevision_72,
  129. Truevision_73,
  130. Truevision_74,
  131. Truevision_75,
  132. Truevision_76,
  133. Truevision_77,
  134. Truevision_78,
  135. Truevision_79,
  136. Truevision_80,
  137. Truevision_81,
  138. Truevision_82,
  139. Truevision_83,
  140. Truevision_84,
  141. Truevision_85,
  142. Truevision_86,
  143. Truevision_87,
  144. Truevision_88,
  145. Truevision_89,
  146. Truevision_90,
  147. Truevision_91,
  148. Truevision_92,
  149. Truevision_93,
  150. Truevision_94,
  151. Truevision_95,
  152. Truevision_96,
  153. Truevision_97,
  154. Truevision_98,
  155. Truevision_99,
  156. Truevision_100,
  157. Truevision_101,
  158. Truevision_102,
  159. Truevision_103,
  160. Truevision_104,
  161. Truevision_105,
  162. Truevision_106,
  163. Truevision_107,
  164. Truevision_108,
  165. Truevision_109,
  166. Truevision_110,
  167. Truevision_111,
  168. Truevision_112,
  169. Truevision_113,
  170. Truevision_114,
  171. Truevision_115,
  172. Truevision_116,
  173. Truevision_117,
  174. Truevision_118,
  175. Truevision_119,
  176. Truevision_120,
  177. Truevision_121,
  178. Truevision_122,
  179. Truevision_123,
  180. Truevision_124,
  181. Truevision_125,
  182. Truevision_126,
  183. Truevision_127,
  184. Other_128,
  185. Other_129,
  186. Other_130,
  187. Other_131,
  188. Other_132,
  189. Other_133,
  190. Other_134,
  191. Other_135,
  192. Other_136,
  193. Other_137,
  194. Other_138,
  195. Other_139,
  196. Other_140,
  197. Other_141,
  198. Other_142,
  199. Other_143,
  200. Other_144,
  201. Other_145,
  202. Other_146,
  203. Other_147,
  204. Other_148,
  205. Other_149,
  206. Other_150,
  207. Other_151,
  208. Other_152,
  209. Other_153,
  210. Other_154,
  211. Other_155,
  212. Other_156,
  213. Other_157,
  214. Other_158,
  215. Other_159,
  216. Other_160,
  217. Other_161,
  218. Other_162,
  219. Other_163,
  220. Other_164,
  221. Other_165,
  222. Other_166,
  223. Other_167,
  224. Other_168,
  225. Other_169,
  226. Other_170,
  227. Other_171,
  228. Other_172,
  229. Other_173,
  230. Other_174,
  231. Other_175,
  232. Other_176,
  233. Other_177,
  234. Other_178,
  235. Other_179,
  236. Other_180,
  237. Other_181,
  238. Other_182,
  239. Other_183,
  240. Other_184,
  241. Other_185,
  242. Other_186,
  243. Other_187,
  244. Other_188,
  245. Other_189,
  246. Other_190,
  247. Other_191,
  248. Other_192,
  249. Other_193,
  250. Other_194,
  251. Other_195,
  252. Other_196,
  253. Other_197,
  254. Other_198,
  255. Other_199,
  256. Other_200,
  257. Other_201,
  258. Other_202,
  259. Other_203,
  260. Other_204,
  261. Other_205,
  262. Other_206,
  263. Other_207,
  264. Other_208,
  265. Other_209,
  266. Other_210,
  267. Other_211,
  268. Other_212,
  269. Other_213,
  270. Other_214,
  271. Other_215,
  272. Other_216,
  273. Other_217,
  274. Other_218,
  275. Other_219,
  276. Other_220,
  277. Other_221,
  278. Other_222,
  279. Other_223,
  280. Other_224,
  281. Other_225,
  282. Other_226,
  283. Other_227,
  284. Other_228,
  285. Other_229,
  286. Other_230,
  287. Other_231,
  288. Other_232,
  289. Other_233,
  290. Other_234,
  291. Other_235,
  292. Other_236,
  293. Other_237,
  294. Other_238,
  295. Other_239,
  296. Other_240,
  297. Other_241,
  298. Other_242,
  299. Other_243,
  300. Other_244,
  301. Other_245,
  302. Other_246,
  303. Other_247,
  304. Other_248,
  305. Other_249,
  306. Other_250,
  307. Other_251,
  308. Other_252,
  309. Other_253,
  310. Other_254,
  311. Other_255
  312. }
  313. /// <summary>
  314. /// Establishes the number of bits per entry. Typically 15, 16, 24 or 32-bit values are used.
  315. /// <para>When working with VDA or VDA/D cards it is preferred that you select 16 bits(5 bits
  316. /// per primary with 1 bit to select interrupt control) and set the 16th bit to 0 so that the
  317. /// interrupt bit is disabled. Even if this field is set to 15 bits(5 bits per primary) you
  318. /// must still parse the color map data 16 bits at a time and ignore the 16th bit.</para>
  319. /// <para>When working with a TARGA M8 card you would select 24 bits (8 bits per primary)
  320. /// since the color map is defined as 256 entries of 24 bit color values.</para>
  321. /// When working with a TrueVista card(ATVista or NuVista) you would select 24-bit(8 bits per
  322. /// primary) or 32-bit(8 bits per primary including Alpha channel) depending on your
  323. /// application’s use of look-up tables. It is suggested that when working with 16-bit and
  324. /// 32-bit color images, you store them as True-Color images and do not use the color map
  325. /// field to store look-up tables. Please refer to the TGA Extensions for fields better suited
  326. /// to storing look-up table information.
  327. /// </summary>
  328. public enum TgaColorMapEntrySize : byte
  329. {
  330. Other = 0,
  331. X1R5G5B5 = 15,
  332. A1R5G5B5 = 16,
  333. R8G8B8 = 24,
  334. A8R8G8B8 = 32
  335. }
  336. /// <summary>
  337. /// Truevision has currently defined seven image types:
  338. /// <para>0 - No Image Data Included;</para>
  339. /// <para>1 - Uncompressed, Color-mapped Image;</para>
  340. /// <para>2 - Uncompressed, True-color Image;</para>
  341. /// <para>3 - Uncompressed, Black-and-white Image;</para>
  342. /// <para>9 - Run-length encoded, Color-mapped Image;</para>
  343. /// <para>10 - Run-length encoded, True-color Image;</para>
  344. /// <para>11 - Run-length encoded, Black-and-white Image.</para>
  345. /// Image Data Type codes 0 to 127 are reserved for use by Truevision for general applications.
  346. /// Image Data Type codes 128 to 255 may be used for developer applications.
  347. /// </summary>
  348. public enum TgaImageType : byte
  349. {
  350. NoImageData = 0,
  351. Uncompressed_ColorMapped = 1,
  352. Uncompressed_TrueColor,
  353. Uncompressed_BlackWhite,
  354. _Truevision_4,
  355. _Truevision_5,
  356. _Truevision_6,
  357. _Truevision_7,
  358. _Truevision_8,
  359. RLE_ColorMapped = 9,
  360. RLE_TrueColor,
  361. RLE_BlackWhite,
  362. _Truevision_12,
  363. _Truevision_13,
  364. _Truevision_14,
  365. _Truevision_15,
  366. _Truevision_16,
  367. _Truevision_17,
  368. _Truevision_18,
  369. _Truevision_19,
  370. _Truevision_20,
  371. _Truevision_21,
  372. _Truevision_22,
  373. _Truevision_23,
  374. _Truevision_24,
  375. _Truevision_25,
  376. _Truevision_26,
  377. _Truevision_27,
  378. _Truevision_28,
  379. _Truevision_29,
  380. _Truevision_30,
  381. _Truevision_31,
  382. _Truevision_32,
  383. _Truevision_33,
  384. _Truevision_34,
  385. _Truevision_35,
  386. _Truevision_36,
  387. _Truevision_37,
  388. _Truevision_38,
  389. _Truevision_39,
  390. _Truevision_40,
  391. _Truevision_41,
  392. _Truevision_42,
  393. _Truevision_43,
  394. _Truevision_44,
  395. _Truevision_45,
  396. _Truevision_46,
  397. _Truevision_47,
  398. _Truevision_48,
  399. _Truevision_49,
  400. _Truevision_50,
  401. _Truevision_51,
  402. _Truevision_52,
  403. _Truevision_53,
  404. _Truevision_54,
  405. _Truevision_55,
  406. _Truevision_56,
  407. _Truevision_57,
  408. _Truevision_58,
  409. _Truevision_59,
  410. _Truevision_60,
  411. _Truevision_61,
  412. _Truevision_62,
  413. _Truevision_63,
  414. _Truevision_64,
  415. _Truevision_65,
  416. _Truevision_66,
  417. _Truevision_67,
  418. _Truevision_68,
  419. _Truevision_69,
  420. _Truevision_70,
  421. _Truevision_71,
  422. _Truevision_72,
  423. _Truevision_73,
  424. _Truevision_74,
  425. _Truevision_75,
  426. _Truevision_76,
  427. _Truevision_77,
  428. _Truevision_78,
  429. _Truevision_79,
  430. _Truevision_80,
  431. _Truevision_81,
  432. _Truevision_82,
  433. _Truevision_83,
  434. _Truevision_84,
  435. _Truevision_85,
  436. _Truevision_86,
  437. _Truevision_87,
  438. _Truevision_88,
  439. _Truevision_89,
  440. _Truevision_90,
  441. _Truevision_91,
  442. _Truevision_92,
  443. _Truevision_93,
  444. _Truevision_94,
  445. _Truevision_95,
  446. _Truevision_96,
  447. _Truevision_97,
  448. _Truevision_98,
  449. _Truevision_99,
  450. _Truevision_100,
  451. _Truevision_101,
  452. _Truevision_102,
  453. _Truevision_103,
  454. _Truevision_104,
  455. _Truevision_105,
  456. _Truevision_106,
  457. _Truevision_107,
  458. _Truevision_108,
  459. _Truevision_109,
  460. _Truevision_110,
  461. _Truevision_111,
  462. _Truevision_112,
  463. _Truevision_113,
  464. _Truevision_114,
  465. _Truevision_115,
  466. _Truevision_116,
  467. _Truevision_117,
  468. _Truevision_118,
  469. _Truevision_119,
  470. _Truevision_120,
  471. _Truevision_121,
  472. _Truevision_122,
  473. _Truevision_123,
  474. _Truevision_124,
  475. _Truevision_125,
  476. _Truevision_126,
  477. _Truevision_127,
  478. _Other_128,
  479. _Other_129,
  480. _Other_130,
  481. _Other_131,
  482. _Other_132,
  483. _Other_133,
  484. _Other_134,
  485. _Other_135,
  486. _Other_136,
  487. _Other_137,
  488. _Other_138,
  489. _Other_139,
  490. _Other_140,
  491. _Other_141,
  492. _Other_142,
  493. _Other_143,
  494. _Other_144,
  495. _Other_145,
  496. _Other_146,
  497. _Other_147,
  498. _Other_148,
  499. _Other_149,
  500. _Other_150,
  501. _Other_151,
  502. _Other_152,
  503. _Other_153,
  504. _Other_154,
  505. _Other_155,
  506. _Other_156,
  507. _Other_157,
  508. _Other_158,
  509. _Other_159,
  510. _Other_160,
  511. _Other_161,
  512. _Other_162,
  513. _Other_163,
  514. _Other_164,
  515. _Other_165,
  516. _Other_166,
  517. _Other_167,
  518. _Other_168,
  519. _Other_169,
  520. _Other_170,
  521. _Other_171,
  522. _Other_172,
  523. _Other_173,
  524. _Other_174,
  525. _Other_175,
  526. _Other_176,
  527. _Other_177,
  528. _Other_178,
  529. _Other_179,
  530. _Other_180,
  531. _Other_181,
  532. _Other_182,
  533. _Other_183,
  534. _Other_184,
  535. _Other_185,
  536. _Other_186,
  537. _Other_187,
  538. _Other_188,
  539. _Other_189,
  540. _Other_190,
  541. _Other_191,
  542. _Other_192,
  543. _Other_193,
  544. _Other_194,
  545. _Other_195,
  546. _Other_196,
  547. _Other_197,
  548. _Other_198,
  549. _Other_199,
  550. _Other_200,
  551. _Other_201,
  552. _Other_202,
  553. _Other_203,
  554. _Other_204,
  555. _Other_205,
  556. _Other_206,
  557. _Other_207,
  558. _Other_208,
  559. _Other_209,
  560. _Other_210,
  561. _Other_211,
  562. _Other_212,
  563. _Other_213,
  564. _Other_214,
  565. _Other_215,
  566. _Other_216,
  567. _Other_217,
  568. _Other_218,
  569. _Other_219,
  570. _Other_220,
  571. _Other_221,
  572. _Other_222,
  573. _Other_223,
  574. _Other_224,
  575. _Other_225,
  576. _Other_226,
  577. _Other_227,
  578. _Other_228,
  579. _Other_229,
  580. _Other_230,
  581. _Other_231,
  582. _Other_232,
  583. _Other_233,
  584. _Other_234,
  585. _Other_235,
  586. _Other_236,
  587. _Other_237,
  588. _Other_238,
  589. _Other_239,
  590. _Other_240,
  591. _Other_241,
  592. _Other_242,
  593. _Other_243,
  594. _Other_244,
  595. _Other_245,
  596. _Other_246,
  597. _Other_247,
  598. _Other_248,
  599. _Other_249,
  600. _Other_250,
  601. _Other_251,
  602. _Other_252,
  603. _Other_253,
  604. _Other_254,
  605. _Other_255
  606. }
  607. /// <summary>
  608. /// Number of bits per pixel. This number includes the Attribute or Alpha channel bits.
  609. /// Common values are 8, 16, 24 and 32 but other pixel depths could be used.
  610. /// </summary>
  611. public enum TgaPixelDepth : byte
  612. {
  613. Other = 0,
  614. Bpp8 = 8,
  615. Bpp16 = 16,
  616. Bpp24 = 24,
  617. Bpp32 = 32
  618. }
  619. /// <summary>
  620. /// Used to indicate the order in which pixel data is transferred from the file to the screen.
  621. /// (Bit 4 (bit 0 in enum) is for left-to-right ordering and bit 5 (bit 1 in enum) is for
  622. /// topto-bottom ordering as shown below.)
  623. /// </summary>
  624. public enum TgaImgOrigin : byte
  625. {
  626. BottomLeft = 0,
  627. BottomRight,
  628. TopLeft,
  629. TopRight
  630. }
  631. /// <summary>
  632. /// Contains a value which specifies the type of Alpha channel
  633. /// data contained in the file. Value Meaning:
  634. /// <para>0: no Alpha data included (bits 3-0 of field 5.6 should also be set to zero)</para>
  635. /// <para>1: undefined data in the Alpha field, can be ignored</para>
  636. /// <para>2: undefined data in the Alpha field, but should be retained</para>
  637. /// <para>3: useful Alpha channel data is present</para>
  638. /// <para>4: pre-multiplied Alpha(see description below)</para>
  639. /// <para>5 -127: RESERVED</para>
  640. /// <para>128-255: Un-assigned</para>
  641. /// <para>Pre-multiplied Alpha Example: Suppose the Alpha channel data is being used to specify the
  642. /// opacity of each pixel(for use when the image is overlayed on another image), where 0 indicates
  643. /// that the pixel is completely transparent and a value of 1 indicates that the pixel is
  644. /// completely opaque(assume all component values have been normalized).</para>
  645. /// <para>A quadruple(a, r, g, b) of( 0.5, 1, 0, 0) would indicate that the pixel is pure red with a
  646. /// transparency of one-half. For numerous reasons(including image compositing) is is better to
  647. /// pre-multiply the individual color components with the value in the Alpha channel.</para>
  648. /// A pre-multiplication of the above would produce a quadruple(0.5, 0.5, 0, 0).
  649. /// A value of 3 in the Attributes Type Field(field 23) would indicate that the color components
  650. /// of the pixel have already been scaled by the value in the Alpha channel.
  651. /// </summary>
  652. public enum TgaAttrType : byte
  653. {
  654. NoAlpha = 0,
  655. UndefinedAlphaCanBeIgnored,
  656. UndefinedAlphaButShouldBeRetained,
  657. UsefulAlpha,
  658. PreMultipliedAlpha,
  659. _Reserved_5,
  660. _Reserved_6,
  661. _Reserved_7,
  662. _Reserved_8,
  663. _Reserved_9,
  664. _Reserved_10,
  665. _Reserved_11,
  666. _Reserved_12,
  667. _Reserved_13,
  668. _Reserved_14,
  669. _Reserved_15,
  670. _Reserved_16,
  671. _Reserved_17,
  672. _Reserved_18,
  673. _Reserved_19,
  674. _Reserved_20,
  675. _Reserved_21,
  676. _Reserved_22,
  677. _Reserved_23,
  678. _Reserved_24,
  679. _Reserved_25,
  680. _Reserved_26,
  681. _Reserved_27,
  682. _Reserved_28,
  683. _Reserved_29,
  684. _Reserved_30,
  685. _Reserved_31,
  686. _Reserved_32,
  687. _Reserved_33,
  688. _Reserved_34,
  689. _Reserved_35,
  690. _Reserved_36,
  691. _Reserved_37,
  692. _Reserved_38,
  693. _Reserved_39,
  694. _Reserved_40,
  695. _Reserved_41,
  696. _Reserved_42,
  697. _Reserved_43,
  698. _Reserved_44,
  699. _Reserved_45,
  700. _Reserved_46,
  701. _Reserved_47,
  702. _Reserved_48,
  703. _Reserved_49,
  704. _Reserved_50,
  705. _Reserved_51,
  706. _Reserved_52,
  707. _Reserved_53,
  708. _Reserved_54,
  709. _Reserved_55,
  710. _Reserved_56,
  711. _Reserved_57,
  712. _Reserved_58,
  713. _Reserved_59,
  714. _Reserved_60,
  715. _Reserved_61,
  716. _Reserved_62,
  717. _Reserved_63,
  718. _Reserved_64,
  719. _Reserved_65,
  720. _Reserved_66,
  721. _Reserved_67,
  722. _Reserved_68,
  723. _Reserved_69,
  724. _Reserved_70,
  725. _Reserved_71,
  726. _Reserved_72,
  727. _Reserved_73,
  728. _Reserved_74,
  729. _Reserved_75,
  730. _Reserved_76,
  731. _Reserved_77,
  732. _Reserved_78,
  733. _Reserved_79,
  734. _Reserved_80,
  735. _Reserved_81,
  736. _Reserved_82,
  737. _Reserved_83,
  738. _Reserved_84,
  739. _Reserved_85,
  740. _Reserved_86,
  741. _Reserved_87,
  742. _Reserved_88,
  743. _Reserved_89,
  744. _Reserved_90,
  745. _Reserved_91,
  746. _Reserved_92,
  747. _Reserved_93,
  748. _Reserved_94,
  749. _Reserved_95,
  750. _Reserved_96,
  751. _Reserved_97,
  752. _Reserved_98,
  753. _Reserved_99,
  754. _Reserved_100,
  755. _Reserved_101,
  756. _Reserved_102,
  757. _Reserved_103,
  758. _Reserved_104,
  759. _Reserved_105,
  760. _Reserved_106,
  761. _Reserved_107,
  762. _Reserved_108,
  763. _Reserved_109,
  764. _Reserved_110,
  765. _Reserved_111,
  766. _Reserved_112,
  767. _Reserved_113,
  768. _Reserved_114,
  769. _Reserved_115,
  770. _Reserved_116,
  771. _Reserved_117,
  772. _Reserved_118,
  773. _Reserved_119,
  774. _Reserved_120,
  775. _Reserved_121,
  776. _Reserved_122,
  777. _Reserved_123,
  778. _Reserved_124,
  779. _Reserved_125,
  780. _Reserved_126,
  781. _Reserved_127,
  782. _UnAssigned_128,
  783. _UnAssigned_129,
  784. _UnAssigned_130,
  785. _UnAssigned_131,
  786. _UnAssigned_132,
  787. _UnAssigned_133,
  788. _UnAssigned_134,
  789. _UnAssigned_135,
  790. _UnAssigned_136,
  791. _UnAssigned_137,
  792. _UnAssigned_138,
  793. _UnAssigned_139,
  794. _UnAssigned_140,
  795. _UnAssigned_141,
  796. _UnAssigned_142,
  797. _UnAssigned_143,
  798. _UnAssigned_144,
  799. _UnAssigned_145,
  800. _UnAssigned_146,
  801. _UnAssigned_147,
  802. _UnAssigned_148,
  803. _UnAssigned_149,
  804. _UnAssigned_150,
  805. _UnAssigned_151,
  806. _UnAssigned_152,
  807. _UnAssigned_153,
  808. _UnAssigned_154,
  809. _UnAssigned_155,
  810. _UnAssigned_156,
  811. _UnAssigned_157,
  812. _UnAssigned_158,
  813. _UnAssigned_159,
  814. _UnAssigned_160,
  815. _UnAssigned_161,
  816. _UnAssigned_162,
  817. _UnAssigned_163,
  818. _UnAssigned_164,
  819. _UnAssigned_165,
  820. _UnAssigned_166,
  821. _UnAssigned_167,
  822. _UnAssigned_168,
  823. _UnAssigned_169,
  824. _UnAssigned_170,
  825. _UnAssigned_171,
  826. _UnAssigned_172,
  827. _UnAssigned_173,
  828. _UnAssigned_174,
  829. _UnAssigned_175,
  830. _UnAssigned_176,
  831. _UnAssigned_177,
  832. _UnAssigned_178,
  833. _UnAssigned_179,
  834. _UnAssigned_180,
  835. _UnAssigned_181,
  836. _UnAssigned_182,
  837. _UnAssigned_183,
  838. _UnAssigned_184,
  839. _UnAssigned_185,
  840. _UnAssigned_186,
  841. _UnAssigned_187,
  842. _UnAssigned_188,
  843. _UnAssigned_189,
  844. _UnAssigned_190,
  845. _UnAssigned_191,
  846. _UnAssigned_192,
  847. _UnAssigned_193,
  848. _UnAssigned_194,
  849. _UnAssigned_195,
  850. _UnAssigned_196,
  851. _UnAssigned_197,
  852. _UnAssigned_198,
  853. _UnAssigned_199,
  854. _UnAssigned_200,
  855. _UnAssigned_201,
  856. _UnAssigned_202,
  857. _UnAssigned_203,
  858. _UnAssigned_204,
  859. _UnAssigned_205,
  860. _UnAssigned_206,
  861. _UnAssigned_207,
  862. _UnAssigned_208,
  863. _UnAssigned_209,
  864. _UnAssigned_210,
  865. _UnAssigned_211,
  866. _UnAssigned_212,
  867. _UnAssigned_213,
  868. _UnAssigned_214,
  869. _UnAssigned_215,
  870. _UnAssigned_216,
  871. _UnAssigned_217,
  872. _UnAssigned_218,
  873. _UnAssigned_219,
  874. _UnAssigned_220,
  875. _UnAssigned_221,
  876. _UnAssigned_222,
  877. _UnAssigned_223,
  878. _UnAssigned_224,
  879. _UnAssigned_225,
  880. _UnAssigned_226,
  881. _UnAssigned_227,
  882. _UnAssigned_228,
  883. _UnAssigned_229,
  884. _UnAssigned_230,
  885. _UnAssigned_231,
  886. _UnAssigned_232,
  887. _UnAssigned_233,
  888. _UnAssigned_234,
  889. _UnAssigned_235,
  890. _UnAssigned_236,
  891. _UnAssigned_237,
  892. _UnAssigned_238,
  893. _UnAssigned_239,
  894. _UnAssigned_240,
  895. _UnAssigned_241,
  896. _UnAssigned_242,
  897. _UnAssigned_243,
  898. _UnAssigned_244,
  899. _UnAssigned_245,
  900. _UnAssigned_246,
  901. _UnAssigned_247,
  902. _UnAssigned_248,
  903. _UnAssigned_249,
  904. _UnAssigned_250,
  905. _UnAssigned_251,
  906. _UnAssigned_252,
  907. _UnAssigned_253,
  908. _UnAssigned_254,
  909. _UnAssigned_255
  910. }
  911. #endregion
  912. #region Classes
  913. public class TgaColorKey : ICloneable
  914. {
  915. byte a = 0;
  916. byte r = 0;
  917. byte g = 0;
  918. byte b = 0;
  919. public TgaColorKey()
  920. {
  921. }
  922. /// <summary>
  923. /// Make <see cref="TgaColorKey"/> from ARGB bytes.
  924. /// </summary>
  925. /// <param name="A">Alpha value.</param>
  926. /// <param name="R">Red value.</param>
  927. /// <param name="G">Green value.</param>
  928. /// <param name="B">Blue value.</param>
  929. public TgaColorKey(byte A, byte R, byte G, byte B)
  930. {
  931. a = A;
  932. r = R;
  933. g = G;
  934. b = B;
  935. }
  936. /// <summary>
  937. /// Make <see cref="TgaColorKey"/> from ARGB bytes.
  938. /// </summary>
  939. /// <param name="Bytes">Array of bytes(byte[4]).</param>
  940. public TgaColorKey(byte[] Bytes)
  941. {
  942. if (Bytes == null)
  943. throw new ArgumentNullException(nameof(Bytes) + " = null!");
  944. if (Bytes.Length != Size)
  945. throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
  946. Color color = Color.FromArgb(BitConverter.ToInt32(Bytes, 0));
  947. a = color.A;
  948. r = color.R;
  949. g = color.G;
  950. b = color.B;
  951. }
  952. /// <summary>
  953. /// Make <see cref="TgaColorKey"/> from <see cref="int"/>.
  954. /// </summary>
  955. /// <param name="ARGB">32bit ARGB integer color value.</param>
  956. public TgaColorKey(int ARGB)
  957. {
  958. Color ColorARGB = Color.FromArgb(ARGB);
  959. a = ColorARGB.A;
  960. r = ColorARGB.R;
  961. g = ColorARGB.G;
  962. b = ColorARGB.B;
  963. }
  964. /// <summary>
  965. /// Make <see cref="TgaColorKey"/> from <see cref="Color"/>.
  966. /// </summary>
  967. /// <param name="color">GDI+ <see cref="Color"/> value.</param>
  968. public TgaColorKey(Color color)
  969. {
  970. a = color.A;
  971. r = color.R;
  972. g = color.G;
  973. b = color.B;
  974. }
  975. /// <summary>
  976. /// Gets or sets alpha color value.
  977. /// </summary>
  978. public byte A
  979. {
  980. get { return a; }
  981. set { a = value; }
  982. }
  983. /// <summary>
  984. /// Gets or sets red color value.
  985. /// </summary>
  986. public byte R
  987. {
  988. get { return r; }
  989. set { r = value; }
  990. }
  991. /// <summary>
  992. /// Gets or sets green color value.
  993. /// </summary>
  994. public byte G
  995. {
  996. get { return g; }
  997. set { g = value; }
  998. }
  999. /// <summary>
  1000. /// Gets or sets blue color value.
  1001. /// </summary>
  1002. public byte B
  1003. {
  1004. get { return b; }
  1005. set { b = value; }
  1006. }
  1007. /// <summary>
  1008. /// Gets TGA Field size in bytes.
  1009. /// </summary>
  1010. public const int Size = 4;
  1011. /// <summary>
  1012. /// Make full independed copy of <see cref="TgaColorKey"/>.
  1013. /// </summary>
  1014. /// <returns>Copy of <see cref="TgaColorKey"/></returns>
  1015. public TgaColorKey Clone()
  1016. {
  1017. return new TgaColorKey(a, r, g, b);
  1018. }
  1019. /// <summary>
  1020. /// Make full independed copy of <see cref="TgaColorKey"/>.
  1021. /// </summary>
  1022. /// <returns>Copy of <see cref="TgaColorKey"/></returns>
  1023. object ICloneable.Clone()
  1024. {
  1025. return Clone();
  1026. }
  1027. public override bool Equals(object obj)
  1028. {
  1029. return ((obj is TgaColorKey) ? Equals((TgaColorKey)obj) : false);
  1030. }
  1031. public bool Equals(TgaColorKey item)
  1032. {
  1033. return (a == item.a && r == item.r && g == item.g && b == item.b);
  1034. }
  1035. public static bool operator ==(TgaColorKey item1, TgaColorKey item2)
  1036. {
  1037. if (ReferenceEquals(item1, null))
  1038. return ReferenceEquals(item2, null);
  1039. if (ReferenceEquals(item2, null))
  1040. return ReferenceEquals(item1, null);
  1041. return item1.Equals(item2);
  1042. }
  1043. public static bool operator !=(TgaColorKey item1, TgaColorKey item2)
  1044. {
  1045. return !(item1 == item2);
  1046. }
  1047. public override int GetHashCode()
  1048. {
  1049. return ToInt().GetHashCode();
  1050. }
  1051. /// <summary>
  1052. /// Gets <see cref="TgaColorKey"/> like string.
  1053. /// </summary>
  1054. /// <returns>String in ARGB format.</returns>
  1055. public override string ToString()
  1056. {
  1057. return String.Format("{0}={1}, {2}={3}, {4}={5}, {6}={7}",
  1058. nameof(A), a, nameof(R), r, nameof(G), g, nameof(B), b);
  1059. }
  1060. /// <summary>
  1061. /// Convert <see cref="TgaColorKey"/> to byte array.
  1062. /// </summary>
  1063. /// <returns>Byte array with length = 4.</returns>
  1064. public byte[] ToBytes()
  1065. {
  1066. return BitConverter.GetBytes(ToInt());
  1067. }
  1068. /// <summary>
  1069. /// Gets <see cref="TgaColorKey"/> like GDI+ <see cref="Color"/>.
  1070. /// </summary>
  1071. /// <returns><see cref="Color"/> value of <see cref="TgaColorKey"/>.</returns>
  1072. public Color ToColor()
  1073. {
  1074. return Color.FromArgb(a, r, g, b);
  1075. }
  1076. /// <summary>
  1077. /// Gets <see cref="TgaColorKey"/> like ARGB <see cref="int"/>.
  1078. /// </summary>
  1079. /// <returns>ARGB <see cref="int"/> value of <see cref="TgaColorKey"/>.</returns>
  1080. public int ToInt()
  1081. {
  1082. return ToColor().ToArgb();
  1083. }
  1084. }
  1085. /// <summary>
  1086. /// This field (5 bytes) and its sub-fields describe the color map (if any) used for the image.
  1087. /// If the Color Map Type field is set to zero, indicating that no color map exists, then
  1088. /// these 5 bytes should be set to zero. These bytes always must be written to the file.
  1089. /// </summary>
  1090. public class TgaColorMapSpec : ICloneable
  1091. {
  1092. ushort firstEntryIndex = 0;
  1093. ushort colorMapLength = 0;
  1094. TgaColorMapEntrySize colorMapEntrySize = TgaColorMapEntrySize.Other;
  1095. /// <summary>
  1096. /// Make new <see cref="TgaColorMapSpec"/>.
  1097. /// </summary>
  1098. public TgaColorMapSpec()
  1099. {
  1100. }
  1101. /// <summary>
  1102. /// Make <see cref="TgaColorMapSpec"/> from bytes.
  1103. /// </summary>
  1104. /// <param name="Bytes">Array of bytes(byte[5]).</param>
  1105. public TgaColorMapSpec(byte[] Bytes)
  1106. {
  1107. if (Bytes == null)
  1108. throw new ArgumentNullException(nameof(Bytes) + " = null!");
  1109. if (Bytes.Length != Size)
  1110. throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
  1111. firstEntryIndex = BitConverter.ToUInt16(Bytes, 0);
  1112. colorMapLength = BitConverter.ToUInt16(Bytes, 2);
  1113. colorMapEntrySize = (TgaColorMapEntrySize)Bytes[4];
  1114. }
  1115. /// <summary>
  1116. /// Field 4.1 (2 bytes):
  1117. /// Index of the first color map entry. Index refers to the starting entry in loading
  1118. /// the color map.
  1119. /// <para>Example: If you would have 1024 entries in the entire color map but you only
  1120. /// need to store 72 of those entries, this field allows you to start in the middle of
  1121. /// the color-map (e.g., position 342).</para>
  1122. /// </summary>
  1123. public ushort FirstEntryIndex
  1124. {
  1125. get { return firstEntryIndex; }
  1126. set { firstEntryIndex = value; }
  1127. }
  1128. /// <summary>
  1129. /// Field 4.2 (2 bytes):
  1130. /// Total number of color map entries included.
  1131. /// </summary>
  1132. public ushort ColorMapLength
  1133. {
  1134. get { return colorMapLength; }
  1135. set { colorMapLength = value; }
  1136. }
  1137. /// <summary>
  1138. /// Field 4.3 (1 byte):
  1139. /// Establishes the number of bits per entry. Typically 15, 16, 24 or 32-bit values are used.
  1140. /// <para>When working with VDA or VDA/D cards it is preferred that you select 16 bits(5 bits
  1141. /// per primary with 1 bit to select interrupt control) and set the 16th bit to 0 so that the
  1142. /// interrupt bit is disabled. Even if this field is set to 15 bits(5 bits per primary) you
  1143. /// must still parse the color map data 16 bits at a time and ignore the 16th bit.</para>
  1144. /// <para>When working with a TARGA M8 card you would select 24 bits (8 bits per primary)
  1145. /// since the color map is defined as 256 entries of 24 bit color values.</para>
  1146. /// When working with a TrueVista card(ATVista or NuVista) you would select 24-bit(8 bits per
  1147. /// primary) or 32-bit(8 bits per primary including Alpha channel) depending on your
  1148. /// application’s use of look-up tables. It is suggested that when working with 16-bit and
  1149. /// 32-bit color images, you store them as True-Color images and do not use the color map
  1150. /// field to store look-up tables. Please refer to the TGA Extensions for fields better suited
  1151. /// to storing look-up table information.
  1152. /// </summary>
  1153. public TgaColorMapEntrySize ColorMapEntrySize
  1154. {
  1155. get { return colorMapEntrySize; }
  1156. set { colorMapEntrySize = value; }
  1157. }
  1158. /// <summary>
  1159. /// Gets TGA Field size in bytes.
  1160. /// </summary>
  1161. public const int Size = 5;
  1162. /// <summary>
  1163. /// Make full independed copy of <see cref="TgaColorMapSpec"/>.
  1164. /// </summary>
  1165. /// <returns>Copy of <see cref="TgaColorMapSpec"/></returns>
  1166. public TgaColorMapSpec Clone()
  1167. {
  1168. return new TgaColorMapSpec(ToBytes());
  1169. }
  1170. /// <summary>
  1171. /// Make full independed copy of <see cref="TgaColorMapSpec"/>.
  1172. /// </summary>
  1173. /// <returns>Copy of <see cref="TgaColorMapSpec"/></returns>
  1174. object ICloneable.Clone()
  1175. {
  1176. return Clone();
  1177. }
  1178. public override bool Equals(object obj)
  1179. {
  1180. return ((obj is TgaColorMapSpec) ? Equals((TgaColorMapSpec)obj) : false);
  1181. }
  1182. public bool Equals(TgaColorMapSpec item)
  1183. {
  1184. return (firstEntryIndex == item.firstEntryIndex &&
  1185. colorMapLength == item.colorMapLength &&
  1186. colorMapEntrySize == item.colorMapEntrySize);
  1187. }
  1188. public static bool operator ==(TgaColorMapSpec item1, TgaColorMapSpec item2)
  1189. {
  1190. if (ReferenceEquals(item1, null))
  1191. return ReferenceEquals(item2, null);
  1192. if (ReferenceEquals(item2, null))
  1193. return ReferenceEquals(item1, null);
  1194. return item1.Equals(item2);
  1195. }
  1196. public static bool operator !=(TgaColorMapSpec item1, TgaColorMapSpec item2)
  1197. {
  1198. return !(item1 == item2);
  1199. }
  1200. public override int GetHashCode()
  1201. {
  1202. unchecked
  1203. {
  1204. return (firstEntryIndex << 16 | colorMapLength).GetHashCode() ^ colorMapEntrySize.GetHashCode();
  1205. }
  1206. }
  1207. public override string ToString()
  1208. {
  1209. return String.Format("{0}={1}, {2}={3}, {4}={5}", nameof(FirstEntryIndex), FirstEntryIndex,
  1210. nameof(ColorMapLength), ColorMapLength, nameof(ColorMapEntrySize), ColorMapEntrySize);
  1211. }
  1212. /// <summary>
  1213. /// Convert ColorMapSpec to byte array.
  1214. /// </summary>
  1215. /// <returns>Byte array with length = 5.</returns>
  1216. public byte[] ToBytes()
  1217. {
  1218. return BitConverterExt.ToBytes(firstEntryIndex, colorMapLength, (byte)colorMapEntrySize);
  1219. }
  1220. }
  1221. public class TgaComment : ICloneable
  1222. {
  1223. const int StrNLen = 80; //80 ASCII chars + 1 '\0' = 81 per SrtN!
  1224. string origString = String.Empty;
  1225. char blankSpaceChar = TgaString.DefaultBlankSpaceChar;
  1226. public TgaComment()
  1227. {
  1228. }
  1229. public TgaComment(string Str, char BlankSpaceChar = '\0')
  1230. {
  1231. if (Str == null)
  1232. throw new ArgumentNullException(nameof(Str) + " = null!");
  1233. origString = Str;
  1234. blankSpaceChar = BlankSpaceChar;
  1235. }
  1236. public TgaComment(byte[] Bytes)
  1237. {
  1238. if (Bytes == null)
  1239. throw new ArgumentNullException(nameof(Bytes) + " = null!");
  1240. if (Bytes.Length != Size)
  1241. throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
  1242. string s = Encoding.ASCII.GetString(Bytes, 0, StrNLen);
  1243. s += Encoding.ASCII.GetString(Bytes, 81, StrNLen);
  1244. s += Encoding.ASCII.GetString(Bytes, 162, StrNLen);
  1245. s += Encoding.ASCII.GetString(Bytes, 243, StrNLen);
  1246. switch (s[s.Length - 1])
  1247. {
  1248. case '\0':
  1249. case ' ':
  1250. blankSpaceChar = s[s.Length - 1];
  1251. origString = s.TrimEnd(new char[] { s[s.Length - 1] });
  1252. break;
  1253. default:
  1254. origString = s;
  1255. break;
  1256. }
  1257. }
  1258. /// <summary>
  1259. /// Gets TGA Field size in bytes.
  1260. /// </summary>
  1261. public const int Size = 81 * 4;
  1262. public string OriginalString
  1263. {
  1264. get { return origString; }
  1265. set { origString = value; }
  1266. }
  1267. public char BlankSpaceChar
  1268. {
  1269. get { return blankSpaceChar; }
  1270. set { blankSpaceChar = value; }
  1271. }
  1272. /// <summary>
  1273. /// Make full independed copy of <see cref="TgaComment"/>.
  1274. /// </summary>
  1275. /// <returns>Copy of <see cref="TgaComment"/></returns>
  1276. public TgaComment Clone()
  1277. {
  1278. return new TgaComment(origString, blankSpaceChar);
  1279. }
  1280. /// <summary>
  1281. /// Make full independed copy of <see cref="TgaComment"/>.
  1282. /// </summary>
  1283. /// <returns>Copy of <see cref="TgaComment"/></returns>
  1284. object ICloneable.Clone()
  1285. {
  1286. return Clone();
  1287. }
  1288. public override bool Equals(object obj)
  1289. {
  1290. return ((obj is TgaComment) ? Equals((TgaComment)obj) : false);
  1291. }
  1292. public bool Equals(TgaComment item)
  1293. {
  1294. return (origString == item.origString && blankSpaceChar == item.blankSpaceChar);
  1295. }
  1296. public static bool operator ==(TgaComment item1, TgaComment item2)
  1297. {
  1298. if (ReferenceEquals(item1, null))
  1299. return ReferenceEquals(item2, null);
  1300. if (ReferenceEquals(item2, null))
  1301. return ReferenceEquals(item1, null);
  1302. return item1.Equals(item2);
  1303. }
  1304. public static bool operator !=(TgaComment item1, TgaComment item2)
  1305. {
  1306. return !(item1 == item2);
  1307. }
  1308. public override int GetHashCode()
  1309. {
  1310. return origString.GetHashCode() ^ blankSpaceChar.GetHashCode();
  1311. }
  1312. /// <summary>
  1313. /// Get ASCII-Like string with string-terminators, example: "Line1 \0\0 Line2 \0\0\0".
  1314. /// </summary>
  1315. /// <returns>String with replaced string-terminators to "\0".</returns>
  1316. public override string ToString()
  1317. {
  1318. return Encoding.ASCII.GetString(ToBytes()).Replace("\0", @"\0");
  1319. }
  1320. /// <summary>
  1321. /// Get ASCII-Like string to first string-terminator, example:
  1322. /// "Some string \0 Some Data \0" - > "Some string".
  1323. /// </summary>
  1324. /// <returns>String to first string-terminator.</returns>
  1325. public string GetString()
  1326. {
  1327. String Str = Encoding.ASCII.GetString(ToBytes());
  1328. for (int i = 1; i < 4; i++)
  1329. Str = Str.Insert((StrNLen + 1) * i + i - 1, "\n");
  1330. return Str.Replace("\0", String.Empty).TrimEnd(new char[] { '\n' });
  1331. }
  1332. /// <summary>
  1333. /// Convert <see cref="TgaComment"/> to byte array.
  1334. /// </summary>
  1335. /// <returns>Byte array, every byte is ASCII symbol.</returns>
  1336. public byte[] ToBytes()
  1337. {
  1338. return ToBytes(origString, blankSpaceChar);
  1339. }
  1340. /// <summary>
  1341. /// Convert <see cref="TgaComment"/> to byte array.
  1342. /// </summary>
  1343. /// <param name="Str">Input string.</param>
  1344. /// <param name="BlankSpaceChar">Char for filling blank space in string.</param>
  1345. /// <returns>Byte array, every byte is ASCII symbol.</returns>
  1346. public static byte[] ToBytes(string Str, char BlankSpaceChar = '\0')
  1347. {
  1348. char[] C = new char[81 * 4];
  1349. for (int i = 0; i < C.Length; i++)
  1350. {
  1351. if ((i + 82) % 81 == 0)
  1352. C[i] = TgaString.DefaultEndingChar;
  1353. else
  1354. {
  1355. int Index = i - i / 81;
  1356. C[i] = (Index < Str.Length ? Str[Index] : BlankSpaceChar);
  1357. }
  1358. }
  1359. return Encoding.ASCII.GetBytes(C);
  1360. }
  1361. }
  1362. public class TgaDateTime : ICloneable
  1363. {
  1364. ushort month = 0;
  1365. ushort day = 0;
  1366. ushort year = 0;
  1367. ushort hour = 0;
  1368. ushort minute = 0;
  1369. ushort second = 0;
  1370. /// <summary>
  1371. /// Make empty <see cref="TgaDateTime"/>.
  1372. /// </summary>
  1373. public TgaDateTime()
  1374. {
  1375. }
  1376. /// <summary>
  1377. /// Make <see cref="TgaDateTime"/> from <see cref="DateTime"/>.
  1378. /// </summary>
  1379. /// <param name="DateAndTime">Some <see cref="DateTime"/> variable.</param>
  1380. public TgaDateTime(DateTime DateAndTime)
  1381. {
  1382. month = (ushort)DateAndTime.Month;
  1383. day = (ushort)DateAndTime.Day;
  1384. year = (ushort)DateAndTime.Year;
  1385. hour = (ushort)DateAndTime.Hour;
  1386. minute = (ushort)DateAndTime.Minute;
  1387. second = (ushort)DateAndTime.Second;
  1388. }
  1389. /// <summary>
  1390. /// Make <see cref="TgaDateTime"/> from ushort values.
  1391. /// </summary>
  1392. /// <param name="Month">Month (1 - 12).</param>
  1393. /// <param name="Day">Day (1 - 31).</param>
  1394. /// <param name="Year">Year (4 digit, ie. 1989).</param>
  1395. /// <param name="Hour">Hour (0 - 23).</param>
  1396. /// <param name="Minute">Minute (0 - 59).</param>
  1397. /// <param name="Second">Second (0 - 59).</param>
  1398. public TgaDateTime(ushort Month, ushort Day, ushort Year, ushort Hour, ushort Minute, ushort Second)
  1399. {
  1400. month = Month;
  1401. day = Day;
  1402. year = Year;
  1403. hour = Hour;
  1404. minute = Minute;
  1405. second = Second;
  1406. }
  1407. /// <summary>
  1408. /// Make <see cref="TgaDateTime"/> from bytes.
  1409. /// </summary>
  1410. /// <param name="Bytes">Array of bytes(byte[12]).</param>
  1411. public TgaDateTime(byte[] Bytes)
  1412. {
  1413. if (Bytes == null)
  1414. throw new ArgumentNullException(nameof(Bytes) + " = null!");
  1415. else if (Bytes.Length != Size)
  1416. throw new ArgumentOutOfRangeException(nameof(Bytes) + " must be equal " + Size + "!");
  1417. month = BitConverter.ToUInt16(Bytes, 0);
  1418. day = BitConverter.ToUInt16(Bytes, 2);
  1419. year = BitConverter.ToUInt16(Bytes, 4);
  1420. hour = BitConverter.ToUInt16(Bytes, 6);
  1421. minute = BitConverter.ToUInt16(Bytes, 8);
  1422. second = BitConverter.ToUInt16(Bytes, 10);
  1423. }
  1424. /// <summary>
  1425. /// Gets or Sets month (1 - 12).
  1426. /// </summary>
  1427. public ushort Month
  1428. {
  1429. get { return month; }
  1430. set { month = value; }
  1431. }
  1432. /// <summary>
  1433. /// Gets or Sets day (1 - 31).
  1434. /// </summary>
  1435. public ushort Day
  1436. {
  1437. get { return day; }
  1438. set { day = value; }
  1439. }
  1440. /// <summary>
  1441. /// Gets or Sets year (4 digit, ie. 1989).
  1442. /// </summary>
  1443. public ushort Year
  1444. {
  1445. get { return year; }
  1446. set { year = value; }
  1447. }
  1448. /// <summary>
  1449. /// Gets or Sets hour (0 - 23).
  1450. /// </summary>
  1451. public ushort Hour
  1452. {
  1453. get { return hour; }
  1454. set { hour = value; }
  1455. }
  1456. /// <summary>
  1457. /// Gets or Sets minute (0 - 59).
  1458. /// </summary>
  1459. public ushort Minute
  1460. {
  1461. get { return minute; }
  1462. set { minute = value; }
  1463. }
  1464. /// <summary>
  1465. /// Gets or Sets second (0 - 59).
  1466. /// </summary>
  1467. public ushort Second
  1468. {
  1469. get { return second; }
  1470. set { second = value; }
  1471. }
  1472. /// <summary>
  1473. /// Gets TGA Field size in bytes.
  1474. /// </summary>
  1475. public const int Size = 12;
  1476. /// <summary>
  1477. /// Make full independed copy of <see cref="TgaDateTime"/>.
  1478. /// </summary>
  1479. /// <returns>Copy of <see cref="TgaDateTime"/></returns>
  1480. public TgaDateTime Clone()
  1481. {
  1482. return new TgaDateTime(month, day, year, hour, minute, second);
  1483. }
  1484. /// <summary>
  1485. /// Make full independed copy of <see cref="TgaDateTime"/>.
  1486. /// </summary>
  1487. /// <returns>Copy of <see cref="TgaDateTime"/></returns>
  1488. object ICloneable.Clone()
  1489. {
  1490. return Clone();
  1491. }
  1492. public override bool Equals(object obj)
  1493. {
  1494. return ((obj is TgaDateTime) ? Equals((TgaDateTime)obj) : false);
  1495. }
  1496. public bool Equals(TgaDateTime item)
  1497. {
  1498. return (
  1499. month == item.month &&
  1500. day == item.day &&
  1501. year == item.year &&
  1502. hour == item.hour &&
  1503. minute == item.minute &&
  1504. second == item.second);
  1505. }
  1506. public static bool operator ==(TgaDateTime item1, TgaDateTime item2)
  1507. {
  1508. if (ReferenceEquals(item1, null))
  1509. return ReferenceEquals(item2, null);
  1510. if (ReferenceEquals(item2, null))
  1511. return ReferenceEquals(item1, null);
  1512. return item1.Equals(item2);
  1513. }
  1514. public static bool operator !=(TgaDateTime item1, TgaDateTime item2)
  1515. {
  1516. return !(item1 == item2);
  1517. }
  1518. public override int GetHashCode()
  1519. {
  1520. unchecked
  1521. {
  1522. int hash = 17;
  1523. hash = hash * 23 + (month << 16 | hour).GetHashCode();
  1524. hash = hash * 23 + (day << 16 | minute).GetHashCode();
  1525. hash = hash * 23 + (year << 16 | second).GetHashCode();
  1526. return hash;
  1527. }
  1528. }
  1529. /// <summary>
  1530. /// Gets <see cref="TgaDateTime"/> like string.
  1531. /// </summary>
  1532. /// <returns>String in "1990.01.23 1:02:03" format.</returns>
  1533. public override string ToString()
  1534. {
  1535. return String.Format("{0:D4}.{1:D2}.{2:D2} {3}:{4:D2}:{5:D2}", year, month, day, hour, minute, second);
  1536. }
  1537. /// <summary>
  1538. /// Convert <see cref="TgaDateTime"/> to byte array.
  1539. /// </summary>
  1540. /// <returns>Byte array with length = 12.</returns>
  1541. public byte[] ToBytes()
  1542. {
  1543. return BitConverterExt.ToBytes(month, day, year, hour, minute, second);
  1544. }
  1545. /// <summary>
  1546. /// Gets <see cref="TgaDateTime"/> like <see cref="DateTime"/>.
  1547. /// </summary>
  1548. /// <returns><see cref="DateTime"/> value of <see cref="TgaDateTime"/>.</returns>
  1549. public DateTime ToDateTime()
  1550. {
  1551. return new DateTime(year, month, day, hour, minute, second);
  1552. }
  1553. }
  1554. public class TgaDevEntry : ICloneable
  1555. {
  1556. // Directory
  1557. ushort fieldTag = 0;
  1558. uint fieldFileOffset = 0;
  1559. // Field
  1560. byte[] data = null;
  1561. /// <summary>
  1562. /// Make empty <see cref="TgaDevEntry"/>.
  1563. /// </summary>
  1564. public TgaDevEntry()
  1565. {
  1566. }
  1567. /// <summary>
  1568. /// Make <see cref="TgaDevEntry"/> from other <see cref="TgaDevEntry"/>.
  1569. /// </summary>
  1570. /// <param name="Entry">Some <see cref="TgaDevEntry"/> variable.</param>
  1571. public TgaDevEntry(TgaDevEntry Entry)
  1572. {
  1573. if (Entry == null)
  1574. throw new ArgumentNullException();
  1575. fieldTag = Entry.fieldTag;
  1576. fieldFileOffset = Entry.fieldFileOffset;
  1577. data = BitConverterExt.ToBytes(Entry.data);
  1578. }
  1579. /// <summary>
  1580. /// Make <see cref="TgaDevEntry"/> from <see cref="Tag"/>, <see cref="Offset"/> and <see cref="FieldSize"/>.
  1581. /// </summary>
  1582. /// <param name="Tag">TAG ID (0 - 65535). See <see cref="Tag"/>.</param>
  1583. /// <param name="Offset">TAG file offset in bytes. See <see cref="Offset"/>.</param>
  1584. /// <param name="Data">This is DevEntry Field Data. See <see cref="Data"/>.</param>
  1585. public TgaDevEntry(ushort Tag, uint Offset, byte[] Data = null)
  1586. {
  1587. fieldTag = Tag;
  1588. fieldFileOffset = Offset;
  1589. data = Data;
  1590. }
  1591. /// <summary>
  1592. /// Make <see cref="TgaDevEntry"/> from bytes.
  1593. /// </summary>
  1594. /// <param name="Bytes">Array of bytes(byte[6] or bigger, if <see cref="Data"/> exist).</param>
  1595. public TgaDevEntry(byte[] Bytes)
  1596. {
  1597. if (Bytes == null)
  1598. throw new ArgumentNullException(nameof(Bytes) + " = null!");
  1599. else if (Bytes.Length < 6)
  1600. throw new ArgumentOutOfRangeException(nameof(Bytes) + " must be >= 6!");
  1601. fieldTag = BitConverter.ToUInt16(Bytes, 0);
  1602. fieldFileOffset = BitConverter.ToUInt32(Bytes, 2);
  1603. if (Bytes.Length > 6)
  1604. data = BitConverterExt.GetElements(Bytes, 6, Bytes.Length - 6);
  1605. }
  1606. /// <summary>
  1607. /// Each TAG is a value in the range of 0 to 65535. Values from 0 - 32767 are available for developer use,
  1608. /// while values from 32768 - 65535 are reserved for Truevision.
  1609. /// </summary>
  1610. public ushort Tag
  1611. {
  1612. get { return fieldTag; }
  1613. set { fieldTag = value; }
  1614. }
  1615. /// <summary>
  1616. /// This OFFSET is a number of bytes from the beginning of the file to the start of the field
  1617. /// referenced by the tag.
  1618. /// </summary>
  1619. public uint Offset
  1620. {
  1621. get { return fieldFileOffset; }
  1622. set { fieldFileOffset = value; }
  1623. }
  1624. /// <summary>
  1625. /// Field DATA.
  1626. /// Although the size and format of the actual Developer Area fields are totally up to the developer,
  1627. /// please define your formats to address future considerations you might have concerning your fields.
  1628. /// This means that if you anticipate changing a field, build flexibility into the format to make these
  1629. /// changes easy on other developers.Major changes to an existing TAG’s definition should never happen.
  1630. /// </summary>
  1631. public byte[] Data
  1632. {
  1633. get { return data; }
  1634. set { data = value; }
  1635. }
  1636. /// <summary>
  1637. /// The FIELD SIZE is a number of bytes in the field. Same like: <see cref="Data.Length"/>,
  1638. /// if <see cref="Data"/> is null, return -1.
  1639. /// </summary>
  1640. public int FieldSize
  1641. {
  1642. get
  1643. {
  1644. if (Data == null)
  1645. return -1;
  1646. return Data.Length;
  1647. }
  1648. }
  1649. /// <summary>
  1650. /// Gets TGA <see cref="TgaDevEntry"/> size in bytes (Always constant and equal 10!).
  1651. /// It is not <see cref="FieldSize"/>! It is just size of entry sizeof(ushort + uint + uint).
  1652. /// </summary>
  1653. public const int Size = 10;
  1654. /// <summary>
  1655. /// Make full independed copy of <see cref="TgaDevEntry"/>.
  1656. /// </summary>
  1657. /// <returns>Copy of <see cref="TgaDevEntry"/></returns>
  1658. public TgaDevEntry Clone()
  1659. {
  1660. return new TgaDevEntry(this);
  1661. }
  1662. /// <summary>
  1663. /// Make full independed copy of <see cref="TgaDevEntry"/>.
  1664. /// </summary>
  1665. /// <returns>Copy of <see cref="TgaDevEntry"/></returns>
  1666. object ICloneable.Clone()
  1667. {
  1668. return Clone();
  1669. }
  1670. public override bool Equals(object obj)
  1671. {
  1672. return ((obj is TgaDevEntry) ? Equals((TgaDevEntry)obj) : false);
  1673. }
  1674. public bool Equals(TgaDevEntry item)
  1675. {
  1676. return (fieldTag == item.fieldTag &&
  1677. fieldFileOffset == item.fieldFileOffset &&
  1678. BitConverterExt.IsArraysEqual(data, item.data));
  1679. }
  1680. public static bool operator ==(TgaDevEntry item1, TgaDevEntry item2)
  1681. {
  1682. if (ReferenceEquals(item1, null))
  1683. return ReferenceEquals(item2, null);
  1684. if (ReferenceEquals(item2, null))
  1685. return ReferenceEquals(item1, null);
  1686. return item1.Equals(item2);
  1687. }
  1688. public static bool operator !=(TgaDevEntry item1, TgaDevEntry item2)
  1689. {
  1690. return !(item1 == item2);
  1691. }
  1692. public override int GetHashCode()
  1693. {
  1694. unchecked
  1695. {
  1696. int hash = 17;
  1697. hash = hash * 23 + fieldTag.GetHashCode();
  1698. hash = hash * 23 + fieldFileOffset.GetHashCode();
  1699. if (data != null)
  1700. for (int i = 0; i < data.Length; i++)
  1701. hash = hash * 23 + data[i].GetHashCode();
  1702. return hash;
  1703. }
  1704. }
  1705. /// <summary>
  1706. /// Gets <see cref="TgaDevEntry"/> like string.
  1707. /// </summary>
  1708. /// <returns>String in "Tag={0}, Offset={1}, FieldSize={2}" format.</returns>
  1709. public override string ToString()
  1710. {
  1711. return String.Format("{0}={1}, {1}={2}, {3}={4}", nameof(Tag), fieldTag,
  1712. nameof(Offset), fieldFileOffset, nameof(FieldSize), FieldSize);
  1713. }
  1714. /// <summary>
  1715. /// Convert <see cref="TgaDevEntry"/> to byte array. (Not include <see cref="Data"/>!).
  1716. /// </summary>
  1717. /// <returns>Byte array with length = 10.</returns>
  1718. public byte[] ToBytes()
  1719. {
  1720. return BitConverterExt.ToBytes(fieldTag, fieldFileOffset, (data == null ? 0 : data.Length));
  1721. }
  1722. } //Not full ToBytes()
  1723. public class TgaFraction : ICloneable
  1724. {
  1725. ushort numerator = 0;
  1726. ushort denominator = 0;
  1727. /// <summary>
  1728. /// Make <see cref="TgaFraction"/> from <see cref="Numerator"/> and <see cref="Denominator"/>.
  1729. /// </summary>
  1730. /// <param name="Numerator">Numerator value.</param>
  1731. /// <param name="Denominator">Denominator value.</param>
  1732. public TgaFraction(ushort Numerator = 0, ushort Denominator = 0)
  1733. {
  1734. numerator = Numerator;
  1735. denominator = Denominator;
  1736. }
  1737. /// <summary>
  1738. /// Make <see cref="TgaFraction"/> from bytes.
  1739. /// </summary>
  1740. /// <param name="Bytes">Array of bytes(byte[4]).</param>
  1741. public TgaFraction(byte[] Bytes)
  1742. {
  1743. if (Bytes == null)
  1744. throw new ArgumentNullException(nameof(Bytes) + " = null!");
  1745. if (Bytes.Length != Size)
  1746. throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
  1747. numerator = BitConverter.ToUInt16(Bytes, 0);
  1748. denominator = BitConverter.ToUInt16(Bytes, 2);
  1749. }
  1750. /// <summary>
  1751. /// Gets or sets numerator value.
  1752. /// </summary>
  1753. public ushort Numerator
  1754. {
  1755. get { return numerator; }
  1756. set { numerator = value; }
  1757. }
  1758. /// <summary>
  1759. /// Gets or sets denominator value.
  1760. /// </summary>
  1761. public ushort Denominator
  1762. {
  1763. get { return denominator; }
  1764. set { denominator = value; }
  1765. }
  1766. /// <summary>
  1767. /// Get aspect ratio = <see cref="Numerator"/> / <see cref="Denominator"/>.
  1768. /// </summary>
  1769. public float AspectRatio
  1770. {
  1771. get
  1772. {
  1773. if (numerator == denominator)
  1774. return 1f;
  1775. return numerator / (float)denominator;
  1776. }
  1777. }
  1778. /// <summary>
  1779. /// Gets Empty <see cref="TgaFraction"/>, all values are 0.
  1780. /// </summary>
  1781. public static readonly TgaFraction Empty = new TgaFraction();
  1782. /// <summary>
  1783. /// Gets One <see cref="TgaFraction"/>, all values are 1 (ones, 1 / 1 = 1).
  1784. /// </summary>
  1785. public static readonly TgaFraction One = new TgaFraction(1, 1);
  1786. /// <summary>
  1787. /// Gets TGA Field size in bytes.
  1788. /// </summary>
  1789. public const int Size = 4;
  1790. /// <summary>
  1791. /// Make full independed copy of <see cref="TgaFraction"/>.
  1792. /// </summary>
  1793. /// <returns>Copy of <see cref="TgaFraction"/></returns>
  1794. public TgaFraction Clone()
  1795. {
  1796. return new TgaFraction(numerator, denominator);
  1797. }
  1798. /// <summary>
  1799. /// Make full independed copy of <see cref="TgaFraction"/>.
  1800. /// </summary>
  1801. /// <returns>Copy of <see cref="TgaFraction"/></returns>
  1802. object ICloneable.Clone()
  1803. {
  1804. return Clone();
  1805. }
  1806. public override bool Equals(object obj)
  1807. {
  1808. return ((obj is TgaFraction) ? Equals((TgaFraction)obj) : false);
  1809. }
  1810. public bool Equals(TgaFraction item)
  1811. {
  1812. return (numerator == item.numerator && denominator == item.denominator);
  1813. }
  1814. public static bool operator ==(TgaFraction item1, TgaFraction item2)
  1815. {
  1816. if (ReferenceEquals(item1, null))
  1817. return ReferenceEquals(item2, null);
  1818. if (ReferenceEquals(item2, null))
  1819. return ReferenceEquals(item1, null);
  1820. return item1.Equals(item2);
  1821. }
  1822. public static bool operator !=(TgaFraction item1, TgaFraction item2)
  1823. {
  1824. return !(item1 == item2);
  1825. }
  1826. public override int GetHashCode()
  1827. {
  1828. return (numerator << 16 | denominator).GetHashCode();
  1829. }
  1830. /// <summary>
  1831. /// Gets <see cref="TgaFraction"/> like string.
  1832. /// </summary>
  1833. /// <returns>String in "Numerator=1, Denominator=2" format.</returns>
  1834. public override string ToString()
  1835. {
  1836. return String.Format("{0}={1}, {2}={3}", nameof(Numerator), numerator,
  1837. nameof(Denominator), denominator);
  1838. }
  1839. /// <summary>
  1840. /// Convert <see cref="TgaFraction"/> to byte array.
  1841. /// </summary>
  1842. /// <returns>Byte array with length = 4.</returns>
  1843. public byte[] ToBytes()
  1844. {
  1845. return BitConverterExt.ToBytes(numerator, denominator);
  1846. }
  1847. }
  1848. /// <summary>
  1849. /// Contains image origin bits and alpha channel bits(or number of overlay bits)
  1850. /// </summary>
  1851. public class TgaImageDescriptor : ICloneable
  1852. {
  1853. TgaImgOrigin imageOrigin = 0; //bits 5-4
  1854. byte alphaChannelBits = 0; //bits 3-0
  1855. /// <summary>
  1856. /// Make empty <see cref="TgaImageDescriptor"/>.
  1857. /// </summary>
  1858. public TgaImageDescriptor()
  1859. {
  1860. }
  1861. /// <summary>
  1862. /// Make <see cref="TgaImageDescriptor"/> from bytes.
  1863. /// </summary>
  1864. /// <param name="b">ImageDescriptor byte with reserved 7-6 bits, bits 5-4 used for
  1865. /// <see cref="ImageOrigin"/>, 3-0 used as alpha channel bits or number of overlay bits.</param>
  1866. public TgaImageDescriptor(byte b)
  1867. {
  1868. imageOrigin = (TgaImgOrigin)((b & 0x30) >> 4);
  1869. alphaChannelBits = (byte)(b & 0x0F);
  1870. }
  1871. /// <summary>
  1872. /// Gets or Sets Image Origin bits (select from enum only, don'n use 5-4 bits!).
  1873. /// </summary>
  1874. public TgaImgOrigin ImageOrigin
  1875. {
  1876. get { return imageOrigin; }
  1877. set { imageOrigin = value; }
  1878. }
  1879. /// <summary>
  1880. /// Gets or Sets alpha channel bits or number of overlay bits.
  1881. /// </summary>
  1882. public byte AlphaChannelBits
  1883. {
  1884. get { return alphaChannelBits; }
  1885. set { alphaChannelBits = value; }
  1886. }
  1887. /// <summary>
  1888. /// Gets TGA Field size in bytes.
  1889. /// </summary>
  1890. public const int Size = 1;
  1891. /// <summary>
  1892. /// Make full copy of <see cref="TgaImageDescriptor"/>.
  1893. /// </summary>
  1894. /// <returns>Full independent copy of <see cref="TgaImageDescriptor"/>.</returns>
  1895. public TgaImageDescriptor Clone()
  1896. {
  1897. return new TgaImageDescriptor(ToByte());
  1898. }
  1899. /// <summary>
  1900. /// Make full copy of <see cref="TgaImageDescriptor"/>.
  1901. /// </summary>
  1902. /// <returns>Full independent copy of <see cref="TgaImageDescriptor"/>.</returns>
  1903. object ICloneable.Clone()
  1904. {
  1905. return Clone();
  1906. }
  1907. public override bool Equals(object obj)
  1908. {
  1909. return ((obj is TgaImageDescriptor) ? Equals((TgaImageDescriptor)obj) : false);
  1910. }
  1911. public bool Equals(TgaImageDescriptor item)
  1912. {
  1913. return (imageOrigin == item.imageOrigin && alphaChannelBits == item.alphaChannelBits);
  1914. }
  1915. public static bool operator ==(TgaImageDescriptor item1, TgaImageDescriptor item2)
  1916. {
  1917. if (ReferenceEquals(item1, null))
  1918. return ReferenceEquals(item2, null);
  1919. if (ReferenceEquals(item2, null))
  1920. return ReferenceEquals(item1, null);
  1921. return item1.Equals(item2);
  1922. }
  1923. public static bool operator !=(TgaImageDescriptor item1, TgaImageDescriptor item2)
  1924. {
  1925. return !(item1 == item2);
  1926. }
  1927. public override int GetHashCode()
  1928. {
  1929. unchecked
  1930. {
  1931. return ((int)ImageOrigin << 4 | alphaChannelBits).GetHashCode();
  1932. }
  1933. }
  1934. public override string ToString()
  1935. {
  1936. return String.Format("{0}={1}, {2}={3}, ImageDescriptor_AsByte={4}", nameof(ImageOrigin),
  1937. imageOrigin, nameof(AlphaChannelBits), alphaChannelBits, ToByte());
  1938. }
  1939. /// <summary>
  1940. /// Gets ImageDescriptor byte.
  1941. /// </summary>
  1942. /// <returns>ImageDescriptor byte with reserved 7-6 bits, bits 5-4 used for imageOrigin,
  1943. /// 3-0 used as alpha channel bits or number of overlay bits.</returns>
  1944. public byte ToByte()
  1945. {
  1946. return (byte)(((int)imageOrigin << 4) | alphaChannelBits);
  1947. }
  1948. }
  1949. /// <summary>
  1950. /// Image Specification - Field 5 (10 bytes):
  1951. /// <para>This field and its sub-fields describe the image screen location, size and pixel depth.
  1952. /// These information is always written to the file.</para>
  1953. /// </summary>
  1954. public class TgaImageSpec : ICloneable
  1955. {
  1956. ushort x_Origin = 0;
  1957. ushort y_Origin = 0;
  1958. ushort imageWidth = 0;
  1959. ushort imageHeight = 0;
  1960. TgaPixelDepth pixelDepth = TgaPixelDepth.Other;
  1961. TgaImageDescriptor imageDescriptor = new TgaImageDescriptor();
  1962. public TgaImageSpec()
  1963. {
  1964. }
  1965. /// <summary>
  1966. /// Make ImageSpec from values.
  1967. /// </summary>
  1968. /// <param name="X_Origin">These specify the absolute horizontal coordinate for the lower
  1969. /// left corner of the image as it is positioned on a display device having an origin at
  1970. /// the lower left of the screen(e.g., the TARGA series).</param>
  1971. /// <param name="Y_Origin">These specify the absolute vertical coordinate for the lower
  1972. /// left corner of the image as it is positioned on a display device having an origin at
  1973. /// the lower left of the screen(e.g., the TARGA series).</param>
  1974. /// <param name="ImageWidth">This field specifies the width of the image in pixels.</param>
  1975. /// <param name="ImageHeight">This field specifies the height of the image in pixels.</param>
  1976. /// <param name="PixelDepth">This field indicates the number of bits per pixel. This number
  1977. /// includes the Attribute or Alpha channel bits. Common values are 8, 16, 24 and 32 but
  1978. /// other pixel depths could be used.</param>
  1979. /// <param name="ImageDescriptor">Contains image origin bits and alpha channel bits
  1980. /// (or number of overlay bits).</param>
  1981. public TgaImageSpec(ushort X_Origin, ushort Y_Origin, ushort ImageWidth, ushort ImageHeight,
  1982. TgaPixelDepth PixelDepth, TgaImageDescriptor ImageDescriptor)
  1983. {
  1984. x_Origin = X_Origin;
  1985. y_Origin = Y_Origin;
  1986. imageWidth = ImageWidth;
  1987. imageHeight = ImageHeight;
  1988. pixelDepth = PixelDepth;
  1989. imageDescriptor = ImageDescriptor;
  1990. }
  1991. /// <summary>
  1992. /// Make ImageSpec from bytes.
  1993. /// </summary>
  1994. /// <param name="Bytes">Array of bytes(byte[10]).</param>
  1995. public TgaImageSpec(byte[] Bytes)
  1996. {
  1997. if (Bytes == null)
  1998. throw new ArgumentNullException(nameof(Bytes) + " = null!");
  1999. if (Bytes.Length != Size)
  2000. throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
  2001. x_Origin = BitConverter.ToUInt16(Bytes, 0);
  2002. y_Origin = BitConverter.ToUInt16(Bytes, 2);
  2003. imageWidth = BitConverter.ToUInt16(Bytes, 4);
  2004. imageHeight = BitConverter.ToUInt16(Bytes, 6);
  2005. pixelDepth = (TgaPixelDepth)Bytes[8];
  2006. imageDescriptor = new TgaImageDescriptor(Bytes[9]);
  2007. }
  2008. /// <summary>
  2009. /// These specify the absolute horizontal coordinate for the lower left corner of the image
  2010. /// as it is positioned on a display device having an origin at the lower left of the
  2011. /// screen(e.g., the TARGA series).
  2012. /// </summary>
  2013. public ushort X_Origin
  2014. {
  2015. get { return x_Origin; }
  2016. set { x_Origin = value; }
  2017. }
  2018. /// <summary>
  2019. /// These specify the absolute vertical coordinate for the lower left corner of the image
  2020. /// as it is positioned on a display device having an origin at the lower left of the
  2021. /// screen(e.g., the TARGA series).
  2022. /// </summary>
  2023. public ushort Y_Origin
  2024. {
  2025. get { return y_Origin; }
  2026. set { y_Origin = value; }
  2027. }
  2028. /// <summary>
  2029. /// This field specifies the width of the image in pixels.
  2030. /// </summary>
  2031. public ushort ImageWidth
  2032. {
  2033. get { return imageWidth; }
  2034. set { imageWidth = value; }
  2035. }
  2036. /// <summary>
  2037. /// This field specifies the height of the image in pixels.
  2038. /// </summary>
  2039. public ushort ImageHeight
  2040. {
  2041. get { return imageHeight; }
  2042. set { imageHeight = value; }
  2043. }
  2044. /// <summary>
  2045. /// This field indicates the number of bits per pixel. This number includes the Attribute or
  2046. /// Alpha channel bits. Common values are 8, 16, 24 and 32 but other pixel depths could be used.
  2047. /// </summary>
  2048. public TgaPixelDepth PixelDepth
  2049. {
  2050. get { return pixelDepth; }
  2051. set { pixelDepth = value; }
  2052. }
  2053. /// <summary>
  2054. /// Contains image origin bits and alpha channel bits(or number of overlay bits).
  2055. /// </summary>
  2056. public TgaImageDescriptor ImageDescriptor
  2057. {
  2058. get { return imageDescriptor; }
  2059. set { imageDescriptor = value; }
  2060. }
  2061. /// <summary>
  2062. /// Gets TGA Field size in bytes.
  2063. /// </summary>
  2064. public const int Size = 10;
  2065. /// <summary>
  2066. /// Make full copy of <see cref="TgaImageDescriptor"/>.
  2067. /// </summary>
  2068. /// <returns></returns>
  2069. public TgaImageSpec Clone()
  2070. {
  2071. return new TgaImageSpec(ToBytes());
  2072. }
  2073. /// <summary>
  2074. /// Make full copy of <see cref="TgaImageDescriptor"/>.
  2075. /// </summary>
  2076. /// <returns></returns>
  2077. object ICloneable.Clone()
  2078. {
  2079. return Clone();
  2080. }
  2081. public override bool Equals(object obj)
  2082. {
  2083. return ((obj is TgaImageSpec) ? Equals((TgaImageSpec)obj) : false);
  2084. }
  2085. public bool Equals(TgaImageSpec item)
  2086. {
  2087. return (
  2088. x_Origin == item.x_Origin &&
  2089. y_Origin == item.y_Origin &&
  2090. imageWidth == item.imageWidth &&
  2091. imageHeight == item.imageHeight &&
  2092. pixelDepth == item.pixelDepth &&
  2093. imageDescriptor == item.imageDescriptor);
  2094. }
  2095. public static bool operator ==(TgaImageSpec item1, TgaImageSpec item2)
  2096. {
  2097. if (ReferenceEquals(item1, null))
  2098. return ReferenceEquals(item2, null);
  2099. if (ReferenceEquals(item2, null))
  2100. return ReferenceEquals(item1, null);
  2101. return item1.Equals(item2);
  2102. }
  2103. public static bool operator !=(TgaImageSpec item1, TgaImageSpec item2)
  2104. {
  2105. return !(item1 == item2);
  2106. }
  2107. public override int GetHashCode()
  2108. {
  2109. unchecked
  2110. {
  2111. int hash = 17;
  2112. hash = hash * 23 + x_Origin.GetHashCode();
  2113. hash = hash * 23 + y_Origin.GetHashCode();
  2114. hash = hash * 23 + imageWidth.GetHashCode();
  2115. hash = hash * 23 + imageHeight.GetHashCode();
  2116. hash = hash * 23 + pixelDepth.GetHashCode();
  2117. if (imageDescriptor != null)
  2118. hash = hash * 23 + imageDescriptor.GetHashCode();
  2119. return hash;
  2120. }
  2121. }
  2122. public override string ToString()
  2123. {
  2124. return String.Format("{0}={1}, {2}={3}, {4}={5}, {6}={7}, {8}={9}, {10}={11}",
  2125. nameof(X_Origin), x_Origin,
  2126. nameof(Y_Origin), y_Origin,
  2127. nameof(ImageWidth), imageWidth,
  2128. nameof(ImageHeight), imageHeight,
  2129. nameof(PixelDepth), pixelDepth,
  2130. nameof(ImageDescriptor), imageDescriptor);
  2131. }
  2132. /// <summary>
  2133. /// Convert <see cref="TgaImageSpec"/> to byte array.
  2134. /// </summary>
  2135. /// <returns>Byte array with length = 10.</returns>
  2136. public byte[] ToBytes()
  2137. {
  2138. return BitConverterExt.ToBytes(x_Origin, y_Origin, imageWidth, imageHeight,
  2139. (byte)pixelDepth, (imageDescriptor == null ? byte.MinValue : imageDescriptor.ToByte()));
  2140. }
  2141. }
  2142. /// <summary>
  2143. /// Postage Stamp Image (MaxSize 64x64, uncompressed, PixelDepth like in full image).
  2144. /// </summary>
  2145. public class TgaPostageStampImage : ICloneable
  2146. {
  2147. byte width = 0;
  2148. byte height = 0;
  2149. byte[] data = null;
  2150. public TgaPostageStampImage()
  2151. {
  2152. }
  2153. /// <summary>
  2154. /// Make <see cref="TgaPostageStampImage"/> from bytes array.
  2155. /// </summary>
  2156. /// <param name="Bytes">Bytes array, first 2 bytes are <see cref="Width"/> and <see cref="Height"/>,
  2157. /// next bytes - image data.</param>
  2158. public TgaPostageStampImage(byte[] Bytes)
  2159. {
  2160. if (Bytes == null)
  2161. throw new ArgumentNullException(nameof(Bytes) + " = null!");
  2162. if (Bytes.Length < 2)
  2163. throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be >= " + 2 + "!");
  2164. width = Bytes[0];
  2165. height = Bytes[1];
  2166. if (Bytes.Length > 2)
  2167. data = BitConverterExt.GetElements(Bytes, 2, Bytes.Length - 2);
  2168. }
  2169. /// <summary>
  2170. /// Make <see cref="TgaPostageStampImage"/> from bytes and size.
  2171. /// </summary>
  2172. /// <param name="Width">Image Width.</param>
  2173. /// <param name="Height">Image Height.</param>
  2174. /// <param name="Bytes">Postage Stamp Image Data.</param>
  2175. public TgaPostageStampImage(byte Width, byte Height, byte[] Bytes)
  2176. {
  2177. if (Bytes == null)
  2178. throw new ArgumentNullException(nameof(Bytes) + " = null!");
  2179. width = Width;
  2180. height = Height;
  2181. data = Bytes;
  2182. }
  2183. /// <summary>
  2184. /// Postage Stamp Image Data
  2185. /// </summary>
  2186. public byte[] Data
  2187. {
  2188. get { return data; }
  2189. set { data = value; }
  2190. }
  2191. /// <summary>
  2192. /// Postage Stamp Image Width (maximum = 64).
  2193. /// </summary>
  2194. public byte Width
  2195. {
  2196. get { return width; }
  2197. set { width = value; }
  2198. }
  2199. /// <summary>
  2200. /// Postage Stamp Image Height (maximum = 64).
  2201. /// </summary>
  2202. public byte Height
  2203. {
  2204. get { return height; }
  2205. set { height = value; }
  2206. }
  2207. /// <summary>
  2208. /// Make full copy of <see cref="TgaPostageStampImage"/>.
  2209. /// </summary>
  2210. /// <returns>Full independent copy of <see cref="TgaPostageStampImage"/>.</returns>
  2211. public TgaPostageStampImage Clone()
  2212. {
  2213. return new TgaPostageStampImage(width, height, BitConverterExt.ToBytes(data));
  2214. }
  2215. /// <summary>
  2216. /// Make full copy of <see cref="TgaPostageStampImage"/>.
  2217. /// </summary>
  2218. /// <returns>Full independent copy of <see cref="TgaPostageStampImage"/>.</returns>
  2219. object ICloneable.Clone()
  2220. {
  2221. return Clone();
  2222. }
  2223. public override bool Equals(object obj)
  2224. {
  2225. return ((obj is TgaPostageStampImage) ? Equals((TgaPostageStampImage)obj) : false);
  2226. }
  2227. public bool Equals(TgaPostageStampImage item)
  2228. {
  2229. return width == item.width && height == item.height && BitConverterExt.IsArraysEqual(data, item.data);
  2230. }
  2231. public static bool operator ==(TgaPostageStampImage item1, TgaPostageStampImage item2)
  2232. {
  2233. if (ReferenceEquals(item1, null))
  2234. return ReferenceEquals(item2, null);
  2235. if (ReferenceEquals(item2, null))
  2236. return ReferenceEquals(item1, null);
  2237. return item1.Equals(item2);
  2238. }
  2239. public static bool operator !=(TgaPostageStampImage item1, TgaPostageStampImage item2)
  2240. {
  2241. return !(item1 == item2);
  2242. }
  2243. public override int GetHashCode()
  2244. {
  2245. unchecked
  2246. {
  2247. int hash = 27;
  2248. hash = (13 * hash) + width.GetHashCode();
  2249. hash = (13 * hash) + height.GetHashCode();
  2250. if (data != null)
  2251. for (int i = 0; i < data.Length; i++)
  2252. hash = (13 * hash) + data[i].GetHashCode();
  2253. return hash;
  2254. }
  2255. }
  2256. public override string ToString()
  2257. {
  2258. return String.Format("{0}={1}, {2}={3}, DataLength={4}",
  2259. nameof(Width), width, nameof(Height), height, (data == null ? -1 : data.Length));
  2260. }
  2261. /// <summary>
  2262. /// Convert <see cref="TgaPostageStampImage"/> to byte array.
  2263. /// </summary>
  2264. /// <returns>Byte array.</returns>
  2265. public byte[] ToBytes()
  2266. {
  2267. return BitConverterExt.ToBytes(width, height, data);
  2268. }
  2269. }
  2270. public class TgaSoftVersion : ICloneable
  2271. {
  2272. ushort versionNumber = 0;
  2273. char versionLetter = ' ';
  2274. /// <summary>
  2275. /// Gets Empty <see cref="TgaSoftVersion"/>, <see cref="VersionLetter"/> = ' ' (space).
  2276. /// </summary>
  2277. public TgaSoftVersion()
  2278. {
  2279. }
  2280. /// <summary>
  2281. /// Make <see cref="TgaSoftVersion"/> from string.
  2282. /// </summary>
  2283. /// <param name="Str">Input string, example: "123d".</param>
  2284. public TgaSoftVersion(string Str)
  2285. {
  2286. if (Str == null)
  2287. throw new ArgumentNullException();
  2288. if (Str.Length < 3 || Str.Length > 4)
  2289. throw new ArgumentOutOfRangeException(nameof(Str.Length) + " must be equal 3 or 4!");
  2290. bool Res = ushort.TryParse(Str.Substring(0, 3), out versionNumber);
  2291. if (Res && Str.Length == 4)
  2292. versionLetter = Str[3];
  2293. }
  2294. /// <summary>
  2295. /// Make <see cref="TgaSoftVersion"/> from bytes.
  2296. /// </summary>
  2297. /// <param name="Bytes">Bytes array (byte[3]).</param>
  2298. public TgaSoftVersion(byte[] Bytes)
  2299. {
  2300. if (Bytes == null)
  2301. throw new ArgumentNullException(nameof(Bytes) + " = null!");
  2302. if (Bytes.Length != Size)
  2303. throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
  2304. versionNumber = BitConverter.ToUInt16(Bytes, 0);
  2305. versionLetter = Encoding.ASCII.GetString(Bytes, 2, 1)[0];
  2306. }
  2307. public TgaSoftVersion(ushort VersionNumber, char VersionLetter = ' ')
  2308. {
  2309. versionNumber = VersionNumber;
  2310. versionLetter = VersionLetter;
  2311. }
  2312. public ushort VersionNumber
  2313. {
  2314. get { return versionNumber; }
  2315. set { versionNumber = value; }
  2316. }
  2317. public char VersionLetter
  2318. {
  2319. get { return versionLetter; }
  2320. set { versionLetter = value; }
  2321. }
  2322. /// <summary>
  2323. /// Gets TGA Field size in bytes.
  2324. /// </summary>
  2325. public const int Size = 3;
  2326. /// <summary>
  2327. /// Make full copy of <see cref="TgaSoftVersion"/>.
  2328. /// </summary>
  2329. /// <returns></returns>
  2330. public TgaSoftVersion Clone()
  2331. {
  2332. return new TgaSoftVersion(versionNumber, versionLetter);
  2333. }
  2334. /// <summary>
  2335. /// Make full copy of <see cref="TgaSoftVersion"/>.
  2336. /// </summary>
  2337. /// <returns></returns>
  2338. object ICloneable.Clone()
  2339. {
  2340. return Clone();
  2341. }
  2342. public override bool Equals(object obj)
  2343. {
  2344. return ((obj is TgaSoftVersion) ? Equals((TgaSoftVersion)obj) : false);
  2345. }
  2346. public bool Equals(TgaSoftVersion item)
  2347. {
  2348. return (versionNumber == item.versionNumber && versionLetter == item.versionLetter);
  2349. }
  2350. public static bool operator ==(TgaSoftVersion item1, TgaSoftVersion item2)
  2351. {
  2352. if (ReferenceEquals(item1, null))
  2353. return ReferenceEquals(item2, null);
  2354. if (ReferenceEquals(item2, null))
  2355. return ReferenceEquals(item1, null);
  2356. return item1.Equals(item2);
  2357. }
  2358. public static bool operator !=(TgaSoftVersion item1, TgaSoftVersion item2)
  2359. {
  2360. return !(item1 == item2);
  2361. }
  2362. public override int GetHashCode()
  2363. {
  2364. return versionNumber.GetHashCode() ^ versionLetter.GetHashCode();
  2365. }
  2366. public override string ToString()
  2367. {
  2368. return (versionNumber.ToString("000") + versionLetter).TrimEnd(new char[] { ' ', '\0' });
  2369. }
  2370. /// <summary>
  2371. /// Convert <see cref="TgaSoftVersion"/> to byte array.
  2372. /// </summary>
  2373. /// <returns>Byte array, <see cref="VersionNumber"/> (2 bytes) and
  2374. /// <see cref="VersionLetter"/> (ASCII symbol).</returns>
  2375. public byte[] ToBytes()
  2376. {
  2377. return ToBytes(versionNumber, versionLetter);
  2378. }
  2379. /// <summary>
  2380. /// Convert <see cref="TgaSoftVersion"/> to byte array.
  2381. /// </summary>
  2382. /// <param name="VersionNumber">Set 123 for 1.23 version.</param>
  2383. /// <param name="VersionLetter">Version letter, example: for 'a' - "1.23a".</param>
  2384. /// <returns>Byte array, <see cref="VersionNumber"/> (2 bytes) and <see cref="VersionLetter"/> (ASCII symbol).</returns>
  2385. public static byte[] ToBytes(ushort VersionNumber, char VersionLetter = ' ')
  2386. {
  2387. return BitConverterExt.ToBytes(VersionNumber, Encoding.ASCII.GetBytes(VersionLetter.ToString()));
  2388. }
  2389. }
  2390. /// <summary>
  2391. /// Use it for working with ASCII strings in TGA files.
  2392. /// </summary>
  2393. public class TgaString : ICloneable
  2394. {
  2395. public const string XFileSignatuteConst = "TRUEVISION-XFILE";
  2396. public const string DotSymbolConst = ".";
  2397. string origString = String.Empty;
  2398. int length = 0;
  2399. char blankSpaceChar = DefaultBlankSpaceChar;
  2400. bool useEnding = false;
  2401. public TgaString(bool UseEnding = false)
  2402. {
  2403. useEnding = UseEnding;
  2404. }
  2405. public TgaString(byte[] Bytes, bool UseEnding = false)
  2406. {
  2407. if (Bytes == null)
  2408. throw new ArgumentNullException(nameof(Bytes) + " = null!");
  2409. length = Bytes.Length;
  2410. useEnding = UseEnding;
  2411. string s = Encoding.ASCII.GetString(Bytes, 0, Bytes.Length - (useEnding ? 1 : 0));
  2412. if (s.Length > 0)
  2413. switch (s[s.Length - 1])
  2414. {
  2415. case '\0':
  2416. case ' ':
  2417. blankSpaceChar = s[s.Length - 1];
  2418. origString = s.TrimEnd(new char[] { s[s.Length - 1] });
  2419. break;
  2420. default:
  2421. origString = s;
  2422. break;
  2423. }
  2424. }
  2425. public TgaString(int Length, bool UseEnding = false)
  2426. {
  2427. length = Length;
  2428. useEnding = UseEnding;
  2429. }
  2430. public TgaString(string Str, int Length, bool UseEnding = false, char BlankSpaceChar = '\0')
  2431. {
  2432. if (Str == null)
  2433. throw new ArgumentNullException(nameof(Str) + " = null!");
  2434. origString = Str;
  2435. length = Length;
  2436. blankSpaceChar = BlankSpaceChar;
  2437. useEnding = UseEnding;
  2438. }
  2439. public string OriginalString
  2440. {
  2441. get { return origString; }
  2442. set { origString = value; }
  2443. }
  2444. public int Length
  2445. {
  2446. get { return length; }
  2447. set { length = value; }
  2448. }
  2449. public char BlankSpaceChar
  2450. {
  2451. get { return blankSpaceChar; }
  2452. set { blankSpaceChar = value; }
  2453. }
  2454. public bool UseEndingChar
  2455. {
  2456. get { return useEnding; }
  2457. set { useEnding = value; }
  2458. }
  2459. /// <summary>
  2460. /// Gets ending char, default '\0'.
  2461. /// </summary>
  2462. public static readonly char DefaultEndingChar = '\0';
  2463. /// <summary>
  2464. /// Gets blank space char, value = '\0'.
  2465. /// </summary>
  2466. public static readonly char DefaultBlankSpaceChar = '\0';
  2467. /// <summary>
  2468. /// Gets Empty <see cref="TgaString"/>.
  2469. /// </summary>
  2470. public static readonly TgaString Empty = new TgaString();
  2471. /// <summary>
  2472. /// Gets <see cref="TgaString"/> with <see cref="DefaultEndingChar"/> = '\0' and <see cref="UseEndingChar"/> = true.
  2473. /// </summary>
  2474. public static readonly TgaString ZeroTerminator = new TgaString(true);
  2475. /// <summary>
  2476. /// Gets "." <see cref="TgaString"/> with dot (period) symbol.
  2477. /// </summary>
  2478. public static readonly TgaString DotSymbol = new TgaString(DotSymbolConst, DotSymbolConst.Length);
  2479. /// <summary>
  2480. /// Gets "TRUEVISION-XFILE" <see cref="TgaString"/> (TGA File Format Version 2.0 signatute).
  2481. /// </summary>
  2482. public static readonly TgaString XFileSignatute = new TgaString(XFileSignatuteConst, XFileSignatuteConst.Length);
  2483. /// <summary>
  2484. /// Make full independed copy of <see cref="TgaString"/>.
  2485. /// </summary>
  2486. /// <returns>Copy of <see cref="TgaString"/></returns>
  2487. public TgaString Clone()
  2488. {
  2489. return new TgaString(origString, length, useEnding, blankSpaceChar);
  2490. }
  2491. /// <summary>
  2492. /// Make full independed copy of <see cref="TgaString"/>.
  2493. /// </summary>
  2494. /// <returns>Copy of <see cref="TgaString"/></returns>
  2495. object ICloneable.Clone()
  2496. {
  2497. return Clone();
  2498. }
  2499. public override bool Equals(object obj)
  2500. {
  2501. return ((obj is TgaString) ? Equals((TgaString)obj) : false);
  2502. }
  2503. public bool Equals(TgaString item)
  2504. {
  2505. return (
  2506. origString == item.origString &&
  2507. length == item.length &&
  2508. blankSpaceChar == item.blankSpaceChar &&
  2509. useEnding == item.useEnding);
  2510. }
  2511. public static bool operator ==(TgaString item1, TgaString item2)
  2512. {
  2513. if (ReferenceEquals(item1, null))
  2514. return ReferenceEquals(item2, null);
  2515. if (ReferenceEquals(item2, null))
  2516. return ReferenceEquals(item1, null);
  2517. return item1.Equals(item2);
  2518. }
  2519. public static bool operator !=(TgaString item1, TgaString item2)
  2520. {
  2521. return !(item1 == item2);
  2522. }
  2523. public static TgaString operator +(TgaString item1, TgaString item2)
  2524. {
  2525. if (ReferenceEquals(item1, null) || ReferenceEquals(item2, null))
  2526. throw new ArgumentNullException();
  2527. return new TgaString(BitConverterExt.ToBytes(item1.ToBytes(), item2.ToBytes()));
  2528. }
  2529. public override int GetHashCode()
  2530. {
  2531. unchecked
  2532. {
  2533. int hash = 17;
  2534. hash = hash * 23 + origString.GetHashCode();
  2535. hash = hash * 23 + length.GetHashCode();
  2536. hash = hash * 23 + blankSpaceChar.GetHashCode();
  2537. hash = hash * 23 + useEnding.GetHashCode();
  2538. return hash;
  2539. }
  2540. }
  2541. /// <summary>
  2542. /// Get ASCII-Like string with string-terminators, example: "Some string\0\0\0\0\0".
  2543. /// </summary>
  2544. /// <returns>String with replaced string-terminators to "\0".</returns>
  2545. public override string ToString()
  2546. {
  2547. return Encoding.ASCII.GetString(ToBytes()).Replace("\0", @"\0");
  2548. }
  2549. /// <summary>
  2550. /// Get ASCII-Like string to first string-terminator, example:
  2551. /// "Some string \0 Some Data \0" - > "Some string".
  2552. /// </summary>
  2553. /// <returns>String to first string-terminator.</returns>
  2554. public string GetString()
  2555. {
  2556. String Str = Encoding.ASCII.GetString(ToBytes());
  2557. int EndIndex = Str.IndexOf('\0');
  2558. if (EndIndex != -1)
  2559. Str = Str.Substring(0, EndIndex);
  2560. return Str;
  2561. }
  2562. /// <summary>
  2563. /// Convert <see cref="TgaString"/> to byte array.
  2564. /// </summary>
  2565. /// <returns>Byte array, every byte is ASCII symbol.</returns>
  2566. public byte[] ToBytes()
  2567. {
  2568. return ToBytes(origString, length, useEnding, blankSpaceChar);
  2569. }
  2570. /// <summary>
  2571. /// Convert <see cref="TgaString"/> to byte array.
  2572. /// </summary>
  2573. /// <param name="str">Input string.</param>
  2574. /// <param name="Length">Length of output ASCII string with Ending char (if used).</param>
  2575. /// <param name="UseEnding">Add <see cref="EndingChr"/> to string or not?</param>
  2576. /// <param name="BlankSpaceChar">Char for filling blank space in string. If this char is '-' (only for example!),
  2577. /// for string "ABC" with <see cref="Length"/> = 7, with <see cref="UseEnding"/> = true,
  2578. /// <see cref="DefaultEndingChar"/> is '\0', result string is "ABC---\0".</param>
  2579. /// <returns>Byte array, every byte is ASCII symbol.</returns>
  2580. public static byte[] ToBytes(string str, int Length, bool UseEnding = true, char BlankSpaceChar = '\0')
  2581. {
  2582. char[] C = new char[Math.Max(Length, (UseEnding ? 1 : 0))];
  2583. for (int i = 0; i < C.Length; i++)
  2584. C[i] = (i < str.Length ? str[i] : BlankSpaceChar);
  2585. if (UseEnding)
  2586. C[C.Length - 1] = DefaultEndingChar;
  2587. return Encoding.ASCII.GetBytes(C);
  2588. }
  2589. }
  2590. public class TgaTime : ICloneable
  2591. {
  2592. ushort hours = 0;
  2593. ushort minutes = 0;
  2594. ushort seconds = 0;
  2595. /// <summary>
  2596. /// Make empty <see cref="TgaTime"/>.
  2597. /// </summary>
  2598. public TgaTime()
  2599. {
  2600. }
  2601. /// <summary>
  2602. /// Make <see cref="TgaTime"/> from <see cref="TimeSpan"/>.
  2603. /// </summary>
  2604. /// <param name="Time">Some <see cref="TimeSpan"/> variable.</param>
  2605. public TgaTime(TimeSpan Time)
  2606. {
  2607. hours = (ushort)Time.TotalHours;
  2608. minutes = (ushort)Time.Minutes;
  2609. seconds = (ushort)Time.Seconds;
  2610. }
  2611. /// <summary>
  2612. /// Make <see cref="TgaTime"/> from ushort values.
  2613. /// </summary>
  2614. /// <param name="Hours">Hour (0 - 65535).</param>
  2615. /// <param name="Minutes">Minute (0 - 59).</param>
  2616. /// <param name="Seconds">Second (0 - 59).</param>
  2617. public TgaTime(ushort Hours, ushort Minutes, ushort Seconds)
  2618. {
  2619. hours = Hours;
  2620. minutes = Minutes;
  2621. seconds = Seconds;
  2622. }
  2623. /// <summary>
  2624. /// Make <see cref="TgaTime"/> from bytes.
  2625. /// </summary>
  2626. /// <param name="Bytes">Array of bytes(byte[6]).</param>
  2627. public TgaTime(byte[] Bytes)
  2628. {
  2629. if (Bytes == null)
  2630. throw new ArgumentNullException(nameof(Bytes) + " = null!");
  2631. else if (Bytes.Length != Size)
  2632. throw new ArgumentOutOfRangeException(nameof(Bytes) + " must be equal " + Size + "!");
  2633. hours = BitConverter.ToUInt16(Bytes, 0);
  2634. minutes = BitConverter.ToUInt16(Bytes, 2);
  2635. seconds = BitConverter.ToUInt16(Bytes, 4);
  2636. }
  2637. /// <summary>
  2638. /// Gets or Sets hour (0 - 65535).
  2639. /// </summary>
  2640. public ushort Hours
  2641. {
  2642. get { return hours; }
  2643. set { hours = value; }
  2644. }
  2645. /// <summary>
  2646. /// Gets or Sets minute (0 - 59).
  2647. /// </summary>
  2648. public ushort Minutes
  2649. {
  2650. get { return minutes; }
  2651. set { minutes = value; }
  2652. }
  2653. /// <summary>
  2654. /// Gets or Sets second (0 - 59).
  2655. /// </summary>
  2656. public ushort Seconds
  2657. {
  2658. get { return seconds; }
  2659. set { seconds = value; }
  2660. }
  2661. /// <summary>
  2662. /// Gets TGA Field size in bytes.
  2663. /// </summary>
  2664. public const int Size = 6;
  2665. /// <summary>
  2666. /// Make full independed copy of <see cref="TgaTime"/>.
  2667. /// </summary>
  2668. /// <returns>Copy of <see cref="TgaTime"/></returns>
  2669. public TgaTime Clone()
  2670. {
  2671. return new TgaTime(hours, minutes, seconds);
  2672. }
  2673. /// <summary>
  2674. /// Make full independed copy of <see cref="TgaTime"/>.
  2675. /// </summary>
  2676. /// <returns>Copy of <see cref="TgaTime"/></returns>
  2677. object ICloneable.Clone()
  2678. {
  2679. return Clone();
  2680. }
  2681. public override bool Equals(object obj)
  2682. {
  2683. return ((obj is TgaTime) ? Equals((TgaTime)obj) : false);
  2684. }
  2685. public bool Equals(TgaTime item)
  2686. {
  2687. return (hours == item.hours && minutes == item.minutes && seconds == item.seconds);
  2688. }
  2689. public static bool operator ==(TgaTime item1, TgaTime item2)
  2690. {
  2691. if (ReferenceEquals(item1, null))
  2692. return ReferenceEquals(item2, null);
  2693. if (ReferenceEquals(item2, null))
  2694. return ReferenceEquals(item1, null);
  2695. return item1.Equals(item2);
  2696. }
  2697. public static bool operator !=(TgaTime item1, TgaTime item2)
  2698. {
  2699. return !(item1 == item2);
  2700. }
  2701. public override int GetHashCode()
  2702. {
  2703. unchecked
  2704. {
  2705. int hash = 17;
  2706. hash = hash * 23 + hours.GetHashCode();
  2707. hash = hash * 23 + (minutes << 16 | seconds).GetHashCode();
  2708. return hash;
  2709. }
  2710. }
  2711. /// <summary>
  2712. /// Gets <see cref="TgaTime"/> like string.
  2713. /// </summary>
  2714. /// <returns>String in "H:M:S" format.</returns>
  2715. public override string ToString()
  2716. {
  2717. return String.Format("{0}:{1}:{2}", hours, minutes, seconds);
  2718. }
  2719. /// <summary>
  2720. /// Convert <see cref="TgaTime"/> to byte array.
  2721. /// </summary>
  2722. /// <returns>Byte array with length = 6.</returns>
  2723. public byte[] ToBytes()
  2724. {
  2725. return BitConverterExt.ToBytes(hours, minutes, seconds);
  2726. }
  2727. /// <summary>
  2728. /// Gets <see cref="TgaTime"/> like <see cref="TimeSpan"/>.
  2729. /// </summary>
  2730. /// <returns><see cref="TimeSpan"/> value of <see cref="TgaTime"/>.</returns>
  2731. public TimeSpan ToTimeSpan()
  2732. {
  2733. return new TimeSpan(hours, minutes, seconds);
  2734. }
  2735. }
  2736. ////////////////////////////////////////////////////////////////////////////////////////////////
  2737. /// <summary>
  2738. /// File Header Area (18 bytes)
  2739. /// </summary>
  2740. public class TgaHeader : ICloneable
  2741. {
  2742. byte idLength = 0;
  2743. TgaColorMapType colorMapType = TgaColorMapType.NoColorMap;
  2744. TgaImageType imageType = TgaImageType.NoImageData;
  2745. TgaColorMapSpec colorMapSpec = new TgaColorMapSpec();
  2746. TgaImageSpec imageSpec = new TgaImageSpec();
  2747. /// <summary>
  2748. /// Make empty <see cref="TgaHeader"/>.
  2749. /// </summary>
  2750. public TgaHeader()
  2751. {
  2752. }
  2753. /// <summary>
  2754. /// Make <see cref="TgaHeader"/> from bytes.
  2755. /// </summary>
  2756. /// <param name="Bytes">Bytes array (byte[18]).</param>
  2757. public TgaHeader(byte[] Bytes)
  2758. {
  2759. if (Bytes == null)
  2760. throw new ArgumentNullException(nameof(Bytes) + " = null!");
  2761. if (Bytes.Length != Size)
  2762. throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
  2763. idLength = Bytes[0];
  2764. colorMapType = (TgaColorMapType)Bytes[1];
  2765. imageType = (TgaImageType)Bytes[2];
  2766. colorMapSpec = new TgaColorMapSpec(BitConverterExt.GetElements(Bytes, 3, TgaColorMapSpec.Size));
  2767. imageSpec = new TgaImageSpec(BitConverterExt.GetElements(Bytes, 8, TgaImageSpec.Size));
  2768. }
  2769. /// <summary>
  2770. /// ID Length - Field 1 (1 byte):
  2771. /// This field identifies the number of bytes contained in the <see cref="ImageID"/> Field.
  2772. /// The maximum number of characters is 255. A value of zero indicates that no Image ID
  2773. /// field is included with the image.
  2774. /// </summary>
  2775. public byte IDLength
  2776. {
  2777. get { return idLength; }
  2778. set { idLength = value; }
  2779. }
  2780. /// <summary>
  2781. /// Color Map Type - Field 2 (1 byte):
  2782. /// This field indicates the type of color map (if any) included with the image.
  2783. /// There are currently 2 defined values for this field:
  2784. /// <para>0 - indicates that no color-map data is included with this image;</para>
  2785. /// <para>1 - indicates that a color-map is included with this image.</para>
  2786. /// </summary>
  2787. public TgaColorMapType ColorMapType
  2788. {
  2789. get { return colorMapType; }
  2790. set { colorMapType = value; }
  2791. }
  2792. /// <summary>
  2793. /// Image Type - Field 3 (1 byte):
  2794. /// <para>The TGA File Format can be used to store Pseudo-Color, True-Color and Direct-Color images
  2795. /// of various pixel depths.</para>
  2796. /// </summary>
  2797. public TgaImageType ImageType
  2798. {
  2799. get { return imageType; }
  2800. set { imageType = value; }
  2801. }
  2802. /// <summary>
  2803. /// Color Map Specification - Field 4 (5 bytes):
  2804. /// <para>This field and its sub-fields describe the color map (if any) used for the image.
  2805. /// If the Color Map Type field is set to zero, indicating that no color map exists, then
  2806. /// these 5 bytes should be set to zero. These bytes always must be written to the file.</para>
  2807. /// </summary>
  2808. public TgaColorMapSpec ColorMapSpec
  2809. {
  2810. get { return colorMapSpec; }
  2811. set { colorMapSpec = value; }
  2812. }
  2813. /// <summary>
  2814. /// Image Specification - Field 5 (10 bytes):
  2815. /// <para>This field and its sub-fields describe the image screen location, size and pixel depth.
  2816. /// These information is always written to the file.</para>
  2817. /// </summary>
  2818. public TgaImageSpec ImageSpec
  2819. {
  2820. get { return imageSpec; }
  2821. set { imageSpec = value; }
  2822. }
  2823. /// <summary>
  2824. /// Gets TGA Header Section size in bytes.
  2825. /// </summary>
  2826. public const int Size = 18;
  2827. /// <summary>
  2828. /// Make full copy of <see cref="TgaHeader"/>.
  2829. /// </summary>
  2830. /// <returns>Full independent copy of <see cref="TgaHeader"/>.</returns>
  2831. public TgaHeader Clone()
  2832. {
  2833. return new TgaHeader(ToBytes());
  2834. }
  2835. /// <summary>
  2836. /// Make full copy of <see cref="TgaHeader"/>.
  2837. /// </summary>
  2838. /// <returns>Full independent copy of <see cref="TgaHeader"/>.</returns>
  2839. object ICloneable.Clone()
  2840. {
  2841. return Clone();
  2842. }
  2843. public override bool Equals(object obj)
  2844. {
  2845. return ((obj is TgaHeader) ? Equals((TgaHeader)obj) : false);
  2846. }
  2847. public bool Equals(TgaHeader item)
  2848. {
  2849. return (idLength == item.idLength &&
  2850. colorMapType == item.colorMapType &&
  2851. imageType == item.imageType &&
  2852. colorMapSpec == item.colorMapSpec &&
  2853. imageSpec == item.imageSpec);
  2854. }
  2855. public static bool operator ==(TgaHeader item1, TgaHeader item2)
  2856. {
  2857. if (ReferenceEquals(item1, null))
  2858. return ReferenceEquals(item2, null);
  2859. if (ReferenceEquals(item2, null))
  2860. return ReferenceEquals(item1, null);
  2861. return item1.Equals(item2);
  2862. }
  2863. public static bool operator !=(TgaHeader item1, TgaHeader item2)
  2864. {
  2865. return !(item1 == item2);
  2866. }
  2867. public override int GetHashCode()
  2868. {
  2869. unchecked
  2870. {
  2871. int hash = 17;
  2872. hash = hash * 23 + (idLength << 24 | (byte)colorMapType << 8 | (byte)imageType).GetHashCode();
  2873. if (colorMapSpec != null)
  2874. hash = hash * 23 + colorMapSpec.GetHashCode();
  2875. if (imageSpec != null)
  2876. hash = hash * 23 + imageSpec.GetHashCode();
  2877. return hash;
  2878. }
  2879. }
  2880. public override string ToString()
  2881. {
  2882. return String.Format("{0}={1}, {2}={3}, {4}={5}, {6}={7}, {8}={9}",
  2883. nameof(IDLength), idLength,
  2884. nameof(ColorMapType), colorMapType,
  2885. nameof(ImageType), imageType,
  2886. nameof(ColorMapSpec), colorMapSpec,
  2887. nameof(ImageSpec), imageSpec);
  2888. }
  2889. /// <summary>
  2890. /// Convert <see cref="TgaHeader"/> to byte array.
  2891. /// </summary>
  2892. /// <returns>Byte array with size equal <see cref="Size"/>.</returns>
  2893. public byte[] ToBytes()
  2894. {
  2895. return BitConverterExt.ToBytes(idLength, (byte)colorMapType, (byte)imageType,
  2896. (colorMapSpec == null ? new byte[TgaColorMapSpec.Size] : colorMapSpec.ToBytes()),
  2897. (imageSpec == null ? new byte[TgaImageSpec.Size] : imageSpec.ToBytes()));
  2898. }
  2899. }
  2900. /// <summary>
  2901. /// Image Or ColorMap Area
  2902. /// </summary>
  2903. public class TgaImgOrColMap : ICloneable
  2904. {
  2905. TgaString imageID = null;
  2906. byte[] colorMapData = null;
  2907. byte[] imageData = null;
  2908. /// <summary>
  2909. /// Make empty <see cref="TgaImgOrColMap"/>.
  2910. /// </summary>
  2911. public TgaImgOrColMap()
  2912. {
  2913. }
  2914. /// <summary>
  2915. /// Make <see cref="TgaImgOrColMap"/> from arrays.
  2916. /// </summary>
  2917. /// <param name="ImageID">This optional field contains identifying information about the image.
  2918. /// The maximum length for this field is 255 bytes. Refer to <see cref="TgaHeader.IDLength"/>
  2919. /// for the length of this field. If field 1 is set to Zero indicating that no Image ID exists
  2920. /// then these bytes are not written to the file.</param>
  2921. /// <param name="ColorMapData">Color Map Data, see <see cref="ColorMapData"/> description.</param>
  2922. /// <param name="ImageData">Image Data, see <see cref="ImageData"/> description.</param>
  2923. public TgaImgOrColMap(TgaString ImageID, byte[] ColorMapData, byte[] ImageData)
  2924. {
  2925. imageID = ImageID;
  2926. colorMapData = ColorMapData;
  2927. imageData = ImageData;
  2928. }
  2929. /// <summary>
  2930. /// Image ID - Field 6 (variable):
  2931. /// <para>This optional field contains identifying information about the image. The maximum length
  2932. /// for this field is 255 bytes. Refer to <see cref="TgaHeader.IDLength"/> for the length of this
  2933. /// field. If field 1 is set to Zero indicating that no Image ID exists then these bytes are not
  2934. /// written to the file. Can have text inside (ASCII).</para>
  2935. /// </summary>
  2936. public TgaString ImageID
  2937. {
  2938. get { return imageID; }
  2939. set { imageID = value; }
  2940. }
  2941. /// <summary>
  2942. /// Color Map Data - Field 7 (variable):
  2943. /// <para>If the Color Map Type(field 2) field is set to zero indicating that no Color-Map
  2944. /// exists then this field will not be present (i.e., no bytes written to the file).</para>
  2945. /// <para>This variable-length field contains the actual color map information (LUT data).
  2946. /// Field 4.3 specifies the width in bits of each color map entry while Field 4.2 specifies
  2947. /// the number of color map entries in this field. These two fields together are used to
  2948. /// determine the number of bytes contained in field 7.</para>
  2949. /// <para>Each color map entry is stored using an integral number of bytes.The RGB specification
  2950. /// for each color map entry is stored in successive bit-fields in the multi-byte entries.
  2951. /// Each color bit-field is assumed to be MIN(Field4.3/3, 8) bits in length. If Field 4.3
  2952. /// contains 24, then each color specification is 8 bits in length; if Field 4.3 contains 32,
  2953. /// then each color specification is also 8 bits (32/3 gives 10, but 8 is smaller).
  2954. /// Unused bit(s) in the multi-byte entries are assumed to specify attribute bits. The
  2955. /// attribute bit field is often called the Alpha Channel, Overlay Bit(s) or Interrupt Bit(s).</para>
  2956. /// For the TARGA M-8, ATVista and NuVista, the number of bits in a color map specification is
  2957. /// 24 (or 32). The red, green, and blue components are each represented by one byte.
  2958. /// </summary>
  2959. public byte[] ColorMapData
  2960. {
  2961. get { return colorMapData; }
  2962. set { colorMapData = value; }
  2963. }
  2964. /// <summary>
  2965. /// Image Data - Field 8 (variable):
  2966. /// <para>This field contains (Width)x(Height) pixels. Each pixel specifies image data in one
  2967. /// of the following formats:</para>
  2968. /// <para>a single color-map index for Pseudo-Color;
  2969. /// Attribute, Red, Green and Blue ordered data for True-Color;
  2970. /// and independent color-map indices for Direct-Color.</para>
  2971. /// <para>The values for Width and Height are specified in Fields 5.3 and 5.4 respectively.
  2972. /// The number of attribute and color-definition bits for each pixel are defined in Fields 5.6
  2973. /// and 5.5, respectively.Each pixel is stored as an integral number of bytes.</para>
  2974. /// </summary>
  2975. public byte[] ImageData
  2976. {
  2977. get { return imageData; }
  2978. set { imageData = value; }
  2979. }
  2980. /// <summary>
  2981. /// Make full copy of <see cref="TgaImgOrColMap"/>.
  2982. /// </summary>
  2983. /// <returns>Full independed copy of <see cref="TgaImgOrColMap"/>.</returns>
  2984. public TgaImgOrColMap Clone()
  2985. {
  2986. return new TgaImgOrColMap(
  2987. (imageID == null ? null : imageID.Clone()),
  2988. (colorMapData == null ? null : (byte[])colorMapData.Clone()),
  2989. (imageData == null ? null : (byte[])imageData.Clone()));
  2990. }
  2991. /// <summary>
  2992. /// Make full copy of <see cref="TgaImgOrColMap"/>.
  2993. /// </summary>
  2994. /// <returns>Full independed copy of <see cref="TgaImgOrColMap"/>.</returns>
  2995. object ICloneable.Clone()
  2996. {
  2997. return Clone();
  2998. }
  2999. public override bool Equals(object obj)
  3000. {
  3001. return ((obj is TgaImgOrColMap) ? Equals((TgaImgOrColMap)obj) : false);
  3002. }
  3003. public bool Equals(TgaImgOrColMap item)
  3004. {
  3005. return imageID == item.imageID &&
  3006. BitConverterExt.IsArraysEqual(colorMapData, item.colorMapData) &&
  3007. BitConverterExt.IsArraysEqual(imageData, item.imageData);
  3008. }
  3009. public static bool operator ==(TgaImgOrColMap item1, TgaImgOrColMap item2)
  3010. {
  3011. if (ReferenceEquals(item1, null))
  3012. return ReferenceEquals(item2, null);
  3013. if (ReferenceEquals(item2, null))
  3014. return ReferenceEquals(item1, null);
  3015. return item1.Equals(item2);
  3016. }
  3017. public static bool operator !=(TgaImgOrColMap item1, TgaImgOrColMap item2)
  3018. {
  3019. return !(item1 == item2);
  3020. }
  3021. public override int GetHashCode()
  3022. {
  3023. unchecked
  3024. {
  3025. int hash = 27;
  3026. if (imageID != null)
  3027. hash = (13 * hash) + imageID.GetHashCode();
  3028. if (colorMapData != null)
  3029. for (int i = 0; i < colorMapData.Length; i++)
  3030. hash = (13 * hash) + colorMapData[i].GetHashCode();
  3031. if (imageData != null)
  3032. for (int i = 0; i < imageData.Length; i++)
  3033. hash = (13 * hash) + imageData[i].GetHashCode();
  3034. return hash;
  3035. }
  3036. }
  3037. } //No ToBytes()
  3038. /// <summary>
  3039. /// Developer Area
  3040. /// </summary> //?
  3041. public class TgaDevArea : ICloneable
  3042. {
  3043. List<TgaDevEntry> entries = new List<TgaDevEntry>();
  3044. public TgaDevArea()
  3045. {
  3046. }
  3047. public TgaDevArea(List<TgaDevEntry> Entries)
  3048. {
  3049. if (Entries == null)
  3050. throw new ArgumentNullException(nameof(Entries) + " = null!");
  3051. entries = Entries;
  3052. }
  3053. /// <summary>
  3054. /// Developer Data - Field 9 (variable):
  3055. /// </summary>
  3056. public List<TgaDevEntry> Entries
  3057. {
  3058. get { return entries; }
  3059. set { entries = value; }
  3060. }
  3061. public int Count
  3062. {
  3063. get { return entries.Count; }
  3064. }
  3065. public TgaDevEntry this[int index]
  3066. {
  3067. get { return entries[index]; }
  3068. set { entries[index] = value; }
  3069. }
  3070. /// <summary>
  3071. /// Make full copy of <see cref="TgaDevArea"/>.
  3072. /// </summary>
  3073. /// <returns>Full independent copy of <see cref="TgaDevArea"/>.</returns>
  3074. public TgaDevArea Clone()
  3075. {
  3076. if (entries == null)
  3077. return new TgaDevArea(null);
  3078. List<TgaDevEntry> L = new List<TgaDevEntry>();
  3079. for (int i = 0; i < entries.Count; i++)
  3080. L.Add(entries[i].Clone());
  3081. return new TgaDevArea(L);
  3082. }
  3083. /// <summary>
  3084. /// Make full copy of <see cref="TgaDevArea"/>.
  3085. /// </summary>
  3086. /// <returns>Full independent copy of <see cref="TgaDevArea"/>.</returns>
  3087. object ICloneable.Clone()
  3088. {
  3089. return Clone();
  3090. }
  3091. public override bool Equals(object obj)
  3092. {
  3093. return ((obj is TgaDevArea) ? Equals((TgaDevArea)obj) : false);
  3094. }
  3095. public bool Equals(TgaDevArea item)
  3096. {
  3097. return BitConverterExt.IsListsEqual(entries, item.entries);
  3098. }
  3099. public static bool operator ==(TgaDevArea item1, TgaDevArea item2)
  3100. {
  3101. if (ReferenceEquals(item1, null))
  3102. return ReferenceEquals(item2, null);
  3103. if (ReferenceEquals(item2, null))
  3104. return ReferenceEquals(item1, null);
  3105. return item1.Equals(item2);
  3106. }
  3107. public static bool operator !=(TgaDevArea item1, TgaDevArea item2)
  3108. {
  3109. return !(item1 == item2);
  3110. }
  3111. public override int GetHashCode()
  3112. {
  3113. unchecked
  3114. {
  3115. int hash = 27;
  3116. if (entries != null)
  3117. for (int i = 0; i < entries.Count; i++)
  3118. hash = (13 * hash) + entries[i].GetHashCode();
  3119. return hash;
  3120. }
  3121. }
  3122. /// <summary>
  3123. /// Convert <see cref="TgaDevArea"/> (without Fields Data, only Directory!) to byte array.
  3124. /// </summary>
  3125. /// <returns>Byte array, Len = (NUMBER_OF_TAGS_IN_THE_DIRECTORY * 10) + 2 bytes in size.
  3126. /// The "+ 2" includes the 2 bytes for the number of tags in the directory.</returns>
  3127. public byte[] ToBytes()
  3128. {
  3129. if (entries == null)
  3130. throw new Exception(nameof(Entries) + " = null!");
  3131. ushort NumberOfEntries = (ushort)Math.Min(ushort.MaxValue, entries.Count);
  3132. List<byte> DevDir = new List<byte>(BitConverter.GetBytes(NumberOfEntries));
  3133. for (int i = 0; i < entries.Count; i++)
  3134. {
  3135. DevDir.AddRange(BitConverter.GetBytes(entries[i].Tag));
  3136. DevDir.AddRange(BitConverter.GetBytes(entries[i].Offset));
  3137. DevDir.AddRange(BitConverter.GetBytes(entries[i].FieldSize));
  3138. }
  3139. return DevDir.ToArray();
  3140. }
  3141. } //Not full ToBytes()
  3142. /// <summary>
  3143. /// Extension Area
  3144. /// </summary>
  3145. public class TgaExtArea : ICloneable
  3146. {
  3147. public const int MinSize = 495; //bytes
  3148. ushort extensionSize = MinSize;
  3149. TgaString authorName = new TgaString(41, true);
  3150. TgaComment authorComments = new TgaComment();
  3151. TgaDateTime dateTimeStamp = new TgaDateTime();
  3152. TgaString jobNameOrID = new TgaString(41, true);
  3153. TgaTime jobTime = new TgaTime();
  3154. TgaString softwareID = new TgaString(41, true);
  3155. TgaSoftVersion softVersion = new TgaSoftVersion();
  3156. TgaColorKey keyColor = new TgaColorKey();
  3157. TgaFraction pixelAspectRatio = TgaFraction.Empty;
  3158. TgaFraction gammaValue = TgaFraction.Empty;
  3159. uint colorCorrectionOffset = 0;
  3160. uint postageStampOffset = 0;
  3161. uint scanLineOffset = 0;
  3162. TgaAttrType attributesType = TgaAttrType.NoAlpha;
  3163. uint[] scanLineTable = null;
  3164. TgaPostageStampImage postageStampImage = null;
  3165. ushort[] colorCorrectionTable = null;
  3166. byte[] otherDataInExtensionArea = null;
  3167. public TgaExtArea()
  3168. {
  3169. }
  3170. /// <summary>
  3171. /// Make <see cref="TgaExtArea"/> from bytes. Warning: <see cref="ScanLineTable"/>,
  3172. /// <see cref="PostageStampImage"/>, <see cref="ColorCorrectionTable"/> not included,
  3173. /// because thea are can be not in the Extension Area of TGA file!
  3174. /// </summary>
  3175. /// <param name="Bytes">Bytes of <see cref="TgaExtArea"/>.</param>
  3176. /// <param name="SLT">Scan Line Table.</param>
  3177. /// <param name="PostImg">Postage Stamp Image.</param>
  3178. /// <param name="CCT">Color Correction Table.</param>
  3179. public TgaExtArea(byte[] Bytes, uint[] SLT = null, TgaPostageStampImage PostImg = null, ushort[] CCT = null)
  3180. {
  3181. if (Bytes == null)
  3182. throw new ArgumentNullException(nameof(Bytes) + " = null!");
  3183. if (Bytes.Length < MinSize)
  3184. throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be >= " + MinSize + "!");
  3185. extensionSize = BitConverter.ToUInt16(Bytes, 0);
  3186. authorName = new TgaString(BitConverterExt.GetElements(Bytes, 2, 41), true);
  3187. authorComments = new TgaComment(BitConverterExt.GetElements(Bytes, 43, TgaComment.Size));
  3188. dateTimeStamp = new TgaDateTime(BitConverterExt.GetElements(Bytes, 367, TgaDateTime.Size));
  3189. jobNameOrID = new TgaString(BitConverterExt.GetElements(Bytes, 379, 41), true);
  3190. jobTime = new TgaTime(BitConverterExt.GetElements(Bytes, 420, TgaTime.Size));
  3191. softwareID = new TgaString(BitConverterExt.GetElements(Bytes, 426, 41), true);
  3192. softVersion = new TgaSoftVersion(BitConverterExt.GetElements(Bytes, 467, TgaSoftVersion.Size));
  3193. keyColor = new TgaColorKey(BitConverterExt.GetElements(Bytes, 470, TgaColorKey.Size));
  3194. pixelAspectRatio = new TgaFraction(BitConverterExt.GetElements(Bytes, 474, TgaFraction.Size));
  3195. gammaValue = new TgaFraction(BitConverterExt.GetElements(Bytes, 478, TgaFraction.Size));
  3196. colorCorrectionOffset = BitConverter.ToUInt32(Bytes, 482);
  3197. postageStampOffset = BitConverter.ToUInt32(Bytes, 486);
  3198. scanLineOffset = BitConverter.ToUInt32(Bytes, 490);
  3199. attributesType = (TgaAttrType)Bytes[494];
  3200. if (extensionSize > MinSize)
  3201. otherDataInExtensionArea = BitConverterExt.GetElements(Bytes, 495, Bytes.Length - MinSize);
  3202. scanLineTable = SLT;
  3203. postageStampImage = PostImg;
  3204. colorCorrectionTable = CCT;
  3205. }
  3206. #region Properties
  3207. /// <summary>
  3208. /// Extension Size - Field 10 (2 Bytes):
  3209. /// This field is a SHORT field which specifies the number of BYTES in the fixedlength portion of
  3210. /// the Extension Area. For Version 2.0 of the TGA File Format, this number should be set to 495.
  3211. /// If the number found in this field is not 495, then the file will be assumed to be of a
  3212. /// version other than 2.0. If it ever becomes necessary to alter this number, the change
  3213. /// will be controlled by Truevision, and will be accompanied by a revision to the TGA File
  3214. /// Format with an accompanying change in the version number.
  3215. /// </summary>
  3216. public ushort ExtensionSize
  3217. {
  3218. get { return extensionSize; }
  3219. set { extensionSize = value; }
  3220. }
  3221. /// <summary>
  3222. /// Author Name - Field 11 (41 Bytes):
  3223. /// Bytes 2-42 - This field is an ASCII field of 41 bytes where the last byte must be a null
  3224. /// (binary zero). This gives a total of 40 ASCII characters for the name. If the field is used,
  3225. /// it should contain the name of the person who created the image (author). If the field is not
  3226. /// used, you may fill it with nulls or a series of blanks(spaces) terminated by a null.
  3227. /// The 41st byte must always be a null.
  3228. /// </summary>
  3229. public TgaString AuthorName
  3230. {
  3231. get { return authorName; }
  3232. set { authorName = value; }
  3233. }
  3234. /// <summary>
  3235. /// Author Comments - Field 12 (324 Bytes):
  3236. /// Bytes 43-366 - This is an ASCII field consisting of 324 bytes which are organized as four lines
  3237. /// of 80 characters, each followed by a null terminator.This field is provided, in addition to the
  3238. /// original IMAGE ID field(in the original TGA format), because it was determined that a few
  3239. /// developers had used the IMAGE ID field for their own purposes.This field gives the developer
  3240. /// four lines of 80 characters each, to use as an Author Comment area. Each line is fixed to 81
  3241. /// bytes which makes access to the four lines easy.Each line must be terminated by a null.
  3242. /// If you do not use all 80 available characters in the line, place the null after the last
  3243. /// character and blank or null fill the rest of the line. The 81st byte of each of the four
  3244. /// lines must be null.
  3245. /// </summary>
  3246. public TgaComment AuthorComments
  3247. {
  3248. get { return authorComments; }
  3249. set { authorComments = value; }
  3250. }
  3251. /// <summary>
  3252. /// Date/Time Stamp - Field 13 (12 Bytes):
  3253. /// Bytes 367-378 - This field contains a series of 6 SHORT values which define the integer
  3254. /// value for the date and time that the image was saved. This data is formatted as follows:
  3255. /// <para>SHORT 0: Month(1 - 12)</para>
  3256. /// <para>SHORT 1: Day(1 - 31)</para>
  3257. /// <para>SHORT 2: Year(4 digit, ie. 1989)</para>
  3258. /// <para>SHORT 3: Hour(0 - 23)</para>
  3259. /// <para>SHORT 4: Minute(0 - 59)</para>
  3260. /// <para>SHORT 5: Second(0 - 59)</para>
  3261. /// Even though operating systems typically time- and date-stamp files, this feature is
  3262. /// provided because the operating system may change the time and date stamp if the file is
  3263. /// copied. By using this area, you are guaranteed an unmodified region for date and time
  3264. /// recording. If the fields are not used, you should fill them with binary zeros (0).
  3265. /// </summary>
  3266. public TgaDateTime DateTimeStamp
  3267. {
  3268. get { return dateTimeStamp; }
  3269. set { dateTimeStamp = value; }
  3270. }
  3271. /// <summary>
  3272. /// Job Name/ID - Field 14 (41 Bytes):
  3273. /// Bytes 379-419 - This field is an ASCII field of 41 bytes where the last byte must be
  3274. /// a binary zero. This gives a total of 40 ASCII characters for the job name or the ID.
  3275. /// If the field is used, it should contain a name or id tag which refers to the job with
  3276. /// which the image was associated.This allows production companies (and others) to tie
  3277. /// images with jobs by using this field as a job name (i.e., CITY BANK) or job id number
  3278. /// (i.e., CITY023). If the field is not used, you may fill it with a null terminated series
  3279. /// of blanks (spaces) or nulls. In any case, the 41st byte must be a null.
  3280. /// </summary>
  3281. public TgaString JobNameOrID
  3282. {
  3283. get { return jobNameOrID; }
  3284. set { jobNameOrID = value; }
  3285. }
  3286. /// <summary>
  3287. /// Job Time - Field 15 (6 Bytes):
  3288. /// Bytes 420-425 - This field contains a series of 3 SHORT values which define the integer
  3289. /// value for the job elapsed time when the image was saved.This data is formatted as follows:
  3290. /// <para>SHORT 0: Hours(0 - 65535)</para>
  3291. /// <para>SHORT 1: Minutes(0 - 59)</para>
  3292. /// <para>SHORT 2: Seconds(0 - 59)</para>
  3293. /// The purpose of this field is to allow production houses (and others) to keep a running total
  3294. /// of the amount of time invested in a particular image. This may be useful for billing, costing,
  3295. /// and time estimating. If the fields are not used, you should fill them with binary zeros (0).
  3296. /// </summary>
  3297. public TgaTime JobTime
  3298. {
  3299. get { return jobTime; }
  3300. set { jobTime = value; }
  3301. }
  3302. /// <summary>
  3303. /// Software ID - Field 16 (41 Bytes):
  3304. /// Bytes 426-466 - This field is an ASCII field of 41 bytes where the last byte must be
  3305. /// a binary zero (null). This gives a total of 40 ASCII characters for the Software ID.
  3306. /// The purpose of this field is to allow software to determine and record with what program
  3307. /// a particular image was created.If the field is not used, you may fill it with a
  3308. /// null terminated series of blanks (spaces) or nulls. The 41st byte must always be a null.
  3309. /// </summary>
  3310. public TgaString SoftwareID
  3311. {
  3312. get { return softwareID; }
  3313. set { softwareID = value; }
  3314. }
  3315. /// <summary>
  3316. /// Software Version - Field 17 (3 Bytes):
  3317. /// Bytes 467-469 - This field consists of two sub-fields, a SHORT and an ASCII BYTE.
  3318. /// The purpose of this field is to define the version of software defined by the
  3319. /// “Software ID” field above. The SHORT contains the version number as a binary
  3320. /// integer times 100.
  3321. /// <para>Therefore, software version 4.17 would be the integer value 417.This allows for
  3322. /// two decimal positions of sub-version.The ASCII BYTE supports developers who also
  3323. /// tag a release letter to the end. For example, if the version number is 1.17b, then
  3324. /// the SHORT would contain 117. and the ASCII BYTE would contain “b”.
  3325. /// The organization is as follows:</para>
  3326. /// <para>SHORT (Bytes 0 - 1): Version Number * 100</para>
  3327. /// <para>BYTE(Byte 2): Version Letter</para>
  3328. /// If you do not use this field, set the SHORT to binary zero, and the BYTE to a space(“ “)
  3329. /// </summary>
  3330. public TgaSoftVersion SoftVersion
  3331. {
  3332. get { return softVersion; }
  3333. set { softVersion = value; }
  3334. }
  3335. /// <summary>
  3336. /// Key Color - Field 18 (4 Bytes):
  3337. /// Bytes 470-473 - This field contains a long value which is the key color in effect at
  3338. /// the time the image is saved. The format is in A:R:G:B where ‘A’ (most significant byte)
  3339. /// is the alpha channel key color(if you don’t have an alpha channel in your application,
  3340. /// keep this byte zero [0]).
  3341. /// <para>The Key Color can be thought of as the ‘background color’ or ‘transparent color’.
  3342. /// This is the color of the ‘non image’ area of the screen, and the same color that the
  3343. /// screen would be cleared to if erased in the application. If you don’t use this field,
  3344. /// set it to all zeros (0). Setting the field to all zeros is the same as selecting a key
  3345. /// color of black.</para>
  3346. /// A good example of a key color is the ‘transparent color’ used in TIPS™ for WINDOW loading/saving.
  3347. /// </summary>
  3348. public TgaColorKey KeyColor
  3349. {
  3350. get { return keyColor; }
  3351. set { keyColor = value; }
  3352. }
  3353. /// <summary>
  3354. /// Pixel Aspect Ratio - Field 19 (4 Bytes):
  3355. /// Bytes 474-477 - This field contains two SHORT sub-fields, which when taken together
  3356. /// specify a pixel size ratio.The format is as follows:
  3357. /// <para>SHORT 0: Pixel Ratio Numerator(pixel width)</para>
  3358. /// <para>SHORT 1: Pixel Ratio Denominator(pixel height)</para>
  3359. /// These sub-fields may be used to determine the aspect ratio of a pixel. This is useful when
  3360. /// it is important to preserve the proper aspect ratio of the saved image. If the two values
  3361. /// are set to the same non-zero value, then the image is composed of square pixels. A zero
  3362. /// in the second sub-field (denominator) indicates that no pixel aspect ratio is specified.
  3363. /// </summary>
  3364. public TgaFraction PixelAspectRatio
  3365. {
  3366. get { return pixelAspectRatio; }
  3367. set { pixelAspectRatio = value; }
  3368. }
  3369. /// <summary>
  3370. /// Gamma Value - Field 20 (4 Bytes):
  3371. /// Bytes 478-481 - This field contains two SHORT sub-fields, which when taken together in a ratio,
  3372. /// provide a fractional gamma value.The format is as follows:
  3373. /// <para>SHORT 0: Gamma Numerator</para>
  3374. /// <para>SHORT 1: Gamma Denominator</para>
  3375. /// The resulting value should be in the range of 0.0 to 10.0, with only one decimal place of
  3376. /// precision necessary. An uncorrected image (an image with no gamma) should have the value 1.0 as
  3377. /// the result.This may be accomplished by placing thesame, non-zero values in both positions
  3378. /// (i.e., 1/1). If you decide to totally ignore this field, please set the denominator (the second
  3379. /// SHORT) to the value zero. This will indicate that the Gamma Value field is not being used.
  3380. /// </summary>
  3381. public TgaFraction GammaValue
  3382. {
  3383. get { return gammaValue; }
  3384. set { gammaValue = value; }
  3385. }
  3386. /// <summary>
  3387. /// Color Correction Offset - Field 21 (4 Bytes):
  3388. /// Bytes 482-485 - This field is a 4-byte field containing a single offset value. This is an offset
  3389. /// from the beginning of the file to the start of the Color Correction table. This table may be
  3390. /// written anywhere between the end of the Image Data field (field 8) and the start of the TGA
  3391. /// File Footer. If the image has no Color Correction Table or if the Gamma Value setting is
  3392. /// sufficient, set this value to zero and do not write a Correction Table anywhere.
  3393. /// </summary>
  3394. public uint ColorCorrectionTableOffset
  3395. {
  3396. get { return colorCorrectionOffset; }
  3397. set { colorCorrectionOffset = value; }
  3398. }
  3399. /// <summary>
  3400. /// Postage Stamp Offset - Field 22 (4 Bytes):
  3401. /// Bytes 486-489 - This field is a 4-byte field containing a single offset value. This is an offset
  3402. /// from the beginning of the file to the start of the Postage Stamp Image. The Postage Stamp Image
  3403. /// must be written after Field 25 (Scan Line Table) but before the start of the TGA File Footer.
  3404. /// If no postage stamp is stored, set this field to the value zero (0).
  3405. /// </summary>
  3406. public uint PostageStampOffset
  3407. {
  3408. get { return postageStampOffset; }
  3409. set { postageStampOffset = value; }
  3410. }
  3411. /// <summary>
  3412. /// Scan Line Offset - Field 23 (4 Bytes):
  3413. /// Bytes 490-493 - This field is a 4-byte field containing a single offset value. This is an
  3414. /// offset from the beginning of the file to the start of the Scan Line Table.
  3415. /// </summary>
  3416. public uint ScanLineOffset
  3417. {
  3418. get { return scanLineOffset; }
  3419. set { scanLineOffset = value; }
  3420. }
  3421. /// <summary>
  3422. /// Attributes Type - Field 24 (1 Byte):
  3423. /// Byte 494 - This single byte field contains a value which specifies the type of Alpha channel
  3424. /// data contained in the file. Value Meaning:
  3425. /// <para>0: no Alpha data included (bits 3-0 of field 5.6 should also be set to zero)</para>
  3426. /// <para>1: undefined data in the Alpha field, can be ignored</para>
  3427. /// <para>2: undefined data in the Alpha field, but should be retained</para>
  3428. /// <para>3: useful Alpha channel data is present</para>
  3429. /// <para>4: pre-multiplied Alpha(see description below)</para>
  3430. /// <para>5 -127: RESERVED</para>
  3431. /// <para>128-255: Un-assigned</para>
  3432. /// <para>Pre-multiplied Alpha Example: Suppose the Alpha channel data is being used to specify the
  3433. /// opacity of each pixel(for use when the image is overlayed on another image), where 0 indicates
  3434. /// that the pixel is completely transparent and a value of 1 indicates that the pixel is
  3435. /// completely opaque(assume all component values have been normalized).</para>
  3436. /// <para>A quadruple(a, r, g, b) of( 0.5, 1, 0, 0) would indicate that the pixel is pure red with a
  3437. /// transparency of one-half. For numerous reasons(including image compositing) is is better to
  3438. /// pre-multiply the individual color components with the value in the Alpha channel.</para>
  3439. /// A pre-multiplication of the above would produce a quadruple(0.5, 0.5, 0, 0).
  3440. /// A value of 3 in the Attributes Type Field(field 23) would indicate that the color components
  3441. /// of the pixel have already been scaled by the value in the Alpha channel.
  3442. /// </summary>
  3443. public TgaAttrType AttributesType
  3444. {
  3445. get { return attributesType; }
  3446. set { attributesType = value; }
  3447. }
  3448. /// <summary>
  3449. /// Scan Line Table - Field 25 (Variable):
  3450. /// This information is provided, at the developers’ request, for two purposes:
  3451. /// <para>1) To make random access of compressed images easy.</para>
  3452. /// <para>2) To allow “giant picture” access in smaller “chunks”.</para>
  3453. /// This table should contain a series of 4-byte offsets.Each offset you write should point to the
  3454. /// start of the next scan line, in the order that the image was saved (i.e., top down or bottom up).
  3455. /// The offset should be from the start of the file.Therefore, you will have a four byte value for
  3456. /// each scan line in your image. This means that if your image is 768 pixels tall, you will have 768,
  3457. /// 4-byte offset pointers (for a total of 3072 bytes). This size is not extreme, and thus this table
  3458. /// can be built and maintained in memory, and then written out at the proper time.
  3459. /// </summary>
  3460. public uint[] ScanLineTable
  3461. {
  3462. get { return scanLineTable; }
  3463. set { scanLineTable = value; }
  3464. }
  3465. /// <summary>
  3466. /// Postage Stamp Image - Field 26 (Variable):
  3467. /// The Postage Stamp area is a smaller representation of the original image. This is useful for
  3468. /// “browsing” a collection of image files. If your application can deal with a postage stamp image,
  3469. /// it is recommended that you create one using sub-sampling techniques to create the best
  3470. /// representation possible. The postage stamp image must be stored in the same format as the normal
  3471. /// image specified in the file, but without any compression. The first byte of the postage stamp
  3472. /// image specifies the X size of the stamp in pixels, the second byte of the stamp image specifies the
  3473. /// Y size of the stamp in pixels. Truevision does not recommend stamps larger than 64x64 pixels, and
  3474. /// suggests that any stamps stored larger be clipped. Obviously, the storage of the postage stamp
  3475. /// relies heavily on the storage of the image. The two images are stored using the same format under
  3476. /// the assumption that if you can read the image, then you can read the postage stamp. If the original
  3477. /// image is color mapped, DO NOT average the postage stamp, as you will create new colors not in your map.
  3478. /// </summary>
  3479. public TgaPostageStampImage PostageStampImage
  3480. {
  3481. get { return postageStampImage; }
  3482. set { postageStampImage = value; }
  3483. }
  3484. /// <summary>
  3485. /// Color Correction Table - Field 27 (2K Bytes):
  3486. /// The Color Correction Table is a block of 256 x 4 SHORT values, where each set of
  3487. /// four contiguous values are the desired A:R:G:B correction for that entry. This
  3488. /// allows the user to store a correction table for image remapping or LUT driving.
  3489. /// Since each color in the block is a SHORT, the maximum value for a color gun (i.e.,
  3490. /// A, R, G, B) is 65535, and the minimum value is zero.Therefore, BLACK maps to
  3491. /// 0, 0, 0, 0 and WHITE maps to 65535, 65535, 65535, 65535.
  3492. /// </summary>
  3493. public ushort[] ColorCorrectionTable
  3494. {
  3495. get { return colorCorrectionTable; }
  3496. set { colorCorrectionTable = value; }
  3497. }
  3498. /// <summary>
  3499. /// Other Data In Extension Area (if <see cref="ExtensionSize"/> > 495).
  3500. /// </summary>
  3501. public byte[] OtherDataInExtensionArea
  3502. {
  3503. get { return otherDataInExtensionArea; }
  3504. set { otherDataInExtensionArea = value; }
  3505. }
  3506. #endregion
  3507. /// <summary>
  3508. /// Make full copy of <see cref="TgaExtArea"/>.
  3509. /// </summary>
  3510. /// <returns>Full independent copy of <see cref="TgaExtArea"/>.</returns>
  3511. public TgaExtArea Clone()
  3512. {
  3513. TgaExtArea NewExtArea = new TgaExtArea();
  3514. NewExtArea.extensionSize = extensionSize;
  3515. NewExtArea.authorName = authorName.Clone();
  3516. NewExtArea.authorComments = authorComments.Clone();
  3517. NewExtArea.dateTimeStamp = dateTimeStamp.Clone();
  3518. NewExtArea.jobNameOrID = jobNameOrID.Clone();
  3519. NewExtArea.jobTime = jobTime.Clone();
  3520. NewExtArea.softwareID = softwareID.Clone();
  3521. NewExtArea.softVersion = softVersion.Clone();
  3522. NewExtArea.keyColor = keyColor.Clone();
  3523. NewExtArea.pixelAspectRatio = pixelAspectRatio.Clone();
  3524. NewExtArea.gammaValue = gammaValue.Clone();
  3525. NewExtArea.colorCorrectionOffset = colorCorrectionOffset;
  3526. NewExtArea.postageStampOffset = postageStampOffset;
  3527. NewExtArea.scanLineOffset = scanLineOffset;
  3528. NewExtArea.attributesType = attributesType;
  3529. if (scanLineTable != null)
  3530. NewExtArea.scanLineTable = (uint[])scanLineTable.Clone();
  3531. if (postageStampImage != null)
  3532. NewExtArea.postageStampImage = new TgaPostageStampImage(postageStampImage.ToBytes());
  3533. if (colorCorrectionTable != null)
  3534. NewExtArea.colorCorrectionTable = (ushort[])colorCorrectionTable.Clone();
  3535. if (otherDataInExtensionArea != null)
  3536. NewExtArea.otherDataInExtensionArea = (byte[])otherDataInExtensionArea.Clone();
  3537. return NewExtArea;
  3538. }
  3539. /// <summary>
  3540. /// Make full copy of <see cref="TgaExtArea"/>.
  3541. /// </summary>
  3542. /// <returns>Full independent copy of <see cref="TgaExtArea"/>.</returns>
  3543. object ICloneable.Clone()
  3544. {
  3545. return Clone();
  3546. }
  3547. public override bool Equals(object obj)
  3548. {
  3549. return ((obj is TgaExtArea) ? Equals((TgaExtArea)obj) : false);
  3550. }
  3551. public bool Equals(TgaExtArea item)
  3552. {
  3553. return (extensionSize == item.extensionSize &&
  3554. authorName == item.authorName &&
  3555. authorComments == item.authorComments &&
  3556. dateTimeStamp == item.dateTimeStamp &&
  3557. jobNameOrID == item.jobNameOrID &&
  3558. jobTime == item.jobTime &&
  3559. softwareID == item.softwareID &&
  3560. softVersion == item.softVersion &&
  3561. keyColor == item.keyColor &&
  3562. pixelAspectRatio == item.pixelAspectRatio &&
  3563. gammaValue == item.gammaValue &&
  3564. colorCorrectionOffset == item.colorCorrectionOffset &&
  3565. postageStampOffset == item.postageStampOffset &&
  3566. scanLineOffset == item.scanLineOffset &&
  3567. attributesType == item.attributesType &&
  3568. BitConverterExt.IsArraysEqual(scanLineTable, item.scanLineTable) &&
  3569. postageStampImage == item.postageStampImage &&
  3570. BitConverterExt.IsArraysEqual(colorCorrectionTable, item.colorCorrectionTable) &&
  3571. BitConverterExt.IsArraysEqual(otherDataInExtensionArea, item.otherDataInExtensionArea));
  3572. }
  3573. public static bool operator ==(TgaExtArea item1, TgaExtArea item2)
  3574. {
  3575. if (ReferenceEquals(item1, null))
  3576. return ReferenceEquals(item2, null);
  3577. if (ReferenceEquals(item2, null))
  3578. return ReferenceEquals(item1, null);
  3579. return item1.Equals(item2);
  3580. }
  3581. public static bool operator !=(TgaExtArea item1, TgaExtArea item2)
  3582. {
  3583. return !(item1 == item2);
  3584. }
  3585. public override int GetHashCode()
  3586. {
  3587. unchecked
  3588. {
  3589. int hash = 27;
  3590. hash = (13 * hash) + extensionSize.GetHashCode();
  3591. hash = (13 * hash) + authorName.GetHashCode();
  3592. hash = (13 * hash) + authorComments.GetHashCode();
  3593. hash = (13 * hash) + dateTimeStamp.GetHashCode();
  3594. hash = (13 * hash) + jobNameOrID.GetHashCode();
  3595. hash = (13 * hash) + jobTime.GetHashCode();
  3596. hash = (13 * hash) + softwareID.GetHashCode();
  3597. hash = (13 * hash) + softVersion.GetHashCode();
  3598. hash = (13 * hash) + keyColor.GetHashCode();
  3599. hash = (13 * hash) + pixelAspectRatio.GetHashCode();
  3600. hash = (13 * hash) + gammaValue.GetHashCode();
  3601. hash = (13 * hash) + colorCorrectionOffset.GetHashCode();
  3602. hash = (13 * hash) + postageStampOffset.GetHashCode();
  3603. hash = (13 * hash) + scanLineOffset.GetHashCode();
  3604. hash = (13 * hash) + attributesType.GetHashCode();
  3605. if (scanLineTable != null)
  3606. for (int i = 0; i < scanLineTable.Length; i++)
  3607. hash = (13 * hash) + scanLineTable[i].GetHashCode();
  3608. if (postageStampImage != null)
  3609. hash = (13 * hash) + postageStampImage.GetHashCode();
  3610. if (colorCorrectionTable != null)
  3611. for (int i = 0; i < colorCorrectionTable.Length; i++)
  3612. hash = (13 * hash) + colorCorrectionTable[i].GetHashCode();
  3613. if (otherDataInExtensionArea != null)
  3614. for (int i = 0; i < otherDataInExtensionArea.Length; i++)
  3615. hash = (13 * hash) + otherDataInExtensionArea[i].GetHashCode();
  3616. return hash;
  3617. }
  3618. }
  3619. /// <summary>
  3620. /// Convert <see cref="TgaExtArea"/> to byte array. Warning: <see cref="ScanLineTable"/>,
  3621. /// <see cref="PostageStampImage"/>, <see cref="ColorCorrectionTable"/> not included,
  3622. /// because thea are can be not in the Extension Area of TGA file!
  3623. /// </summary>
  3624. /// <returns>Byte array.</returns>
  3625. public byte[] ToBytes()
  3626. {
  3627. #region Exceptions check
  3628. if (authorName == null)
  3629. authorName = new TgaString(41, true);
  3630. if (authorComments == null)
  3631. authorComments = new TgaComment();
  3632. if (dateTimeStamp == null)
  3633. dateTimeStamp = new TgaDateTime(DateTime.UtcNow);
  3634. if (jobNameOrID == null)
  3635. jobNameOrID = new TgaString(41, true);
  3636. if (jobTime == null)
  3637. jobTime = new TgaTime();
  3638. if (softwareID == null)
  3639. softwareID = new TgaString(41, true);
  3640. if (softVersion == null)
  3641. softVersion = new TgaSoftVersion();
  3642. if (keyColor == null)
  3643. keyColor = new TgaColorKey();
  3644. if (pixelAspectRatio == null)
  3645. pixelAspectRatio = new TgaFraction();
  3646. if (gammaValue == null)
  3647. gammaValue = new TgaFraction();
  3648. #endregion
  3649. return BitConverterExt.ToBytes(
  3650. extensionSize,
  3651. authorName.ToBytes(),
  3652. authorComments.ToBytes(),
  3653. dateTimeStamp.ToBytes(),
  3654. jobNameOrID.ToBytes(),
  3655. jobTime.ToBytes(),
  3656. softwareID.ToBytes(),
  3657. softVersion.ToBytes(),
  3658. keyColor.ToBytes(),
  3659. pixelAspectRatio.ToBytes(),
  3660. gammaValue.ToBytes(),
  3661. colorCorrectionOffset,
  3662. postageStampOffset,
  3663. scanLineOffset,
  3664. (byte)attributesType,
  3665. otherDataInExtensionArea);
  3666. }
  3667. } //Not full ToBytes()
  3668. /// <summary>
  3669. /// File Footer Area
  3670. /// </summary>
  3671. public class TgaFooter : ICloneable
  3672. {
  3673. uint extAreaOffset = 0;
  3674. uint devDirOffset = 0;
  3675. TgaString signature = TgaString.XFileSignatute;
  3676. TgaString reservedChar = TgaString.DotSymbol;
  3677. TgaString zeroStrTerminator = TgaString.ZeroTerminator;
  3678. /// <summary>
  3679. /// Make NewXFile format TGA Footer with <see cref="ExtensionAreaOffset"/> = 0 and
  3680. /// <see cref="DeveloperDirectoryOffset"/> = 0.
  3681. /// </summary>
  3682. public TgaFooter()
  3683. {
  3684. }
  3685. /// <summary>
  3686. /// Make <see cref="TgaFooter"/> from values.
  3687. /// </summary>
  3688. /// <param name="ExtOff">Extension Area Offset, offset from the beginning of the file.</param>
  3689. /// <param name="DevDirOff">Developer Directory Offset, offset from the beginning of the file.</param>
  3690. /// <param name="Sign">New TGA format signature.</param>
  3691. /// <param name="ReservChr">Reserved Character - ASCII character “.” (period).</param>
  3692. /// <param name="Termin">Binary Zero Terminator, a binary zero which acts as a final terminator.</param>
  3693. public TgaFooter(uint ExtOff, uint DevDirOff, TgaString Sign, TgaString ReservChr, TgaString Termin)
  3694. {
  3695. extAreaOffset = ExtOff;
  3696. devDirOffset = DevDirOff;
  3697. signature = Sign;
  3698. reservedChar = ReservChr;
  3699. zeroStrTerminator = Termin;
  3700. }
  3701. /// <summary>
  3702. /// Make <see cref="TgaFooter"/> from bytes (if signature is right).
  3703. /// </summary>
  3704. /// <param name="Bytes">Bytes array (byte[26]).</param>
  3705. public TgaFooter(byte[] Bytes)
  3706. {
  3707. if (Bytes == null)
  3708. throw new ArgumentNullException(nameof(Bytes) + " = null!");
  3709. if (Bytes.Length != Size)
  3710. throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
  3711. extAreaOffset = BitConverter.ToUInt32(Bytes, 0);
  3712. devDirOffset = BitConverter.ToUInt32(Bytes, 4);
  3713. signature = new TgaString(BitConverterExt.GetElements(Bytes, 8, TgaString.XFileSignatuteConst.Length));
  3714. reservedChar = new TgaString(new byte[] { Bytes[24] });
  3715. zeroStrTerminator = new TgaString(new byte[] { Bytes[25] });
  3716. }
  3717. /// <summary>
  3718. /// Byte 0-3 - Extension Area Offset - Field 28
  3719. /// The first four bytes (bytes 0-3, the first LONG) of the TGA File Footer contain an
  3720. /// offset from the beginning of the file to the start of the Extension Area. Simply
  3721. /// SEEK to this location to position to the start of the Extension Area. If the
  3722. /// Extension Area Offset is zero, no Extension Area exists in the file.
  3723. /// </summary>
  3724. public uint ExtensionAreaOffset
  3725. {
  3726. get { return extAreaOffset; }
  3727. set { extAreaOffset = value; }
  3728. }
  3729. /// <summary>
  3730. /// Byte 4-7 - Developer Directory Offset - Field 29
  3731. /// The next four bytes(bytes 4-7, the second LONG) contain an offset from the
  3732. /// beginning of the file to the start of the Developer Directory. If the Developer
  3733. /// Directory Offset is zero, then the Developer Area does not exist.
  3734. /// </summary>
  3735. public uint DeveloperDirectoryOffset
  3736. {
  3737. get { return devDirOffset; }
  3738. set { devDirOffset = value; }
  3739. }
  3740. /// <summary>
  3741. /// Byte 8-23 - Signature - Field 30
  3742. /// This string is exactly 16 bytes long and is formatted exactly as shown below
  3743. /// capital letters), with a hyphen between “TRUEVISION” and “XFILE.” If the
  3744. /// signature is detected, the file is assumed to be of the New TGA format and MAY,
  3745. /// therefore, contain the Developer Area and/or the Extension Area fields.If the
  3746. /// signature is not found, then the file is assumed to be in the Original TGA format.
  3747. /// </summary>
  3748. public TgaString Signature
  3749. {
  3750. get { return signature; }
  3751. set { signature = value; }
  3752. }
  3753. /// <summary>
  3754. /// Byte 24 - Reserved Character - Field 31
  3755. /// Byte 24 is an ASCII character “.” (period). This character MUST BE a period or
  3756. /// the file is not considered a proper TGA file.
  3757. /// </summary>
  3758. public TgaString ReservedCharacter
  3759. {
  3760. get { return reservedChar; }
  3761. set { reservedChar = value; }
  3762. }
  3763. /// <summary>
  3764. /// Byte 25 - Binary Zero String Terminator - Field 32
  3765. /// Byte 25 is a binary zero which acts as a final terminator and allows the entire TGA
  3766. /// File Footer to be read and utilized as a “C” string.
  3767. /// </summary>
  3768. public TgaString BinaryZeroStringTerminator
  3769. {
  3770. get { return zeroStrTerminator; }
  3771. set { zeroStrTerminator = value; }
  3772. }
  3773. /// <summary>
  3774. /// Make full copy of <see cref="TgaFooter"/>.
  3775. /// </summary>
  3776. /// <returns></returns>
  3777. public TgaFooter Clone()
  3778. {
  3779. return new TgaFooter(extAreaOffset, devDirOffset, signature.Clone(),
  3780. reservedChar.Clone(), zeroStrTerminator.Clone());
  3781. }
  3782. /// <summary>
  3783. /// Make full copy of <see cref="TgaFooter"/>.
  3784. /// </summary>
  3785. /// <returns></returns>
  3786. object ICloneable.Clone()
  3787. {
  3788. return Clone();
  3789. }
  3790. /// <summary>
  3791. /// Gets TGA Footer Section size in bytes.
  3792. /// </summary>
  3793. public const int Size = 26;
  3794. public override bool Equals(object obj)
  3795. {
  3796. return ((obj is TgaFooter) ? Equals((TgaFooter)obj) : false);
  3797. }
  3798. public bool Equals(TgaFooter item)
  3799. {
  3800. return (extAreaOffset == item.extAreaOffset &&
  3801. devDirOffset == item.devDirOffset &&
  3802. signature == item.signature &&
  3803. reservedChar == item.reservedChar &&
  3804. zeroStrTerminator == item.zeroStrTerminator);
  3805. }
  3806. public static bool operator ==(TgaFooter item1, TgaFooter item2)
  3807. {
  3808. if (ReferenceEquals(item1, null))
  3809. return ReferenceEquals(item2, null);
  3810. if (ReferenceEquals(item2, null))
  3811. return ReferenceEquals(item1, null);
  3812. return item1.Equals(item2);
  3813. }
  3814. public static bool operator !=(TgaFooter item1, TgaFooter item2)
  3815. {
  3816. return !(item1 == item2);
  3817. }
  3818. public override int GetHashCode()
  3819. {
  3820. unchecked
  3821. {
  3822. int hash = 17;
  3823. hash = hash * 23 + extAreaOffset.GetHashCode();
  3824. hash = hash * 23 + devDirOffset.GetHashCode();
  3825. if (signature != null)
  3826. hash = hash * 23 + signature.GetHashCode();
  3827. if (reservedChar != null)
  3828. hash = hash * 23 + reservedChar.GetHashCode();
  3829. if (zeroStrTerminator != null)
  3830. hash = hash * 23 + zeroStrTerminator.GetHashCode();
  3831. return hash;
  3832. }
  3833. }
  3834. public override string ToString()
  3835. {
  3836. return String.Format("{0}={1}, {2}={3}, FullSignature={4}",
  3837. nameof(ExtensionAreaOffset), extAreaOffset, nameof(DeveloperDirectoryOffset), devDirOffset,
  3838. (signature + reservedChar + zeroStrTerminator).ToString());
  3839. }
  3840. /// <summary>
  3841. /// Convert <see cref="TgaFooter"/> to byte array.
  3842. /// </summary>
  3843. /// <returns>Byte array with size equal <see cref="Size"/>.</returns>
  3844. public byte[] ToBytes()
  3845. {
  3846. return BitConverterExt.ToBytes(extAreaOffset, devDirOffset,
  3847. signature.ToBytes(), reservedChar.ToBytes(), zeroStrTerminator.ToBytes());
  3848. }
  3849. /// <summary>
  3850. /// Is footer is real footer of TGA File Format Version 2.0?
  3851. /// Checking by <see cref="TgaString.XFileSignatute"/>.
  3852. /// </summary>
  3853. public bool IsFooterCorrect
  3854. {
  3855. get { return signature == TgaString.XFileSignatute; }
  3856. }
  3857. }
  3858. ////////////////////////////////////////////////////////////////////////////////////////////////
  3859. /// <summary>
  3860. /// Simplify ByteConversion operations, like concatination of byte arrays, comparing and other.
  3861. /// </summary>
  3862. public static class BitConverterExt
  3863. {
  3864. /// <summary>
  3865. /// Combine byte, byte[], (u)short, (u)int, (u)long values to byte[] array.
  3866. /// </summary>
  3867. /// <param name="obj">Array of byte, byte[], (u)short, (u)int, (u)long values.</param>
  3868. /// <returns>Array of bytes, null when some object is null.</returns>
  3869. public static byte[] ToBytes(params object[] obj)
  3870. {
  3871. if (obj == null)
  3872. return null;
  3873. List<byte> BytesList = new List<byte>();
  3874. for (int i = 0; i < obj.Length; i++)
  3875. {
  3876. if (obj[i] == null)
  3877. continue;
  3878. else if (obj[i] is byte)
  3879. BytesList.Add((byte)obj[i]);
  3880. else if (obj[i] is byte[])
  3881. BytesList.AddRange((byte[])obj[i]);
  3882. else if (obj[i] is short)
  3883. BytesList.AddRange(BitConverter.GetBytes((short)obj[i]));
  3884. else if (obj[i] is ushort)
  3885. BytesList.AddRange(BitConverter.GetBytes((ushort)obj[i]));
  3886. else if (obj[i] is int)
  3887. BytesList.AddRange(BitConverter.GetBytes((int)obj[i]));
  3888. else if (obj[i] is uint)
  3889. BytesList.AddRange(BitConverter.GetBytes((uint)obj[i]));
  3890. else if (obj[i] is long)
  3891. BytesList.AddRange(BitConverter.GetBytes((long)obj[i]));
  3892. else if (obj[i] is ulong)
  3893. BytesList.AddRange(BitConverter.GetBytes((ulong)obj[i]));
  3894. }
  3895. return BytesList.ToArray();
  3896. }
  3897. /// <summary>
  3898. /// Copies a range of elements from an Array starting at the specified source index.
  3899. /// The length and the index are specified as 32-bit integers.
  3900. /// </summary>
  3901. /// <param name="SrcArray">The <see cref="Array"/> that contains the data to copy.</param>
  3902. /// <param name="Offset">A 32-bit integer that represents the index in the
  3903. /// <see cref="SrcArray"/> at which copying begins.</param>
  3904. /// <param name="Count">A 32-bit integer that represents the number of elements to copy.</param>
  3905. /// <returns></returns>
  3906. public static T[] GetElements<T>(T[] SrcArray, int Offset, int Count)
  3907. {
  3908. if (SrcArray == null)
  3909. throw new ArgumentNullException(nameof(SrcArray) + " is null!");
  3910. if (Offset >= SrcArray.Length || Offset < 0)
  3911. throw new ArgumentOutOfRangeException(nameof(Offset) + " has wrong value!");
  3912. if (Count <= 0 || Offset + Count > SrcArray.Length)
  3913. throw new ArgumentOutOfRangeException(nameof(Count) + " has wrong value!");
  3914. T[] Buff = new T[Count];
  3915. Array.Copy(SrcArray, Offset, Buff, 0, Buff.Length);
  3916. return Buff;
  3917. }
  3918. /// <summary>
  3919. /// Compare N-dimensional Arrays.
  3920. /// </summary>
  3921. /// <typeparam name="T">Arrays Type.</typeparam>
  3922. /// <param name="item1">First Array.</param>
  3923. /// <param name="item2">Second Array.</param>
  3924. /// <returns>True, if Arrays are equal.</returns>
  3925. public static bool IsArraysEqual<T>(T[] item1, T[] item2)
  3926. {
  3927. if (ReferenceEquals(item1, item2))
  3928. return true;
  3929. if (item1 == null || item2 == null)
  3930. return false;
  3931. if (item1.Length != item2.Length)
  3932. return false;
  3933. EqualityComparer<T> comparer = EqualityComparer<T>.Default;
  3934. for (int i = 0; i < item1.Length; i++)
  3935. if (!comparer.Equals(item1[i], item2[i]))
  3936. return false;
  3937. return true;
  3938. }
  3939. /// <summary>
  3940. /// Compare Lists.
  3941. /// </summary>
  3942. /// <typeparam name="T">List Type.</typeparam>
  3943. /// <param name="item1">First List.</param>
  3944. /// <param name="item2">Second List.</param>
  3945. /// <returns>True, if Lists are equal.</returns>
  3946. public static bool IsListsEqual<T>(List<T> item1, List<T> item2)
  3947. {
  3948. if (ReferenceEquals(item1, item2))
  3949. return true;
  3950. if (item1 == null || item2 == null)
  3951. return false;
  3952. if (item1.Count != item2.Count)
  3953. return false;
  3954. for (int i = 0; i < item1.Count; i++)
  3955. if (!item1[i].Equals(item2[i]))
  3956. return false;
  3957. return true;
  3958. }
  3959. /// <summary>
  3960. /// Compare elements in one Array with different offsets.
  3961. /// </summary>
  3962. /// <typeparam name="T">Array type.</typeparam>
  3963. /// <param name="Arr">Some Array.</param>
  3964. /// <param name="Offset1">First offset.</param>
  3965. /// <param name="Offset2">Second offset.</param>
  3966. /// <param name="Count">Elements count which must be compared.</param>
  3967. /// <returns></returns>
  3968. public static bool IsElementsEqual<T>(T[] Arr, int Offset1, int Offset2, int Count)
  3969. {
  3970. if (Arr == null)
  3971. throw new ArgumentNullException(nameof(Arr) + " is null!");
  3972. if (Offset1 >= Arr.Length || Offset1 < 0)
  3973. throw new ArgumentOutOfRangeException(nameof(Offset1) + " has wrong value!");
  3974. if (Offset2 >= Arr.Length || Offset2 < 0)
  3975. throw new ArgumentOutOfRangeException(nameof(Offset2) + " has wrong value!");
  3976. if (Count <= 0 || Offset1 + Count > Arr.Length || Offset2 + Count > Arr.Length)
  3977. throw new ArgumentOutOfRangeException(nameof(Count) + " has wrong value!");
  3978. if (Offset1 == Offset2)
  3979. return true;
  3980. for (int i = 0; i < Count; i++)
  3981. if (!Arr[Offset1 + i].Equals(Arr[Offset2 + i]))
  3982. return false;
  3983. return true;
  3984. }
  3985. }
  3986. #endregion
  3987. public class TGA : ICloneable
  3988. {
  3989. public TgaHeader Header = new TgaHeader();
  3990. public TgaImgOrColMap ImageOrColorMapArea = new TgaImgOrColMap();
  3991. public TgaDevArea DevArea = null;
  3992. public TgaExtArea ExtArea = null;
  3993. public TgaFooter Footer = null;
  3994. #region TGA Creation, Loading, Saving (all are public, have reference to private metods).
  3995. /// <summary>
  3996. /// Create new empty <see cref="TGA"/> istance.
  3997. /// </summary>
  3998. public TGA()
  3999. {
  4000. }
  4001. /// <summary>
  4002. /// Create <see cref="TGA"/> instance with some params. If it must have ColorMap,
  4003. /// check all ColorMap fields and settings after.
  4004. /// </summary>
  4005. /// <param name="Width">Image Width.</param>
  4006. /// <param name="Height">Image Height.</param>
  4007. /// <param name="PixDepth">Image Pixel Depth (bits / pixel), set ColorMap bpp after, if needed!</param>
  4008. /// <param name="ImgType">Image Type (is RLE compressed, ColorMapped or GrayScaled).</param>
  4009. /// <param name="AttrBits">Set numder of Attrbute bits (Alpha channel bits), default: 0, 1, 8.</param>
  4010. /// <param name="NewFormat">Use new 2.0 TGA XFile format?</param>
  4011. public TGA(ushort Width, ushort Height, TgaPixelDepth PixDepth = TgaPixelDepth.Bpp24,
  4012. TgaImageType ImgType = TgaImageType.Uncompressed_TrueColor, byte AttrBits = 0, bool NewFormat = true)
  4013. {
  4014. if (Width <= 0 || Height <= 0 || PixDepth == TgaPixelDepth.Other)
  4015. {
  4016. Width = Height = 0;
  4017. PixDepth = TgaPixelDepth.Other;
  4018. ImgType = TgaImageType.NoImageData;
  4019. AttrBits = 0;
  4020. }
  4021. else
  4022. {
  4023. int BytesPerPixel = (int)Math.Ceiling((double)PixDepth / 8.0);
  4024. ImageOrColorMapArea.ImageData = new byte[Width * Height * BytesPerPixel];
  4025. if (ImgType == TgaImageType.Uncompressed_ColorMapped || ImgType == TgaImageType.RLE_ColorMapped)
  4026. {
  4027. Header.ColorMapType = TgaColorMapType.ColorMap;
  4028. Header.ColorMapSpec.FirstEntryIndex = 0;
  4029. Header.ColorMapSpec.ColorMapEntrySize = (TgaColorMapEntrySize)Math.Ceiling((double)PixDepth / 8);
  4030. }
  4031. }
  4032. Header.ImageType = ImgType;
  4033. Header.ImageSpec.ImageWidth = Width;
  4034. Header.ImageSpec.ImageHeight = Height;
  4035. Header.ImageSpec.PixelDepth = PixDepth;
  4036. Header.ImageSpec.ImageDescriptor.AlphaChannelBits = AttrBits;
  4037. if (NewFormat)
  4038. {
  4039. Footer = new TgaFooter();
  4040. ExtArea = new TgaExtArea();
  4041. ExtArea.DateTimeStamp = new TgaDateTime(DateTime.UtcNow);
  4042. ExtArea.AttributesType = (AttrBits > 0 ? TgaAttrType.UsefulAlpha : TgaAttrType.NoAlpha);
  4043. }
  4044. }
  4045. /// <summary>
  4046. /// Make <see cref="TGA"/> from some <see cref="TGA"/> instance.
  4047. /// Equal to <see cref="TGA.Clone()"/> function.
  4048. /// </summary>
  4049. /// <param name="tga">Original <see cref="TGA"/> instance.</param>
  4050. public TGA(TGA tga)
  4051. {
  4052. Header = tga.Header.Clone();
  4053. ImageOrColorMapArea = tga.ImageOrColorMapArea.Clone();
  4054. DevArea = tga.DevArea.Clone();
  4055. ExtArea = tga.ExtArea.Clone();
  4056. Footer = tga.Footer.Clone();
  4057. }
  4058. /// <summary>
  4059. /// Load <see cref="TGA"/> from file.
  4060. /// </summary>
  4061. /// <param name="filename">Full path to TGA file.</param>
  4062. /// <returns>Loaded <see cref="TGA"/> file.</returns>
  4063. public TGA(string filename)
  4064. {
  4065. LoadFunc(filename);
  4066. }
  4067. /// <summary>
  4068. /// Make <see cref="TGA"/> from bytes array.
  4069. /// </summary>
  4070. /// <param name="bytes">Bytes array (same like TGA File).</param>
  4071. public TGA(byte[] bytes)
  4072. {
  4073. LoadFunc(bytes);
  4074. }
  4075. /// <summary>
  4076. /// Make <see cref="TGA"/> from <see cref="Stream"/>.
  4077. /// For file opening better use <see cref="FromFile(string)"/>.
  4078. /// </summary>
  4079. /// <param name="stream">Some stream. You can use a lot of Stream types, but Stream must support:
  4080. /// <see cref="Stream.CanSeek"/> and <see cref="Stream.CanRead"/>.</param>
  4081. public TGA(Stream stream)
  4082. {
  4083. LoadFunc(stream);
  4084. }
  4085. /// <summary>
  4086. /// Make <see cref="TGA"/> from <see cref="Bitmap"/>.
  4087. /// </summary>
  4088. /// <param name="bmp">Input Bitmap, supported a lot of bitmaps types: 8/15/16/24/32 Bpp's.</param>
  4089. /// <param name="UseRLE">Use RLE Compression?</param>
  4090. /// <param name="NewFormat">Use new 2.0 TGA XFile format?</param>
  4091. /// <param name="ColorMap2BytesEntry">Is Color Map Entry size equal 15 or 16 Bpp, else - 24 or 32.</param>
  4092. public TGA(Bitmap bmp, bool UseRLE = false, bool NewFormat = true, bool ColorMap2BytesEntry = false)
  4093. {
  4094. LoadFunc(bmp, UseRLE, NewFormat, ColorMap2BytesEntry);
  4095. }
  4096. /// <summary>
  4097. /// Load <see cref="TGA"/> from file.
  4098. /// </summary>
  4099. /// <param name="filename">Full path to TGA file.</param>
  4100. /// <returns>Loaded <see cref="TGA"/> file.</returns>
  4101. public static TGA FromFile(string filename)
  4102. {
  4103. return new TGA(filename);
  4104. }
  4105. /// <summary>
  4106. /// Make <see cref="TGA"/> from bytes array.
  4107. /// </summary>
  4108. /// <param name="bytes">Bytes array (same like TGA File).</param>
  4109. public static TGA FromBytes(byte[] bytes)
  4110. {
  4111. return new TGA(bytes);
  4112. }
  4113. /// <summary>
  4114. /// Make <see cref="TGA"/> from <see cref="Stream"/>.
  4115. /// For file opening better use <see cref="FromFile(string)"/>.
  4116. /// </summary>
  4117. /// <param name="stream">Some stream. You can use a lot of Stream types, but Stream must support:
  4118. /// <see cref="Stream.CanSeek"/> and <see cref="Stream.CanRead"/>.</param>
  4119. public static TGA FromStream(Stream stream)
  4120. {
  4121. return new TGA(stream);
  4122. }
  4123. /// <summary>
  4124. /// Make <see cref="TGA"/> from <see cref="Bitmap"/>.
  4125. /// </summary>
  4126. /// <param name="bmp">Input Bitmap, supported a lot of bitmaps types: 8/15/16/24/32 Bpp's.</param>
  4127. /// <param name="UseRLE">Use RLE Compression?</param>
  4128. /// <param name="NewFormat">Use new 2.0 TGA XFile format?</param>
  4129. /// <param name="ColorMap2BytesEntry">Is Color Map Entry size equal 15 or 16 Bpp, else - 24 or 32.</param>
  4130. public static TGA FromBitmap(Bitmap bmp, bool UseRLE = false,
  4131. bool NewFormat = true, bool ColorMap2BytesEntry = false)
  4132. {
  4133. return new TGA(bmp, UseRLE, NewFormat, ColorMap2BytesEntry);
  4134. }
  4135. /// <summary>
  4136. /// Save <see cref="TGA"/> to file.
  4137. /// </summary>
  4138. /// <param name="filename">Full path to file.</param>
  4139. /// <returns>Return "true", if all done or "false", if failed.</returns>
  4140. public bool Save(string filename)
  4141. {
  4142. try
  4143. {
  4144. bool Result = false;
  4145. using (FileStream Fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None))
  4146. {
  4147. using (MemoryStream Ms = new MemoryStream())
  4148. {
  4149. Result = SaveFunc(Ms);
  4150. Ms.WriteTo(Fs);
  4151. Fs.Flush();
  4152. }
  4153. }
  4154. return Result;
  4155. }
  4156. catch
  4157. {
  4158. return false;
  4159. }
  4160. }
  4161. /// <summary>
  4162. /// Save <see cref="TGA"/> to <see cref="Stream"/>.
  4163. /// </summary>
  4164. /// <param name="stream">Some stream, it must support: <see cref="Stream.CanWrite"/>.</param>
  4165. /// <returns>Return "true", if all done or "false", if failed.</returns>
  4166. public bool Save(Stream stream)
  4167. {
  4168. return SaveFunc(stream);
  4169. }
  4170. #endregion
  4171. /// <summary>
  4172. /// Gets or Sets Image Width (see <see cref="Header.ImageSpec.ImageWidth"/>).
  4173. /// </summary>
  4174. public ushort Width
  4175. {
  4176. get { return Header.ImageSpec.ImageWidth; }
  4177. set { Header.ImageSpec.ImageWidth = value; }
  4178. }
  4179. /// <summary>
  4180. /// Gets or Sets Image Height (see <see cref="Header.ImageSpec.ImageHeight"/>).
  4181. /// </summary>
  4182. public ushort Height
  4183. {
  4184. get { return Header.ImageSpec.ImageHeight; }
  4185. set { Header.ImageSpec.ImageHeight = value; }
  4186. }
  4187. /// <summary>
  4188. /// Gets or Sets <see cref="TGA"/> image Size.
  4189. /// </summary>
  4190. public Size Size
  4191. {
  4192. get { return new Size(Header.ImageSpec.ImageWidth, Header.ImageSpec.ImageHeight); }
  4193. set
  4194. {
  4195. Header.ImageSpec.ImageWidth = (ushort)value.Width;
  4196. Header.ImageSpec.ImageHeight = (ushort)value.Height;
  4197. }
  4198. }
  4199. /// <summary>
  4200. /// Make full independed copy of <see cref="TGA"/>.
  4201. /// </summary>
  4202. /// <returns>Full independed copy of <see cref="TGA"/>.</returns>
  4203. public TGA Clone()
  4204. {
  4205. return new TGA(this);
  4206. }
  4207. object ICloneable.Clone()
  4208. {
  4209. return Clone();
  4210. }
  4211. /// <summary>
  4212. /// Flip <see cref="TGA"/> directions, for more info see <see cref="TgaImgOrigin"/>.
  4213. /// </summary>
  4214. /// <param name="Horizontal">Flip horizontal.</param>
  4215. /// <param name="Vertical">Flip vertical.</param>
  4216. public void Flip(bool Horizontal = false, bool Vertical = false)
  4217. {
  4218. int NewOrigin = (int)Header.ImageSpec.ImageDescriptor.ImageOrigin;
  4219. NewOrigin = NewOrigin ^ ((Vertical ? 0x20 : 0) | (Horizontal ? 0x10 : 0));
  4220. Header.ImageSpec.ImageDescriptor.ImageOrigin = (TgaImgOrigin)NewOrigin;
  4221. }
  4222. /// <summary>
  4223. /// Get information from TGA image.
  4224. /// </summary>
  4225. /// <returns>MultiLine string with info fields (one per line).</returns>
  4226. public string GetInfo()
  4227. {
  4228. StringBuilder SB = new StringBuilder();
  4229. SB.AppendLine("Header:");
  4230. SB.AppendLine("\tID Length = " + Header.IDLength);
  4231. SB.AppendLine("\tImage Type = " + Header.ImageType);
  4232. SB.AppendLine("\tHeader -> ImageSpec:");
  4233. SB.AppendLine("\t\tImage Width = " + Header.ImageSpec.ImageWidth);
  4234. SB.AppendLine("\t\tImage Height = " + Header.ImageSpec.ImageHeight);
  4235. SB.AppendLine("\t\tPixel Depth = " + Header.ImageSpec.PixelDepth);
  4236. SB.AppendLine("\t\tImage Descriptor (AsByte) = " + Header.ImageSpec.ImageDescriptor.ToByte());
  4237. SB.AppendLine("\t\tImage Descriptor -> AttributeBits = " + Header.ImageSpec.ImageDescriptor.AlphaChannelBits);
  4238. SB.AppendLine("\t\tImage Descriptor -> ImageOrigin = " + Header.ImageSpec.ImageDescriptor.ImageOrigin);
  4239. SB.AppendLine("\t\tX_Origin = " + Header.ImageSpec.X_Origin);
  4240. SB.AppendLine("\t\tY_Origin = " + Header.ImageSpec.Y_Origin);
  4241. SB.AppendLine("\tColorMap Type = " + Header.ColorMapType);
  4242. SB.AppendLine("\tHeader -> ColorMapSpec:");
  4243. SB.AppendLine("\t\tColorMap Entry Size = " + Header.ColorMapSpec.ColorMapEntrySize);
  4244. SB.AppendLine("\t\tColorMap Length = " + Header.ColorMapSpec.ColorMapLength);
  4245. SB.AppendLine("\t\tFirstEntry Index = " + Header.ColorMapSpec.FirstEntryIndex);
  4246. SB.AppendLine("\nImage / Color Map Area:");
  4247. if (Header.IDLength > 0 && ImageOrColorMapArea.ImageID != null)
  4248. SB.AppendLine("\tImage ID = \"" + ImageOrColorMapArea.ImageID.GetString() + "\"");
  4249. else
  4250. SB.AppendLine("\tImage ID = null");
  4251. if (ImageOrColorMapArea.ImageData != null)
  4252. SB.AppendLine("\tImage Data Length = " + ImageOrColorMapArea.ImageData.Length);
  4253. else
  4254. SB.AppendLine("\tImage Data = null");
  4255. if (ImageOrColorMapArea.ColorMapData != null)
  4256. SB.AppendLine("\tColorMap Data Length = " + ImageOrColorMapArea.ColorMapData.Length);
  4257. else
  4258. SB.AppendLine("\tColorMap Data = null");
  4259. SB.AppendLine("\nDevelopers Area:");
  4260. if (DevArea != null)
  4261. SB.AppendLine("\tCount = " + DevArea.Count);
  4262. else
  4263. SB.AppendLine("\tDevArea = null");
  4264. SB.AppendLine("\nExtension Area:");
  4265. if (ExtArea != null)
  4266. {
  4267. SB.AppendLine("\tExtension Size = " + ExtArea.ExtensionSize);
  4268. SB.AppendLine("\tAuthor Name = \"" + ExtArea.AuthorName.GetString() + "\"");
  4269. SB.AppendLine("\tAuthor Comments = \"" + ExtArea.AuthorComments.GetString() + "\"");
  4270. SB.AppendLine("\tDate / Time Stamp = " + ExtArea.DateTimeStamp);
  4271. SB.AppendLine("\tJob Name / ID = \"" + ExtArea.JobNameOrID.GetString() + "\"");
  4272. SB.AppendLine("\tJob Time = " + ExtArea.JobTime);
  4273. SB.AppendLine("\tSoftware ID = \"" + ExtArea.SoftwareID.GetString() + "\"");
  4274. SB.AppendLine("\tSoftware Version = \"" + ExtArea.SoftVersion + "\"");
  4275. SB.AppendLine("\tKey Color = " + ExtArea.KeyColor);
  4276. SB.AppendLine("\tPixel Aspect Ratio = " + ExtArea.PixelAspectRatio);
  4277. SB.AppendLine("\tGamma Value = " + ExtArea.GammaValue);
  4278. SB.AppendLine("\tColor Correction Table Offset = " + ExtArea.ColorCorrectionTableOffset);
  4279. SB.AppendLine("\tPostage Stamp Offset = " + ExtArea.PostageStampOffset);
  4280. SB.AppendLine("\tScan Line Offset = " + ExtArea.ScanLineOffset);
  4281. SB.AppendLine("\tAttributes Type = " + ExtArea.AttributesType);
  4282. if (ExtArea.ScanLineTable != null)
  4283. SB.AppendLine("\tScan Line Table = " + ExtArea.ScanLineTable.Length);
  4284. else
  4285. SB.AppendLine("\tScan Line Table = null");
  4286. if (ExtArea.PostageStampImage != null)
  4287. SB.AppendLine("\tPostage Stamp Image: " + ExtArea.PostageStampImage.ToString());
  4288. else
  4289. SB.AppendLine("\tPostage Stamp Image = null");
  4290. SB.AppendLine("\tColor Correction Table = " + (ExtArea.ColorCorrectionTable != null));
  4291. }
  4292. else
  4293. SB.AppendLine("\tExtArea = null");
  4294. SB.AppendLine("\nFooter:");
  4295. if (Footer != null)
  4296. {
  4297. SB.AppendLine("\tExtension Area Offset = " + Footer.ExtensionAreaOffset);
  4298. SB.AppendLine("\tDeveloper Directory Offset = " + Footer.DeveloperDirectoryOffset);
  4299. SB.AppendLine("\tSignature (Full) = \"" + Footer.Signature.ToString() +
  4300. Footer.ReservedCharacter.ToString() + Footer.BinaryZeroStringTerminator.ToString() + "\"");
  4301. }
  4302. else
  4303. SB.AppendLine("\tFooter = null");
  4304. return SB.ToString();
  4305. }
  4306. /// <summary>
  4307. /// Check and update all fields with data length and offsets.
  4308. /// </summary>
  4309. /// <returns>Return "true", if all OK or "false", if checking failed.</returns>
  4310. public bool CheckAndUpdateOffsets(out string ErrorStr)
  4311. {
  4312. ErrorStr = String.Empty;
  4313. if (Header == null)
  4314. {
  4315. ErrorStr = "Header = null";
  4316. return false;
  4317. }
  4318. if (ImageOrColorMapArea == null)
  4319. {
  4320. ErrorStr = "ImageOrColorMapArea = null";
  4321. return false;
  4322. }
  4323. uint Offset = TgaHeader.Size; // Virtual Offset
  4324. #region Header
  4325. if (ImageOrColorMapArea.ImageID != null)
  4326. {
  4327. int StrMaxLen = 255;
  4328. if (ImageOrColorMapArea.ImageID.UseEndingChar)
  4329. StrMaxLen--;
  4330. Header.IDLength = (byte)Math.Min(ImageOrColorMapArea.ImageID.OriginalString.Length, StrMaxLen);
  4331. ImageOrColorMapArea.ImageID.Length = Header.IDLength;
  4332. Offset += Header.IDLength;
  4333. }
  4334. else
  4335. Header.IDLength = 0;
  4336. #endregion
  4337. #region ColorMap
  4338. if (Header.ColorMapType != TgaColorMapType.NoColorMap)
  4339. {
  4340. if (Header.ColorMapSpec == null)
  4341. {
  4342. ErrorStr = "Header.ColorMapSpec = null";
  4343. return false;
  4344. }
  4345. if (Header.ColorMapSpec.ColorMapLength == 0)
  4346. {
  4347. ErrorStr = "Header.ColorMapSpec.ColorMapLength = 0";
  4348. return false;
  4349. }
  4350. if (ImageOrColorMapArea.ColorMapData == null)
  4351. {
  4352. ErrorStr = "ImageOrColorMapArea.ColorMapData = null";
  4353. return false;
  4354. }
  4355. int CmBytesPerPixel = (int)Math.Ceiling((double)Header.ColorMapSpec.ColorMapEntrySize / 8.0);
  4356. int LenBytes = Header.ColorMapSpec.ColorMapLength * CmBytesPerPixel;
  4357. if (LenBytes != ImageOrColorMapArea.ColorMapData.Length)
  4358. {
  4359. ErrorStr = "ImageOrColorMapArea.ColorMapData.Length has wrong size!";
  4360. return false;
  4361. }
  4362. Offset += (uint)ImageOrColorMapArea.ColorMapData.Length;
  4363. }
  4364. #endregion
  4365. #region Image Data
  4366. int BytesPerPixel = 0;
  4367. if (Header.ImageType != TgaImageType.NoImageData)
  4368. {
  4369. if (Header.ImageSpec == null)
  4370. {
  4371. ErrorStr = "Header.ImageSpec = null";
  4372. return false;
  4373. }
  4374. if (Header.ImageSpec.ImageWidth == 0 || Header.ImageSpec.ImageHeight == 0)
  4375. {
  4376. ErrorStr = "Header.ImageSpec.ImageWidth = 0 or Header.ImageSpec.ImageHeight = 0";
  4377. return false;
  4378. }
  4379. if (ImageOrColorMapArea.ImageData == null)
  4380. {
  4381. ErrorStr = "ImageOrColorMapArea.ImageData = null";
  4382. return false;
  4383. }
  4384. BytesPerPixel = (int)Math.Ceiling((double)Header.ImageSpec.PixelDepth / 8.0);
  4385. if (Width * Height * BytesPerPixel != ImageOrColorMapArea.ImageData.Length)
  4386. {
  4387. ErrorStr = "ImageOrColorMapArea.ImageData.Length has wrong size!";
  4388. return false;
  4389. }
  4390. if (Header.ImageType >= TgaImageType.RLE_ColorMapped &&
  4391. Header.ImageType <= TgaImageType.RLE_BlackWhite)
  4392. {
  4393. byte[] RLE = RLE_Encode(ImageOrColorMapArea.ImageData, Width, Height);
  4394. if (RLE == null)
  4395. {
  4396. ErrorStr = "RLE Compressing error! Check Image Data size.";
  4397. return false;
  4398. }
  4399. Offset += (uint)RLE.Length;
  4400. RLE = null;
  4401. }
  4402. else
  4403. Offset += (uint)ImageOrColorMapArea.ImageData.Length;
  4404. }
  4405. #endregion
  4406. #region Footer, DevArea, ExtArea
  4407. if (Footer != null)
  4408. {
  4409. #region DevArea
  4410. if (DevArea != null)
  4411. {
  4412. int DevAreaCount = DevArea.Count;
  4413. for (int i = 0; i < DevAreaCount; i++)
  4414. if (DevArea[i] == null || DevArea[i].FieldSize <= 0) //Del Empty Entries
  4415. {
  4416. DevArea.Entries.RemoveAt(i);
  4417. DevAreaCount--;
  4418. i--;
  4419. }
  4420. if (DevArea.Count <= 0)
  4421. Footer.DeveloperDirectoryOffset = 0;
  4422. if (DevArea.Count > 2)
  4423. {
  4424. DevArea.Entries.Sort((a, b) => { return a.Tag.CompareTo(b.Tag); });
  4425. for (int i = 0; i < DevArea.Count - 1; i++)
  4426. if (DevArea[i].Tag == DevArea[i + 1].Tag)
  4427. {
  4428. ErrorStr = "DevArea Enties has same Tags!";
  4429. return false;
  4430. }
  4431. }
  4432. for (int i = 0; i < DevArea.Count; i++)
  4433. {
  4434. DevArea[i].Offset = Offset;
  4435. Offset += (uint)DevArea[i].FieldSize;
  4436. }
  4437. Footer.DeveloperDirectoryOffset = Offset;
  4438. Offset += (uint)(DevArea.Count * 10 + 2);
  4439. }
  4440. else
  4441. Footer.DeveloperDirectoryOffset = 0;
  4442. #endregion
  4443. #region ExtArea
  4444. if (ExtArea != null)
  4445. {
  4446. ExtArea.ExtensionSize = TgaExtArea.MinSize;
  4447. if (ExtArea.OtherDataInExtensionArea != null)
  4448. ExtArea.ExtensionSize += (ushort)ExtArea.OtherDataInExtensionArea.Length;
  4449. ExtArea.DateTimeStamp = new TgaDateTime(DateTime.UtcNow);
  4450. Footer.ExtensionAreaOffset = Offset;
  4451. Offset += ExtArea.ExtensionSize;
  4452. #region ScanLineTable
  4453. if (ExtArea.ScanLineTable == null)
  4454. ExtArea.ScanLineOffset = 0;
  4455. else
  4456. {
  4457. if (ExtArea.ScanLineTable.Length != Height)
  4458. {
  4459. ErrorStr = "ExtArea.ScanLineTable.Length != Height";
  4460. return false;
  4461. }
  4462. ExtArea.ScanLineOffset = Offset;
  4463. Offset += (uint)(ExtArea.ScanLineTable.Length * 4);
  4464. }
  4465. #endregion
  4466. #region PostageStampImage
  4467. if (ExtArea.PostageStampImage == null)
  4468. ExtArea.PostageStampOffset = 0;
  4469. else
  4470. {
  4471. if (ExtArea.PostageStampImage.Width == 0 || ExtArea.PostageStampImage.Height == 0)
  4472. {
  4473. ErrorStr = "ExtArea.PostageStampImage Width or Height is equal 0!";
  4474. return false;
  4475. }
  4476. if (ExtArea.PostageStampImage.Data == null)
  4477. {
  4478. ErrorStr = "ExtArea.PostageStampImage.Data == null";
  4479. return false;
  4480. }
  4481. int PImgSB = ExtArea.PostageStampImage.Width * ExtArea.PostageStampImage.Height * BytesPerPixel;
  4482. if (Header.ImageType != TgaImageType.NoImageData &&
  4483. ExtArea.PostageStampImage.Data.Length != PImgSB)
  4484. {
  4485. ErrorStr = "ExtArea.PostageStampImage.Data.Length is wrong!";
  4486. return false;
  4487. }
  4488. ExtArea.PostageStampOffset = Offset;
  4489. Offset += (uint)(ExtArea.PostageStampImage.Data.Length);
  4490. }
  4491. #endregion
  4492. #region ColorCorrectionTable
  4493. if (ExtArea.ColorCorrectionTable == null)
  4494. ExtArea.ColorCorrectionTableOffset = 0;
  4495. else
  4496. {
  4497. if (ExtArea.ColorCorrectionTable.Length != 1024)
  4498. {
  4499. ErrorStr = "ExtArea.ColorCorrectionTable.Length != 256 * 4";
  4500. return false;
  4501. }
  4502. ExtArea.ColorCorrectionTableOffset = Offset;
  4503. Offset += (uint)(ExtArea.ColorCorrectionTable.Length * 2);
  4504. }
  4505. #endregion
  4506. }
  4507. else
  4508. Footer.ExtensionAreaOffset = 0;
  4509. #endregion
  4510. #region Footer
  4511. if (Footer.ToBytes().Length != TgaFooter.Size)
  4512. {
  4513. ErrorStr = "Footer.Length is wrong!";
  4514. return false;
  4515. }
  4516. Offset += TgaFooter.Size;
  4517. #endregion
  4518. }
  4519. #endregion
  4520. return true;
  4521. }
  4522. #region Convert
  4523. /// <summary>
  4524. /// Convert <see cref="TGA"/> to <see cref="Bitmap"/>.
  4525. /// </summary>
  4526. /// <param name="ForceUseAlpha">Force use alpha channel.</param>
  4527. /// <returns>Bitmap or null, on error.</returns>
  4528. public Bitmap ToBitmap(bool ForceUseAlpha = false)
  4529. {
  4530. return ToBitmapFunc(ForceUseAlpha, false);
  4531. }
  4532. /// <summary>
  4533. /// Convert <see cref="TGA"/> to bytes array.
  4534. /// </summary>
  4535. /// <returns>Bytes array, (equal to saved file, but in memory) or null (on error).</returns>
  4536. public byte[] ToBytes()
  4537. {
  4538. try
  4539. {
  4540. byte[] Bytes;
  4541. using (MemoryStream ms = new MemoryStream())
  4542. {
  4543. Save(ms);
  4544. Bytes = ms.ToArray();
  4545. ms.Flush();
  4546. }
  4547. return Bytes;
  4548. }
  4549. catch
  4550. {
  4551. return null;
  4552. }
  4553. }
  4554. /// <summary>
  4555. /// Convert TGA Image to new XFile format (v2.0).
  4556. /// </summary>
  4557. public void ToNewFormat()
  4558. {
  4559. if (Footer == null)
  4560. Footer = new TgaFooter();
  4561. if (ExtArea == null)
  4562. {
  4563. ExtArea = new TgaExtArea();
  4564. ExtArea.DateTimeStamp = new TgaDateTime(DateTime.UtcNow);
  4565. if (Header.ImageSpec.ImageDescriptor.AlphaChannelBits > 0)
  4566. ExtArea.AttributesType = TgaAttrType.UsefulAlpha;
  4567. else
  4568. ExtArea.AttributesType = TgaAttrType.NoAlpha;
  4569. }
  4570. }
  4571. #endregion
  4572. #region Private functions
  4573. bool LoadFunc(string filename)
  4574. {
  4575. if (!File.Exists(filename))
  4576. throw new FileNotFoundException("File: \"" + filename + "\" not found!");
  4577. try
  4578. {
  4579. using (FileStream FS = new FileStream(filename, FileMode.Open, FileAccess.Read))
  4580. return LoadFunc(FS);
  4581. }
  4582. catch
  4583. {
  4584. return false;
  4585. }
  4586. }
  4587. bool LoadFunc(byte[] bytes)
  4588. {
  4589. if (bytes == null)
  4590. throw new ArgumentNullException();
  4591. try
  4592. {
  4593. using (MemoryStream FS = new MemoryStream(bytes, false))
  4594. return LoadFunc(FS);
  4595. }
  4596. catch
  4597. {
  4598. return false;
  4599. }
  4600. }
  4601. bool LoadFunc(Stream stream)
  4602. {
  4603. if (stream == null)
  4604. throw new ArgumentNullException();
  4605. if (!(stream.CanRead && stream.CanSeek))
  4606. throw new FileLoadException("Stream reading or seeking is not avaiable!");
  4607. try
  4608. {
  4609. stream.Seek(0, SeekOrigin.Begin);
  4610. BinaryReader Br = new BinaryReader(stream);
  4611. Header = new TgaHeader(Br.ReadBytes(TgaHeader.Size));
  4612. if (Header.IDLength > 0)
  4613. ImageOrColorMapArea.ImageID = new TgaString(Br.ReadBytes(Header.IDLength));
  4614. if (Header.ColorMapSpec.ColorMapLength > 0)
  4615. {
  4616. int CmBytesPerPixel = (int)Math.Ceiling((double)Header.ColorMapSpec.ColorMapEntrySize / 8.0);
  4617. int LenBytes = Header.ColorMapSpec.ColorMapLength * CmBytesPerPixel;
  4618. ImageOrColorMapArea.ColorMapData = Br.ReadBytes(LenBytes);
  4619. }
  4620. #region Read Image Data
  4621. int BytesPerPixel = (int)Math.Ceiling((double)Header.ImageSpec.PixelDepth / 8.0);
  4622. if (Header.ImageType != TgaImageType.NoImageData)
  4623. {
  4624. int ImageDataSize = Width * Height * BytesPerPixel;
  4625. switch (Header.ImageType)
  4626. {
  4627. case TgaImageType.RLE_ColorMapped:
  4628. case TgaImageType.RLE_TrueColor:
  4629. case TgaImageType.RLE_BlackWhite:
  4630. int DataOffset = 0;
  4631. byte PacketInfo;
  4632. int PacketCount;
  4633. byte[] RLE_Bytes, RLE_Part;
  4634. ImageOrColorMapArea.ImageData = new byte[ImageDataSize];
  4635. do
  4636. {
  4637. PacketInfo = Br.ReadByte(); //1 type bit and 7 count bits. Len = Count + 1.
  4638. PacketCount = (PacketInfo & 127) + 1;
  4639. if (PacketInfo >= 128) // bit7 = 1, RLE
  4640. {
  4641. RLE_Bytes = new byte[PacketCount * BytesPerPixel];
  4642. RLE_Part = Br.ReadBytes(BytesPerPixel);
  4643. for (int i = 0; i < RLE_Bytes.Length; i++)
  4644. RLE_Bytes[i] = RLE_Part[i % BytesPerPixel];
  4645. }
  4646. else // RAW format
  4647. RLE_Bytes = Br.ReadBytes(PacketCount * BytesPerPixel);
  4648. Buffer.BlockCopy(RLE_Bytes, 0, ImageOrColorMapArea.ImageData, DataOffset, RLE_Bytes.Length);
  4649. DataOffset += RLE_Bytes.Length;
  4650. }
  4651. while (DataOffset < ImageDataSize);
  4652. RLE_Bytes = null;
  4653. break;
  4654. case TgaImageType.Uncompressed_ColorMapped:
  4655. case TgaImageType.Uncompressed_TrueColor:
  4656. case TgaImageType.Uncompressed_BlackWhite:
  4657. ImageOrColorMapArea.ImageData = Br.ReadBytes(ImageDataSize);
  4658. break;
  4659. }
  4660. }
  4661. #endregion
  4662. #region Try parse Footer
  4663. stream.Seek(-TgaFooter.Size, SeekOrigin.End);
  4664. uint FooterOffset = (uint)stream.Position;
  4665. TgaFooter MbFooter = new TgaFooter(Br.ReadBytes(TgaFooter.Size));
  4666. if (MbFooter.IsFooterCorrect)
  4667. {
  4668. Footer = MbFooter;
  4669. uint DevDirOffset = Footer.DeveloperDirectoryOffset;
  4670. uint ExtAreaOffset = Footer.ExtensionAreaOffset;
  4671. #region If Dev Area exist, read it.
  4672. if (DevDirOffset != 0)
  4673. {
  4674. stream.Seek(DevDirOffset, SeekOrigin.Begin);
  4675. DevArea = new TgaDevArea();
  4676. uint NumberOfTags = Br.ReadUInt16();
  4677. ushort[] Tags = new ushort[NumberOfTags];
  4678. uint[] TagOffsets = new uint[NumberOfTags];
  4679. uint[] TagSizes = new uint[NumberOfTags];
  4680. for (int i = 0; i < NumberOfTags; i++)
  4681. {
  4682. Tags[i] = Br.ReadUInt16();
  4683. TagOffsets[i] = Br.ReadUInt32();
  4684. TagSizes[i] = Br.ReadUInt32();
  4685. }
  4686. for (int i = 0; i < NumberOfTags; i++)
  4687. {
  4688. stream.Seek(TagOffsets[i], SeekOrigin.Begin);
  4689. var Ent = new TgaDevEntry(Tags[i], TagOffsets[i], Br.ReadBytes((int)TagSizes[i]));
  4690. DevArea.Entries.Add(Ent);
  4691. }
  4692. Tags = null;
  4693. TagOffsets = null;
  4694. TagSizes = null;
  4695. }
  4696. #endregion
  4697. #region If Ext Area exist, read it.
  4698. if (ExtAreaOffset != 0)
  4699. {
  4700. stream.Seek(ExtAreaOffset, SeekOrigin.Begin);
  4701. ushort ExtAreaSize = Math.Max((ushort)TgaExtArea.MinSize, Br.ReadUInt16());
  4702. stream.Seek(ExtAreaOffset, SeekOrigin.Begin);
  4703. ExtArea = new TgaExtArea(Br.ReadBytes(ExtAreaSize));
  4704. if (ExtArea.ScanLineOffset > 0)
  4705. {
  4706. stream.Seek(ExtArea.ScanLineOffset, SeekOrigin.Begin);
  4707. ExtArea.ScanLineTable = new uint[Height];
  4708. for (int i = 0; i < ExtArea.ScanLineTable.Length; i++)
  4709. ExtArea.ScanLineTable[i] = Br.ReadUInt32();
  4710. }
  4711. if (ExtArea.PostageStampOffset > 0)
  4712. {
  4713. stream.Seek(ExtArea.PostageStampOffset, SeekOrigin.Begin);
  4714. byte W = Br.ReadByte();
  4715. byte H = Br.ReadByte();
  4716. int ImgDataSize = W * H * BytesPerPixel;
  4717. if (ImgDataSize > 0)
  4718. ExtArea.PostageStampImage = new TgaPostageStampImage(W, H, Br.ReadBytes(ImgDataSize));
  4719. }
  4720. if (ExtArea.ColorCorrectionTableOffset > 0)
  4721. {
  4722. stream.Seek(ExtArea.ColorCorrectionTableOffset, SeekOrigin.Begin);
  4723. ExtArea.ColorCorrectionTable = new ushort[256 * 4];
  4724. for (int i = 0; i < ExtArea.ColorCorrectionTable.Length; i++)
  4725. ExtArea.ColorCorrectionTable[i] = Br.ReadUInt16();
  4726. }
  4727. }
  4728. #endregion
  4729. }
  4730. #endregion
  4731. Br.Close();
  4732. return true;
  4733. }
  4734. catch
  4735. {
  4736. return false;
  4737. }
  4738. }
  4739. bool LoadFunc(Bitmap bmp, bool UseRLE = false, bool NewFormat = true, bool ColorMap2BytesEntry = false)
  4740. {
  4741. if (bmp == null)
  4742. throw new ArgumentNullException();
  4743. try
  4744. {
  4745. Header.ImageSpec.ImageWidth = (ushort)bmp.Width;
  4746. Header.ImageSpec.ImageHeight = (ushort)bmp.Height;
  4747. Header.ImageSpec.ImageDescriptor.ImageOrigin = TgaImgOrigin.TopLeft;
  4748. switch (bmp.PixelFormat)
  4749. {
  4750. case PixelFormat.Indexed:
  4751. case PixelFormat.Gdi:
  4752. case PixelFormat.Alpha:
  4753. case PixelFormat.Undefined:
  4754. case PixelFormat.PAlpha:
  4755. case PixelFormat.Extended:
  4756. case PixelFormat.Max:
  4757. case PixelFormat.Canonical:
  4758. case PixelFormat.Format16bppRgb565:
  4759. default:
  4760. throw new FormatException(nameof(PixelFormat) + " is not supported!");
  4761. case PixelFormat.Format1bppIndexed:
  4762. case PixelFormat.Format4bppIndexed:
  4763. case PixelFormat.Format8bppIndexed:
  4764. case PixelFormat.Format16bppGrayScale:
  4765. case PixelFormat.Format16bppRgb555:
  4766. case PixelFormat.Format16bppArgb1555:
  4767. case PixelFormat.Format24bppRgb:
  4768. case PixelFormat.Format32bppRgb:
  4769. case PixelFormat.Format32bppArgb:
  4770. case PixelFormat.Format32bppPArgb:
  4771. case PixelFormat.Format48bppRgb:
  4772. case PixelFormat.Format64bppArgb:
  4773. case PixelFormat.Format64bppPArgb:
  4774. int bpp = Math.Max(8, Image.GetPixelFormatSize(bmp.PixelFormat));
  4775. int BytesPP = bpp / 8;
  4776. if (bmp.PixelFormat == PixelFormat.Format16bppRgb555)
  4777. bpp = 15;
  4778. bool IsAlpha = Image.IsAlphaPixelFormat(bmp.PixelFormat);
  4779. bool IsPreAlpha = IsAlpha && bmp.PixelFormat.ToString().EndsWith("PArgb");
  4780. bool IsColorMapped = bmp.PixelFormat.ToString().EndsWith("Indexed");
  4781. Header.ImageSpec.PixelDepth = (TgaPixelDepth)(BytesPP * 8);
  4782. if (IsAlpha)
  4783. {
  4784. Header.ImageSpec.ImageDescriptor.AlphaChannelBits = (byte)(BytesPP * 2);
  4785. if (bmp.PixelFormat == PixelFormat.Format16bppArgb1555)
  4786. Header.ImageSpec.ImageDescriptor.AlphaChannelBits = 1;
  4787. }
  4788. #region ColorMap
  4789. bool IsGrayImage = (bmp.PixelFormat == PixelFormat.Format16bppGrayScale | IsColorMapped);
  4790. if (IsColorMapped && bmp.Palette != null)
  4791. {
  4792. Color[] Colors = bmp.Palette.Entries;
  4793. #region Analyze ColorMapType
  4794. int AlphaSum = 0;
  4795. bool ColorMapUseAlpha = false;
  4796. for (int i = 0; i < Colors.Length; i++)
  4797. {
  4798. IsGrayImage &= (Colors[i].R == Colors[i].G && Colors[i].G == Colors[i].B);
  4799. ColorMapUseAlpha |= (Colors[i].A < 248);
  4800. AlphaSum |= Colors[i].A;
  4801. }
  4802. ColorMapUseAlpha &= (AlphaSum > 0);
  4803. int CMapBpp = (ColorMap2BytesEntry ? 15 : 24) + (ColorMapUseAlpha ? (ColorMap2BytesEntry ? 1 : 8) : 0);
  4804. int CMBytesPP = (int)Math.Ceiling(CMapBpp / 8.0);
  4805. #endregion
  4806. Header.ColorMapSpec.ColorMapLength = Math.Min((ushort)Colors.Length, ushort.MaxValue);
  4807. Header.ColorMapSpec.ColorMapEntrySize = (TgaColorMapEntrySize)CMapBpp;
  4808. ImageOrColorMapArea.ColorMapData = new byte[Header.ColorMapSpec.ColorMapLength * CMBytesPP];
  4809. byte[] CMapEntry = new byte[CMBytesPP];
  4810. const float To5Bit = 32f / 256f; // Scale value from 8 to 5 bits.
  4811. for (int i = 0; i < Colors.Length; i++)
  4812. {
  4813. switch (Header.ColorMapSpec.ColorMapEntrySize)
  4814. {
  4815. case TgaColorMapEntrySize.A1R5G5B5:
  4816. case TgaColorMapEntrySize.X1R5G5B5:
  4817. int R = (int)(Colors[i].R * To5Bit);
  4818. int G = (int)(Colors[i].G * To5Bit) << 5;
  4819. int B = (int)(Colors[i].B * To5Bit) << 10;
  4820. int A = 0;
  4821. if (Header.ColorMapSpec.ColorMapEntrySize == TgaColorMapEntrySize.A1R5G5B5)
  4822. A = ((Colors[i].A & 0x80) << 15);
  4823. CMapEntry = BitConverter.GetBytes(A | R | G | B);
  4824. break;
  4825. case TgaColorMapEntrySize.R8G8B8:
  4826. CMapEntry[0] = Colors[i].B;
  4827. CMapEntry[1] = Colors[i].G;
  4828. CMapEntry[2] = Colors[i].R;
  4829. break;
  4830. case TgaColorMapEntrySize.A8R8G8B8:
  4831. CMapEntry[0] = Colors[i].B;
  4832. CMapEntry[1] = Colors[i].G;
  4833. CMapEntry[2] = Colors[i].R;
  4834. CMapEntry[3] = Colors[i].A;
  4835. break;
  4836. case TgaColorMapEntrySize.Other:
  4837. default:
  4838. break;
  4839. }
  4840. Buffer.BlockCopy(CMapEntry, 0, ImageOrColorMapArea.ColorMapData, i * CMBytesPP, CMBytesPP);
  4841. }
  4842. }
  4843. #endregion
  4844. #region ImageType
  4845. if (UseRLE)
  4846. {
  4847. if (IsGrayImage)
  4848. Header.ImageType = TgaImageType.RLE_BlackWhite;
  4849. else if (IsColorMapped)
  4850. Header.ImageType = TgaImageType.RLE_ColorMapped;
  4851. else
  4852. Header.ImageType = TgaImageType.RLE_TrueColor;
  4853. }
  4854. else
  4855. {
  4856. if (IsGrayImage)
  4857. Header.ImageType = TgaImageType.Uncompressed_BlackWhite;
  4858. else if (IsColorMapped)
  4859. Header.ImageType = TgaImageType.Uncompressed_ColorMapped;
  4860. else
  4861. Header.ImageType = TgaImageType.Uncompressed_TrueColor;
  4862. }
  4863. Header.ColorMapType = (IsColorMapped ? TgaColorMapType.ColorMap : TgaColorMapType.NoColorMap);
  4864. #endregion
  4865. #region NewFormat
  4866. if (NewFormat)
  4867. {
  4868. Footer = new TgaFooter();
  4869. ExtArea = new TgaExtArea();
  4870. ExtArea.DateTimeStamp = new TgaDateTime(DateTime.UtcNow);
  4871. if (IsAlpha)
  4872. {
  4873. ExtArea.AttributesType = TgaAttrType.UsefulAlpha;
  4874. if (IsPreAlpha)
  4875. ExtArea.AttributesType = TgaAttrType.PreMultipliedAlpha;
  4876. }
  4877. else
  4878. {
  4879. ExtArea.AttributesType = TgaAttrType.NoAlpha;
  4880. if (Header.ImageSpec.ImageDescriptor.AlphaChannelBits > 0)
  4881. ExtArea.AttributesType = TgaAttrType.UndefinedAlphaButShouldBeRetained;
  4882. }
  4883. }
  4884. #endregion
  4885. #region Bitmap width is aligned by 32 bits = 4 bytes! Delete it.
  4886. int StrideBytes = bmp.Width * BytesPP;
  4887. int PaddingBytes = (int)Math.Ceiling(StrideBytes / 4.0) * 4 - StrideBytes;
  4888. byte[] ImageData = new byte[(StrideBytes + PaddingBytes) * bmp.Height];
  4889. Rectangle Re = new Rectangle(0, 0, bmp.Width, bmp.Height);
  4890. BitmapData BmpData = bmp.LockBits(Re, ImageLockMode.ReadOnly, bmp.PixelFormat);
  4891. Marshal.Copy(BmpData.Scan0, ImageData, 0, ImageData.Length);
  4892. bmp.UnlockBits(BmpData);
  4893. BmpData = null;
  4894. if (PaddingBytes > 0) //Need delete bytes align
  4895. {
  4896. ImageOrColorMapArea.ImageData = new byte[StrideBytes * bmp.Height];
  4897. for (int i = 0; i < bmp.Height; i++)
  4898. Buffer.BlockCopy(ImageData, i * (StrideBytes + PaddingBytes),
  4899. ImageOrColorMapArea.ImageData, i * StrideBytes, StrideBytes);
  4900. }
  4901. else
  4902. ImageOrColorMapArea.ImageData = ImageData;
  4903. ImageData = null;
  4904. // Not official supported, but works (tested on 16bpp GrayScale test images)!
  4905. if (bmp.PixelFormat == PixelFormat.Format16bppGrayScale)
  4906. {
  4907. for (long i = 0; i < ImageOrColorMapArea.ImageData.Length; i++)
  4908. ImageOrColorMapArea.ImageData[i] ^= byte.MaxValue;
  4909. }
  4910. #endregion
  4911. break;
  4912. }
  4913. return true;
  4914. }
  4915. catch
  4916. {
  4917. return false;
  4918. }
  4919. }
  4920. bool SaveFunc(Stream stream)
  4921. {
  4922. try
  4923. {
  4924. if (stream == null)
  4925. throw new ArgumentNullException();
  4926. if (!(stream.CanWrite && stream.CanSeek))
  4927. throw new FileLoadException("Stream writing or seeking is not avaiable!");
  4928. string CheckResult;
  4929. if (!CheckAndUpdateOffsets(out CheckResult))
  4930. return false;
  4931. BinaryWriter Bw = new BinaryWriter(stream);
  4932. Bw.Write(Header.ToBytes());
  4933. if (ImageOrColorMapArea.ImageID != null)
  4934. Bw.Write(ImageOrColorMapArea.ImageID.ToBytes());
  4935. if (Header.ColorMapType != TgaColorMapType.NoColorMap)
  4936. Bw.Write(ImageOrColorMapArea.ColorMapData);
  4937. #region ImageData
  4938. if (Header.ImageType != TgaImageType.NoImageData)
  4939. {
  4940. if (Header.ImageType >= TgaImageType.RLE_ColorMapped &&
  4941. Header.ImageType <= TgaImageType.RLE_BlackWhite)
  4942. Bw.Write(RLE_Encode(ImageOrColorMapArea.ImageData, Width, Height));
  4943. else
  4944. Bw.Write(ImageOrColorMapArea.ImageData);
  4945. }
  4946. #endregion
  4947. #region Footer
  4948. if (Footer != null)
  4949. {
  4950. #region DevArea
  4951. if (DevArea != null)
  4952. {
  4953. for (int i = 0; i < DevArea.Count; i++)
  4954. Bw.Write(DevArea[i].Data);
  4955. Bw.Write((ushort)DevArea.Count);
  4956. for (int i = 0; i < DevArea.Count; i++)
  4957. {
  4958. Bw.Write(DevArea[i].Tag);
  4959. Bw.Write(DevArea[i].Offset);
  4960. Bw.Write(DevArea[i].FieldSize);
  4961. }
  4962. }
  4963. #endregion
  4964. #region ExtArea
  4965. if (ExtArea != null)
  4966. {
  4967. Bw.Write(ExtArea.ToBytes());
  4968. if (ExtArea.ScanLineTable != null)
  4969. for (int i = 0; i < ExtArea.ScanLineTable.Length; i++)
  4970. Bw.Write(ExtArea.ScanLineTable[i]);
  4971. if (ExtArea.PostageStampImage != null)
  4972. Bw.Write(ExtArea.PostageStampImage.ToBytes());
  4973. if (ExtArea.ColorCorrectionTable != null)
  4974. for (int i = 0; i < ExtArea.ColorCorrectionTable.Length; i++)
  4975. Bw.Write(ExtArea.ColorCorrectionTable[i]);
  4976. }
  4977. #endregion
  4978. Bw.Write(Footer.ToBytes());
  4979. }
  4980. #endregion
  4981. Bw.Flush();
  4982. stream.Flush();
  4983. return true;
  4984. }
  4985. catch
  4986. {
  4987. return false;
  4988. }
  4989. }
  4990. /// <summary>
  4991. /// Encode image with RLE compression (used RLE per line)!
  4992. /// </summary>
  4993. /// <param name="ImageData">Image data, bytes array with size = Width * Height * BytesPerPixel.</param>
  4994. /// <param name="Width">Image Width, must be > 0.</param>
  4995. /// <param name="Height">Image Height, must be > 0.</param>
  4996. /// <returns>Bytes array with RLE compressed image data.</returns>
  4997. byte[] RLE_Encode(byte[] ImageData, int Width, int Height)
  4998. {
  4999. if (ImageData == null)
  5000. throw new ArgumentNullException(nameof(ImageData) + "in null!");
  5001. if (Width <= 0 || Height <= 0)
  5002. throw new ArgumentOutOfRangeException(nameof(Width) + " and " + nameof(Height) + " must be > 0!");
  5003. int Bpp = ImageData.Length / Width / Height; // Bytes per pixel
  5004. int ScanLineSize = Width * Bpp;
  5005. if (ScanLineSize * Height != ImageData.Length)
  5006. throw new ArgumentOutOfRangeException("ImageData has wrong Length!");
  5007. try
  5008. {
  5009. int Count = 0;
  5010. int Pos = 0;
  5011. bool IsRLE = false;
  5012. List<byte> Encoded = new List<byte>();
  5013. byte[] RowData = new byte[ScanLineSize];
  5014. for (int y = 0; y < Height; y++)
  5015. {
  5016. Pos = 0;
  5017. Buffer.BlockCopy(ImageData, y * ScanLineSize, RowData, 0, ScanLineSize);
  5018. while (Pos < ScanLineSize)
  5019. {
  5020. if (Pos >= ScanLineSize - Bpp)
  5021. {
  5022. Encoded.Add(0);
  5023. Encoded.AddRange(BitConverterExt.GetElements(RowData, Pos, Bpp));
  5024. Pos += Bpp;
  5025. break;
  5026. }
  5027. Count = 0; //1
  5028. IsRLE = BitConverterExt.IsElementsEqual(RowData, Pos, Pos + Bpp, Bpp);
  5029. for (int i = Pos + Bpp; i < Math.Min(Pos + 128 * Bpp, ScanLineSize) - Bpp; i += Bpp)
  5030. {
  5031. if (IsRLE ^ BitConverterExt.IsElementsEqual(RowData, (IsRLE ? Pos : i), i + Bpp, Bpp))
  5032. {
  5033. //Count--;
  5034. break;
  5035. }
  5036. else
  5037. Count++;
  5038. }
  5039. int CountBpp = (Count + 1) * Bpp;
  5040. Encoded.Add((byte)(IsRLE ? Count | 128 : Count));
  5041. Encoded.AddRange(BitConverterExt.GetElements(RowData, Pos, (IsRLE ? Bpp : CountBpp)));
  5042. Pos += CountBpp;
  5043. }
  5044. }
  5045. return Encoded.ToArray();
  5046. }
  5047. catch
  5048. {
  5049. return null;
  5050. }
  5051. }
  5052. /// <summary>
  5053. /// Convert <see cref="TGA"/> to <see cref="Bitmap"/>.
  5054. /// </summary>
  5055. /// <param name="ForceUseAlpha">Force use alpha channel.</param>
  5056. /// <param name="PostageStampImage">Get Postage Stamp Image (Thumb) or get main image?</param>
  5057. /// <returns>Bitmap or null, on error.</returns>
  5058. Bitmap ToBitmapFunc(bool ForceUseAlpha = false, bool PostageStampImage = false)
  5059. {
  5060. try
  5061. {
  5062. #region UseAlpha?
  5063. bool UseAlpha = true;
  5064. if (ExtArea != null)
  5065. {
  5066. switch (ExtArea.AttributesType)
  5067. {
  5068. case TgaAttrType.NoAlpha:
  5069. case TgaAttrType.UndefinedAlphaCanBeIgnored:
  5070. case TgaAttrType.UndefinedAlphaButShouldBeRetained:
  5071. UseAlpha = false;
  5072. break;
  5073. case TgaAttrType.UsefulAlpha:
  5074. case TgaAttrType.PreMultipliedAlpha:
  5075. default:
  5076. break;
  5077. }
  5078. }
  5079. UseAlpha = (Header.ImageSpec.ImageDescriptor.AlphaChannelBits > 0 && UseAlpha) | ForceUseAlpha;
  5080. #endregion
  5081. #region IsGrayImage
  5082. bool IsGrayImage = Header.ImageType == TgaImageType.RLE_BlackWhite ||
  5083. Header.ImageType == TgaImageType.Uncompressed_BlackWhite;
  5084. #endregion
  5085. #region Get PixelFormat
  5086. PixelFormat PixFormat = PixelFormat.Format24bppRgb;
  5087. switch (Header.ImageSpec.PixelDepth)
  5088. {
  5089. case TgaPixelDepth.Bpp8:
  5090. PixFormat = PixelFormat.Format8bppIndexed;
  5091. break;
  5092. case TgaPixelDepth.Bpp16:
  5093. if (IsGrayImage)
  5094. PixFormat = PixelFormat.Format16bppGrayScale;
  5095. else
  5096. PixFormat = (UseAlpha ? PixelFormat.Format16bppArgb1555 : PixelFormat.Format16bppRgb555);
  5097. break;
  5098. case TgaPixelDepth.Bpp24:
  5099. PixFormat = PixelFormat.Format24bppRgb;
  5100. break;
  5101. case TgaPixelDepth.Bpp32:
  5102. if (UseAlpha)
  5103. {
  5104. var f = Footer;
  5105. if (ExtArea?.AttributesType == TgaAttrType.PreMultipliedAlpha)
  5106. PixFormat = PixelFormat.Format32bppPArgb;
  5107. else
  5108. PixFormat = PixelFormat.Format32bppArgb;
  5109. }
  5110. else
  5111. PixFormat = PixelFormat.Format32bppRgb;
  5112. break;
  5113. default:
  5114. PixFormat = PixelFormat.Undefined;
  5115. break;
  5116. }
  5117. #endregion
  5118. ushort BMP_Width = (PostageStampImage ? ExtArea.PostageStampImage.Width : Width);
  5119. ushort BMP_Height = (PostageStampImage ? ExtArea.PostageStampImage.Height : Height);
  5120. Bitmap BMP = new Bitmap(BMP_Width, BMP_Height, PixFormat);
  5121. #region ColorMap and GrayPalette
  5122. if (Header.ColorMapType == TgaColorMapType.ColorMap &&
  5123. (Header.ImageType == TgaImageType.RLE_ColorMapped ||
  5124. Header.ImageType == TgaImageType.Uncompressed_ColorMapped))
  5125. {
  5126. ColorPalette ColorMap = BMP.Palette;
  5127. Color[] CMapColors = ColorMap.Entries;
  5128. switch (Header.ColorMapSpec.ColorMapEntrySize)
  5129. {
  5130. case TgaColorMapEntrySize.X1R5G5B5:
  5131. case TgaColorMapEntrySize.A1R5G5B5:
  5132. const float To8Bit = 255f / 31f; // Scale value from 5 to 8 bits.
  5133. for (int i = 0; i < Math.Min(CMapColors.Length, Header.ColorMapSpec.ColorMapLength); i++)
  5134. {
  5135. ushort A1R5G5B5 = BitConverter.ToUInt16(ImageOrColorMapArea.ColorMapData, i * 2);
  5136. int A = (UseAlpha ? (A1R5G5B5 & 0x8000) >> 15 : 1) * 255; // (0 or 1) * 255
  5137. int R = (int)(((A1R5G5B5 & 0x7C00) >> 10) * To8Bit);
  5138. int G = (int)(((A1R5G5B5 & 0x3E0) >> 5) * To8Bit);
  5139. int B = (int)((A1R5G5B5 & 0x1F) * To8Bit);
  5140. CMapColors[i] = Color.FromArgb(A, R, G, B);
  5141. }
  5142. break;
  5143. case TgaColorMapEntrySize.R8G8B8:
  5144. for (int i = 0; i < Math.Min(CMapColors.Length, Header.ColorMapSpec.ColorMapLength); i++)
  5145. {
  5146. int Index = i * 3; //RGB = 3 bytes
  5147. int R = ImageOrColorMapArea.ColorMapData[Index + 2];
  5148. int G = ImageOrColorMapArea.ColorMapData[Index + 1];
  5149. int B = ImageOrColorMapArea.ColorMapData[Index];
  5150. CMapColors[i] = Color.FromArgb(R, G, B);
  5151. }
  5152. break;
  5153. case TgaColorMapEntrySize.A8R8G8B8:
  5154. for (int i = 0; i < Math.Min(CMapColors.Length, Header.ColorMapSpec.ColorMapLength); i++)
  5155. {
  5156. int ARGB = BitConverter.ToInt32(ImageOrColorMapArea.ColorMapData, i * 4);
  5157. CMapColors[i] = Color.FromArgb(UseAlpha ? ARGB | (0xFF << 24) : ARGB);
  5158. }
  5159. break;
  5160. default:
  5161. ColorMap = null;
  5162. break;
  5163. }
  5164. if (ColorMap != null)
  5165. BMP.Palette = ColorMap;
  5166. }
  5167. if (PixFormat == PixelFormat.Format8bppIndexed && IsGrayImage)
  5168. {
  5169. ColorPalette GrayPalette = BMP.Palette;
  5170. Color[] GrayColors = GrayPalette.Entries;
  5171. for (int i = 0; i < GrayColors.Length; i++)
  5172. GrayColors[i] = Color.FromArgb(i, i, i);
  5173. BMP.Palette = GrayPalette;
  5174. }
  5175. #endregion
  5176. #region Bitmap width must by aligned (align value = 32 bits = 4 bytes)!
  5177. byte[] ImageData;
  5178. int BytesPerPixel = (int)Math.Ceiling((double)Header.ImageSpec.PixelDepth / 8.0);
  5179. int StrideBytes = BMP.Width * BytesPerPixel;
  5180. int PaddingBytes = (int)Math.Ceiling(StrideBytes / 4.0) * 4 - StrideBytes;
  5181. if (PaddingBytes > 0) //Need bytes align
  5182. {
  5183. ImageData = new byte[(StrideBytes + PaddingBytes) * BMP.Height];
  5184. for (int i = 0; i < BMP.Height; i++)
  5185. Buffer.BlockCopy(PostageStampImage ? ExtArea.PostageStampImage.Data :
  5186. ImageOrColorMapArea.ImageData, i * StrideBytes, ImageData,
  5187. i * (StrideBytes + PaddingBytes), StrideBytes);
  5188. }
  5189. else
  5190. ImageData = BitConverterExt.ToBytes(PostageStampImage ? ExtArea.PostageStampImage.Data :
  5191. ImageOrColorMapArea.ImageData);
  5192. // Not official supported, but works (tested on 2 test images)!
  5193. if (PixFormat == PixelFormat.Format16bppGrayScale)
  5194. {
  5195. for (long i = 0; i < ImageData.Length; i++)
  5196. ImageData[i] ^= byte.MaxValue;
  5197. }
  5198. #endregion
  5199. Rectangle Re = new Rectangle(0, 0, BMP.Width, BMP.Height);
  5200. BitmapData BmpData = BMP.LockBits(Re, ImageLockMode.WriteOnly, BMP.PixelFormat);
  5201. Marshal.Copy(ImageData, 0, BmpData.Scan0, ImageData.Length);
  5202. BMP.UnlockBits(BmpData);
  5203. ImageData = null;
  5204. BmpData = null;
  5205. if (ExtArea != null && ExtArea.KeyColor.ToInt() != 0)
  5206. BMP.MakeTransparent(ExtArea.KeyColor.ToColor());
  5207. #region Flip Image
  5208. switch (Header.ImageSpec.ImageDescriptor.ImageOrigin)
  5209. {
  5210. case TgaImgOrigin.BottomLeft:
  5211. BMP.RotateFlip(RotateFlipType.RotateNoneFlipY);
  5212. break;
  5213. case TgaImgOrigin.BottomRight:
  5214. BMP.RotateFlip(RotateFlipType.RotateNoneFlipXY);
  5215. break;
  5216. case TgaImgOrigin.TopLeft:
  5217. default:
  5218. break;
  5219. case TgaImgOrigin.TopRight:
  5220. BMP.RotateFlip(RotateFlipType.RotateNoneFlipX);
  5221. break;
  5222. }
  5223. #endregion
  5224. return BMP;
  5225. }
  5226. catch
  5227. {
  5228. return null;
  5229. }
  5230. }
  5231. #endregion
  5232. #region Explicit
  5233. public static explicit operator Bitmap(TGA tga)
  5234. {
  5235. return tga.ToBitmap();
  5236. }
  5237. public static explicit operator TGA(Bitmap bmp)
  5238. {
  5239. return FromBitmap(bmp);
  5240. }
  5241. #endregion
  5242. #region PostageStamp Image
  5243. /// <summary>
  5244. /// Convert <see cref="TgaPostageStampImage"/> to <see cref="Bitmap"/>.
  5245. /// </summary>
  5246. /// <param name="ForceUseAlpha">Force use alpha channel.</param>
  5247. /// <returns>Bitmap or null.</returns>
  5248. public Bitmap GetPostageStampImage(bool ForceUseAlpha = false)
  5249. {
  5250. if (ExtArea == null || ExtArea.PostageStampImage == null || ExtArea.PostageStampImage.Data == null ||
  5251. ExtArea.PostageStampImage.Width <= 0 || ExtArea.PostageStampImage.Height <= 0)
  5252. return null;
  5253. return ToBitmapFunc(ForceUseAlpha, true);
  5254. }
  5255. /// <summary>
  5256. /// Update Postage Stamp Image or set it.
  5257. /// </summary>
  5258. public void UpdatePostageStampImage()
  5259. {
  5260. if (Header.ImageType == TgaImageType.NoImageData)
  5261. {
  5262. if (ExtArea != null)
  5263. ExtArea.PostageStampImage = null;
  5264. return;
  5265. }
  5266. ToNewFormat();
  5267. if (ExtArea.PostageStampImage == null)
  5268. ExtArea.PostageStampImage = new TgaPostageStampImage();
  5269. int PS_Width = Header.ImageSpec.ImageWidth;
  5270. int PS_Height = Header.ImageSpec.ImageHeight;
  5271. if (Width > 64 || Height > 64)
  5272. {
  5273. float AspectRatio = Width / (float)Height;
  5274. PS_Width = (byte)(64f * (AspectRatio < 1f ? AspectRatio : 1f));
  5275. PS_Height = (byte)(64f / (AspectRatio > 1f ? AspectRatio : 1f));
  5276. }
  5277. PS_Width = Math.Max(PS_Width, 4);
  5278. PS_Height = Math.Max(PS_Height, 4);
  5279. ExtArea.PostageStampImage.Width = (byte)PS_Width;
  5280. ExtArea.PostageStampImage.Height = (byte)PS_Height;
  5281. int BytesPerPixel = (int)Math.Ceiling((double)Header.ImageSpec.PixelDepth / 8.0);
  5282. ExtArea.PostageStampImage.Data = new byte[PS_Width * PS_Height * BytesPerPixel];
  5283. float WidthCoef = Width / (float)PS_Width;
  5284. float HeightCoef = Height / (float)PS_Height;
  5285. for (int y = 0; y < PS_Height; y++)
  5286. {
  5287. int Y_Offset = (int)(y * HeightCoef) * Width * BytesPerPixel;
  5288. int y_Offset = y * PS_Width * BytesPerPixel;
  5289. for (int x = 0; x < PS_Width; x++)
  5290. {
  5291. Buffer.BlockCopy(ImageOrColorMapArea.ImageData, Y_Offset + (int)(x * WidthCoef) * BytesPerPixel,
  5292. ExtArea.PostageStampImage.Data, y_Offset + x * BytesPerPixel, BytesPerPixel);
  5293. }
  5294. }
  5295. }
  5296. public void DeletePostageStampImage()
  5297. {
  5298. if (ExtArea != null)
  5299. ExtArea.PostageStampImage = null;
  5300. }
  5301. #endregion
  5302. }
  5303. }