1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977 |
- //
- // Copyright 2020 Electronic Arts Inc.
- //
- // The Command & Conquer Map Editor and corresponding source code is free
- // software: you can redistribute it and/or modify it under the terms of
- // the GNU General Public License as published by the Free Software Foundation,
- // either version 3 of the License, or (at your option) any later version.
- // The Command & Conquer Map Editor and corresponding source code is distributed
- // in the hope that it will be useful, but with permitted additional restrictions
- // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
- // distributed with this program. You should have received a copy of the
- // GNU General Public License along with permitted additional restrictions
- // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
- /* MIT License
- Copyright (c) 2017 TGASharpLib
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
- */
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Drawing;
- using System.Drawing.Imaging;
- using System.IO;
- using System.Runtime.InteropServices;
- namespace TGASharpLib
- {
- #region Enums
- /// <summary>
- /// <para>The first 128 Color Map Type codes are reserved for use by Truevision,
- /// while the second set of 128 Color Map Type codes(128 to 255) may be used for
- /// developer applications.</para>
- /// True-Color images do not normally make use of the color map field, but some current
- /// applications store palette information or developer-defined information in this field.
- /// It is best to check Field 3, Image Type, to make sure you have a file which can use the
- /// data stored in the Color Map Field.
- /// Otherwise ignore the information. When saving or creating files for True-Color
- /// images do not use this field and set it to Zero to ensure compatibility. Please refer
- /// to the Developer Area specification for methods of storing developer defined information.
- /// </summary>
- public enum TgaColorMapType : byte
- {
- NoColorMap = 0,
- ColorMap = 1,
- Truevision_2,
- Truevision_3,
- Truevision_4,
- Truevision_5,
- Truevision_6,
- Truevision_7,
- Truevision_8,
- Truevision_9,
- Truevision_10,
- Truevision_11,
- Truevision_12,
- Truevision_13,
- Truevision_14,
- Truevision_15,
- Truevision_16,
- Truevision_17,
- Truevision_18,
- Truevision_19,
- Truevision_20,
- Truevision_21,
- Truevision_22,
- Truevision_23,
- Truevision_24,
- Truevision_25,
- Truevision_26,
- Truevision_27,
- Truevision_28,
- Truevision_29,
- Truevision_30,
- Truevision_31,
- Truevision_32,
- Truevision_33,
- Truevision_34,
- Truevision_35,
- Truevision_36,
- Truevision_37,
- Truevision_38,
- Truevision_39,
- Truevision_40,
- Truevision_41,
- Truevision_42,
- Truevision_43,
- Truevision_44,
- Truevision_45,
- Truevision_46,
- Truevision_47,
- Truevision_48,
- Truevision_49,
- Truevision_50,
- Truevision_51,
- Truevision_52,
- Truevision_53,
- Truevision_54,
- Truevision_55,
- Truevision_56,
- Truevision_57,
- Truevision_58,
- Truevision_59,
- Truevision_60,
- Truevision_61,
- Truevision_62,
- Truevision_63,
- Truevision_64,
- Truevision_65,
- Truevision_66,
- Truevision_67,
- Truevision_68,
- Truevision_69,
- Truevision_70,
- Truevision_71,
- Truevision_72,
- Truevision_73,
- Truevision_74,
- Truevision_75,
- Truevision_76,
- Truevision_77,
- Truevision_78,
- Truevision_79,
- Truevision_80,
- Truevision_81,
- Truevision_82,
- Truevision_83,
- Truevision_84,
- Truevision_85,
- Truevision_86,
- Truevision_87,
- Truevision_88,
- Truevision_89,
- Truevision_90,
- Truevision_91,
- Truevision_92,
- Truevision_93,
- Truevision_94,
- Truevision_95,
- Truevision_96,
- Truevision_97,
- Truevision_98,
- Truevision_99,
- Truevision_100,
- Truevision_101,
- Truevision_102,
- Truevision_103,
- Truevision_104,
- Truevision_105,
- Truevision_106,
- Truevision_107,
- Truevision_108,
- Truevision_109,
- Truevision_110,
- Truevision_111,
- Truevision_112,
- Truevision_113,
- Truevision_114,
- Truevision_115,
- Truevision_116,
- Truevision_117,
- Truevision_118,
- Truevision_119,
- Truevision_120,
- Truevision_121,
- Truevision_122,
- Truevision_123,
- Truevision_124,
- Truevision_125,
- Truevision_126,
- Truevision_127,
- Other_128,
- Other_129,
- Other_130,
- Other_131,
- Other_132,
- Other_133,
- Other_134,
- Other_135,
- Other_136,
- Other_137,
- Other_138,
- Other_139,
- Other_140,
- Other_141,
- Other_142,
- Other_143,
- Other_144,
- Other_145,
- Other_146,
- Other_147,
- Other_148,
- Other_149,
- Other_150,
- Other_151,
- Other_152,
- Other_153,
- Other_154,
- Other_155,
- Other_156,
- Other_157,
- Other_158,
- Other_159,
- Other_160,
- Other_161,
- Other_162,
- Other_163,
- Other_164,
- Other_165,
- Other_166,
- Other_167,
- Other_168,
- Other_169,
- Other_170,
- Other_171,
- Other_172,
- Other_173,
- Other_174,
- Other_175,
- Other_176,
- Other_177,
- Other_178,
- Other_179,
- Other_180,
- Other_181,
- Other_182,
- Other_183,
- Other_184,
- Other_185,
- Other_186,
- Other_187,
- Other_188,
- Other_189,
- Other_190,
- Other_191,
- Other_192,
- Other_193,
- Other_194,
- Other_195,
- Other_196,
- Other_197,
- Other_198,
- Other_199,
- Other_200,
- Other_201,
- Other_202,
- Other_203,
- Other_204,
- Other_205,
- Other_206,
- Other_207,
- Other_208,
- Other_209,
- Other_210,
- Other_211,
- Other_212,
- Other_213,
- Other_214,
- Other_215,
- Other_216,
- Other_217,
- Other_218,
- Other_219,
- Other_220,
- Other_221,
- Other_222,
- Other_223,
- Other_224,
- Other_225,
- Other_226,
- Other_227,
- Other_228,
- Other_229,
- Other_230,
- Other_231,
- Other_232,
- Other_233,
- Other_234,
- Other_235,
- Other_236,
- Other_237,
- Other_238,
- Other_239,
- Other_240,
- Other_241,
- Other_242,
- Other_243,
- Other_244,
- Other_245,
- Other_246,
- Other_247,
- Other_248,
- Other_249,
- Other_250,
- Other_251,
- Other_252,
- Other_253,
- Other_254,
- Other_255
- }
- /// <summary>
- /// Establishes the number of bits per entry. Typically 15, 16, 24 or 32-bit values are used.
- /// <para>When working with VDA or VDA/D cards it is preferred that you select 16 bits(5 bits
- /// per primary with 1 bit to select interrupt control) and set the 16th bit to 0 so that the
- /// interrupt bit is disabled. Even if this field is set to 15 bits(5 bits per primary) you
- /// must still parse the color map data 16 bits at a time and ignore the 16th bit.</para>
- /// <para>When working with a TARGA M8 card you would select 24 bits (8 bits per primary)
- /// since the color map is defined as 256 entries of 24 bit color values.</para>
- /// When working with a TrueVista card(ATVista or NuVista) you would select 24-bit(8 bits per
- /// primary) or 32-bit(8 bits per primary including Alpha channel) depending on your
- /// application’s use of look-up tables. It is suggested that when working with 16-bit and
- /// 32-bit color images, you store them as True-Color images and do not use the color map
- /// field to store look-up tables. Please refer to the TGA Extensions for fields better suited
- /// to storing look-up table information.
- /// </summary>
- public enum TgaColorMapEntrySize : byte
- {
- Other = 0,
- X1R5G5B5 = 15,
- A1R5G5B5 = 16,
- R8G8B8 = 24,
- A8R8G8B8 = 32
- }
- /// <summary>
- /// Truevision has currently defined seven image types:
- /// <para>0 - No Image Data Included;</para>
- /// <para>1 - Uncompressed, Color-mapped Image;</para>
- /// <para>2 - Uncompressed, True-color Image;</para>
- /// <para>3 - Uncompressed, Black-and-white Image;</para>
- /// <para>9 - Run-length encoded, Color-mapped Image;</para>
- /// <para>10 - Run-length encoded, True-color Image;</para>
- /// <para>11 - Run-length encoded, Black-and-white Image.</para>
- /// Image Data Type codes 0 to 127 are reserved for use by Truevision for general applications.
- /// Image Data Type codes 128 to 255 may be used for developer applications.
- /// </summary>
- public enum TgaImageType : byte
- {
- NoImageData = 0,
- Uncompressed_ColorMapped = 1,
- Uncompressed_TrueColor,
- Uncompressed_BlackWhite,
- _Truevision_4,
- _Truevision_5,
- _Truevision_6,
- _Truevision_7,
- _Truevision_8,
- RLE_ColorMapped = 9,
- RLE_TrueColor,
- RLE_BlackWhite,
- _Truevision_12,
- _Truevision_13,
- _Truevision_14,
- _Truevision_15,
- _Truevision_16,
- _Truevision_17,
- _Truevision_18,
- _Truevision_19,
- _Truevision_20,
- _Truevision_21,
- _Truevision_22,
- _Truevision_23,
- _Truevision_24,
- _Truevision_25,
- _Truevision_26,
- _Truevision_27,
- _Truevision_28,
- _Truevision_29,
- _Truevision_30,
- _Truevision_31,
- _Truevision_32,
- _Truevision_33,
- _Truevision_34,
- _Truevision_35,
- _Truevision_36,
- _Truevision_37,
- _Truevision_38,
- _Truevision_39,
- _Truevision_40,
- _Truevision_41,
- _Truevision_42,
- _Truevision_43,
- _Truevision_44,
- _Truevision_45,
- _Truevision_46,
- _Truevision_47,
- _Truevision_48,
- _Truevision_49,
- _Truevision_50,
- _Truevision_51,
- _Truevision_52,
- _Truevision_53,
- _Truevision_54,
- _Truevision_55,
- _Truevision_56,
- _Truevision_57,
- _Truevision_58,
- _Truevision_59,
- _Truevision_60,
- _Truevision_61,
- _Truevision_62,
- _Truevision_63,
- _Truevision_64,
- _Truevision_65,
- _Truevision_66,
- _Truevision_67,
- _Truevision_68,
- _Truevision_69,
- _Truevision_70,
- _Truevision_71,
- _Truevision_72,
- _Truevision_73,
- _Truevision_74,
- _Truevision_75,
- _Truevision_76,
- _Truevision_77,
- _Truevision_78,
- _Truevision_79,
- _Truevision_80,
- _Truevision_81,
- _Truevision_82,
- _Truevision_83,
- _Truevision_84,
- _Truevision_85,
- _Truevision_86,
- _Truevision_87,
- _Truevision_88,
- _Truevision_89,
- _Truevision_90,
- _Truevision_91,
- _Truevision_92,
- _Truevision_93,
- _Truevision_94,
- _Truevision_95,
- _Truevision_96,
- _Truevision_97,
- _Truevision_98,
- _Truevision_99,
- _Truevision_100,
- _Truevision_101,
- _Truevision_102,
- _Truevision_103,
- _Truevision_104,
- _Truevision_105,
- _Truevision_106,
- _Truevision_107,
- _Truevision_108,
- _Truevision_109,
- _Truevision_110,
- _Truevision_111,
- _Truevision_112,
- _Truevision_113,
- _Truevision_114,
- _Truevision_115,
- _Truevision_116,
- _Truevision_117,
- _Truevision_118,
- _Truevision_119,
- _Truevision_120,
- _Truevision_121,
- _Truevision_122,
- _Truevision_123,
- _Truevision_124,
- _Truevision_125,
- _Truevision_126,
- _Truevision_127,
- _Other_128,
- _Other_129,
- _Other_130,
- _Other_131,
- _Other_132,
- _Other_133,
- _Other_134,
- _Other_135,
- _Other_136,
- _Other_137,
- _Other_138,
- _Other_139,
- _Other_140,
- _Other_141,
- _Other_142,
- _Other_143,
- _Other_144,
- _Other_145,
- _Other_146,
- _Other_147,
- _Other_148,
- _Other_149,
- _Other_150,
- _Other_151,
- _Other_152,
- _Other_153,
- _Other_154,
- _Other_155,
- _Other_156,
- _Other_157,
- _Other_158,
- _Other_159,
- _Other_160,
- _Other_161,
- _Other_162,
- _Other_163,
- _Other_164,
- _Other_165,
- _Other_166,
- _Other_167,
- _Other_168,
- _Other_169,
- _Other_170,
- _Other_171,
- _Other_172,
- _Other_173,
- _Other_174,
- _Other_175,
- _Other_176,
- _Other_177,
- _Other_178,
- _Other_179,
- _Other_180,
- _Other_181,
- _Other_182,
- _Other_183,
- _Other_184,
- _Other_185,
- _Other_186,
- _Other_187,
- _Other_188,
- _Other_189,
- _Other_190,
- _Other_191,
- _Other_192,
- _Other_193,
- _Other_194,
- _Other_195,
- _Other_196,
- _Other_197,
- _Other_198,
- _Other_199,
- _Other_200,
- _Other_201,
- _Other_202,
- _Other_203,
- _Other_204,
- _Other_205,
- _Other_206,
- _Other_207,
- _Other_208,
- _Other_209,
- _Other_210,
- _Other_211,
- _Other_212,
- _Other_213,
- _Other_214,
- _Other_215,
- _Other_216,
- _Other_217,
- _Other_218,
- _Other_219,
- _Other_220,
- _Other_221,
- _Other_222,
- _Other_223,
- _Other_224,
- _Other_225,
- _Other_226,
- _Other_227,
- _Other_228,
- _Other_229,
- _Other_230,
- _Other_231,
- _Other_232,
- _Other_233,
- _Other_234,
- _Other_235,
- _Other_236,
- _Other_237,
- _Other_238,
- _Other_239,
- _Other_240,
- _Other_241,
- _Other_242,
- _Other_243,
- _Other_244,
- _Other_245,
- _Other_246,
- _Other_247,
- _Other_248,
- _Other_249,
- _Other_250,
- _Other_251,
- _Other_252,
- _Other_253,
- _Other_254,
- _Other_255
- }
- /// <summary>
- /// Number of bits per pixel. This number includes the Attribute or Alpha channel bits.
- /// Common values are 8, 16, 24 and 32 but other pixel depths could be used.
- /// </summary>
- public enum TgaPixelDepth : byte
- {
- Other = 0,
- Bpp8 = 8,
- Bpp16 = 16,
- Bpp24 = 24,
- Bpp32 = 32
- }
- /// <summary>
- /// Used to indicate the order in which pixel data is transferred from the file to the screen.
- /// (Bit 4 (bit 0 in enum) is for left-to-right ordering and bit 5 (bit 1 in enum) is for
- /// topto-bottom ordering as shown below.)
- /// </summary>
- public enum TgaImgOrigin : byte
- {
- BottomLeft = 0,
- BottomRight,
- TopLeft,
- TopRight
- }
- /// <summary>
- /// Contains a value which specifies the type of Alpha channel
- /// data contained in the file. Value Meaning:
- /// <para>0: no Alpha data included (bits 3-0 of field 5.6 should also be set to zero)</para>
- /// <para>1: undefined data in the Alpha field, can be ignored</para>
- /// <para>2: undefined data in the Alpha field, but should be retained</para>
- /// <para>3: useful Alpha channel data is present</para>
- /// <para>4: pre-multiplied Alpha(see description below)</para>
- /// <para>5 -127: RESERVED</para>
- /// <para>128-255: Un-assigned</para>
- /// <para>Pre-multiplied Alpha Example: Suppose the Alpha channel data is being used to specify the
- /// opacity of each pixel(for use when the image is overlayed on another image), where 0 indicates
- /// that the pixel is completely transparent and a value of 1 indicates that the pixel is
- /// completely opaque(assume all component values have been normalized).</para>
- /// <para>A quadruple(a, r, g, b) of( 0.5, 1, 0, 0) would indicate that the pixel is pure red with a
- /// transparency of one-half. For numerous reasons(including image compositing) is is better to
- /// pre-multiply the individual color components with the value in the Alpha channel.</para>
- /// A pre-multiplication of the above would produce a quadruple(0.5, 0.5, 0, 0).
- /// A value of 3 in the Attributes Type Field(field 23) would indicate that the color components
- /// of the pixel have already been scaled by the value in the Alpha channel.
- /// </summary>
- public enum TgaAttrType : byte
- {
- NoAlpha = 0,
- UndefinedAlphaCanBeIgnored,
- UndefinedAlphaButShouldBeRetained,
- UsefulAlpha,
- PreMultipliedAlpha,
- _Reserved_5,
- _Reserved_6,
- _Reserved_7,
- _Reserved_8,
- _Reserved_9,
- _Reserved_10,
- _Reserved_11,
- _Reserved_12,
- _Reserved_13,
- _Reserved_14,
- _Reserved_15,
- _Reserved_16,
- _Reserved_17,
- _Reserved_18,
- _Reserved_19,
- _Reserved_20,
- _Reserved_21,
- _Reserved_22,
- _Reserved_23,
- _Reserved_24,
- _Reserved_25,
- _Reserved_26,
- _Reserved_27,
- _Reserved_28,
- _Reserved_29,
- _Reserved_30,
- _Reserved_31,
- _Reserved_32,
- _Reserved_33,
- _Reserved_34,
- _Reserved_35,
- _Reserved_36,
- _Reserved_37,
- _Reserved_38,
- _Reserved_39,
- _Reserved_40,
- _Reserved_41,
- _Reserved_42,
- _Reserved_43,
- _Reserved_44,
- _Reserved_45,
- _Reserved_46,
- _Reserved_47,
- _Reserved_48,
- _Reserved_49,
- _Reserved_50,
- _Reserved_51,
- _Reserved_52,
- _Reserved_53,
- _Reserved_54,
- _Reserved_55,
- _Reserved_56,
- _Reserved_57,
- _Reserved_58,
- _Reserved_59,
- _Reserved_60,
- _Reserved_61,
- _Reserved_62,
- _Reserved_63,
- _Reserved_64,
- _Reserved_65,
- _Reserved_66,
- _Reserved_67,
- _Reserved_68,
- _Reserved_69,
- _Reserved_70,
- _Reserved_71,
- _Reserved_72,
- _Reserved_73,
- _Reserved_74,
- _Reserved_75,
- _Reserved_76,
- _Reserved_77,
- _Reserved_78,
- _Reserved_79,
- _Reserved_80,
- _Reserved_81,
- _Reserved_82,
- _Reserved_83,
- _Reserved_84,
- _Reserved_85,
- _Reserved_86,
- _Reserved_87,
- _Reserved_88,
- _Reserved_89,
- _Reserved_90,
- _Reserved_91,
- _Reserved_92,
- _Reserved_93,
- _Reserved_94,
- _Reserved_95,
- _Reserved_96,
- _Reserved_97,
- _Reserved_98,
- _Reserved_99,
- _Reserved_100,
- _Reserved_101,
- _Reserved_102,
- _Reserved_103,
- _Reserved_104,
- _Reserved_105,
- _Reserved_106,
- _Reserved_107,
- _Reserved_108,
- _Reserved_109,
- _Reserved_110,
- _Reserved_111,
- _Reserved_112,
- _Reserved_113,
- _Reserved_114,
- _Reserved_115,
- _Reserved_116,
- _Reserved_117,
- _Reserved_118,
- _Reserved_119,
- _Reserved_120,
- _Reserved_121,
- _Reserved_122,
- _Reserved_123,
- _Reserved_124,
- _Reserved_125,
- _Reserved_126,
- _Reserved_127,
- _UnAssigned_128,
- _UnAssigned_129,
- _UnAssigned_130,
- _UnAssigned_131,
- _UnAssigned_132,
- _UnAssigned_133,
- _UnAssigned_134,
- _UnAssigned_135,
- _UnAssigned_136,
- _UnAssigned_137,
- _UnAssigned_138,
- _UnAssigned_139,
- _UnAssigned_140,
- _UnAssigned_141,
- _UnAssigned_142,
- _UnAssigned_143,
- _UnAssigned_144,
- _UnAssigned_145,
- _UnAssigned_146,
- _UnAssigned_147,
- _UnAssigned_148,
- _UnAssigned_149,
- _UnAssigned_150,
- _UnAssigned_151,
- _UnAssigned_152,
- _UnAssigned_153,
- _UnAssigned_154,
- _UnAssigned_155,
- _UnAssigned_156,
- _UnAssigned_157,
- _UnAssigned_158,
- _UnAssigned_159,
- _UnAssigned_160,
- _UnAssigned_161,
- _UnAssigned_162,
- _UnAssigned_163,
- _UnAssigned_164,
- _UnAssigned_165,
- _UnAssigned_166,
- _UnAssigned_167,
- _UnAssigned_168,
- _UnAssigned_169,
- _UnAssigned_170,
- _UnAssigned_171,
- _UnAssigned_172,
- _UnAssigned_173,
- _UnAssigned_174,
- _UnAssigned_175,
- _UnAssigned_176,
- _UnAssigned_177,
- _UnAssigned_178,
- _UnAssigned_179,
- _UnAssigned_180,
- _UnAssigned_181,
- _UnAssigned_182,
- _UnAssigned_183,
- _UnAssigned_184,
- _UnAssigned_185,
- _UnAssigned_186,
- _UnAssigned_187,
- _UnAssigned_188,
- _UnAssigned_189,
- _UnAssigned_190,
- _UnAssigned_191,
- _UnAssigned_192,
- _UnAssigned_193,
- _UnAssigned_194,
- _UnAssigned_195,
- _UnAssigned_196,
- _UnAssigned_197,
- _UnAssigned_198,
- _UnAssigned_199,
- _UnAssigned_200,
- _UnAssigned_201,
- _UnAssigned_202,
- _UnAssigned_203,
- _UnAssigned_204,
- _UnAssigned_205,
- _UnAssigned_206,
- _UnAssigned_207,
- _UnAssigned_208,
- _UnAssigned_209,
- _UnAssigned_210,
- _UnAssigned_211,
- _UnAssigned_212,
- _UnAssigned_213,
- _UnAssigned_214,
- _UnAssigned_215,
- _UnAssigned_216,
- _UnAssigned_217,
- _UnAssigned_218,
- _UnAssigned_219,
- _UnAssigned_220,
- _UnAssigned_221,
- _UnAssigned_222,
- _UnAssigned_223,
- _UnAssigned_224,
- _UnAssigned_225,
- _UnAssigned_226,
- _UnAssigned_227,
- _UnAssigned_228,
- _UnAssigned_229,
- _UnAssigned_230,
- _UnAssigned_231,
- _UnAssigned_232,
- _UnAssigned_233,
- _UnAssigned_234,
- _UnAssigned_235,
- _UnAssigned_236,
- _UnAssigned_237,
- _UnAssigned_238,
- _UnAssigned_239,
- _UnAssigned_240,
- _UnAssigned_241,
- _UnAssigned_242,
- _UnAssigned_243,
- _UnAssigned_244,
- _UnAssigned_245,
- _UnAssigned_246,
- _UnAssigned_247,
- _UnAssigned_248,
- _UnAssigned_249,
- _UnAssigned_250,
- _UnAssigned_251,
- _UnAssigned_252,
- _UnAssigned_253,
- _UnAssigned_254,
- _UnAssigned_255
- }
- #endregion
- #region Classes
- public class TgaColorKey : ICloneable
- {
- byte a = 0;
- byte r = 0;
- byte g = 0;
- byte b = 0;
- public TgaColorKey()
- {
- }
- /// <summary>
- /// Make <see cref="TgaColorKey"/> from ARGB bytes.
- /// </summary>
- /// <param name="A">Alpha value.</param>
- /// <param name="R">Red value.</param>
- /// <param name="G">Green value.</param>
- /// <param name="B">Blue value.</param>
- public TgaColorKey(byte A, byte R, byte G, byte B)
- {
- a = A;
- r = R;
- g = G;
- b = B;
- }
- /// <summary>
- /// Make <see cref="TgaColorKey"/> from ARGB bytes.
- /// </summary>
- /// <param name="Bytes">Array of bytes(byte[4]).</param>
- public TgaColorKey(byte[] Bytes)
- {
- if (Bytes == null)
- throw new ArgumentNullException(nameof(Bytes) + " = null!");
- if (Bytes.Length != Size)
- throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
- Color color = Color.FromArgb(BitConverter.ToInt32(Bytes, 0));
- a = color.A;
- r = color.R;
- g = color.G;
- b = color.B;
- }
- /// <summary>
- /// Make <see cref="TgaColorKey"/> from <see cref="int"/>.
- /// </summary>
- /// <param name="ARGB">32bit ARGB integer color value.</param>
- public TgaColorKey(int ARGB)
- {
- Color ColorARGB = Color.FromArgb(ARGB);
- a = ColorARGB.A;
- r = ColorARGB.R;
- g = ColorARGB.G;
- b = ColorARGB.B;
- }
- /// <summary>
- /// Make <see cref="TgaColorKey"/> from <see cref="Color"/>.
- /// </summary>
- /// <param name="color">GDI+ <see cref="Color"/> value.</param>
- public TgaColorKey(Color color)
- {
- a = color.A;
- r = color.R;
- g = color.G;
- b = color.B;
- }
- /// <summary>
- /// Gets or sets alpha color value.
- /// </summary>
- public byte A
- {
- get { return a; }
- set { a = value; }
- }
- /// <summary>
- /// Gets or sets red color value.
- /// </summary>
- public byte R
- {
- get { return r; }
- set { r = value; }
- }
- /// <summary>
- /// Gets or sets green color value.
- /// </summary>
- public byte G
- {
- get { return g; }
- set { g = value; }
- }
- /// <summary>
- /// Gets or sets blue color value.
- /// </summary>
- public byte B
- {
- get { return b; }
- set { b = value; }
- }
- /// <summary>
- /// Gets TGA Field size in bytes.
- /// </summary>
- public const int Size = 4;
- /// <summary>
- /// Make full independed copy of <see cref="TgaColorKey"/>.
- /// </summary>
- /// <returns>Copy of <see cref="TgaColorKey"/></returns>
- public TgaColorKey Clone()
- {
- return new TgaColorKey(a, r, g, b);
- }
- /// <summary>
- /// Make full independed copy of <see cref="TgaColorKey"/>.
- /// </summary>
- /// <returns>Copy of <see cref="TgaColorKey"/></returns>
- object ICloneable.Clone()
- {
- return Clone();
- }
- public override bool Equals(object obj)
- {
- return ((obj is TgaColorKey) ? Equals((TgaColorKey)obj) : false);
- }
- public bool Equals(TgaColorKey item)
- {
- return (a == item.a && r == item.r && g == item.g && b == item.b);
- }
- public static bool operator ==(TgaColorKey item1, TgaColorKey item2)
- {
- if (ReferenceEquals(item1, null))
- return ReferenceEquals(item2, null);
- if (ReferenceEquals(item2, null))
- return ReferenceEquals(item1, null);
- return item1.Equals(item2);
- }
- public static bool operator !=(TgaColorKey item1, TgaColorKey item2)
- {
- return !(item1 == item2);
- }
- public override int GetHashCode()
- {
- return ToInt().GetHashCode();
- }
- /// <summary>
- /// Gets <see cref="TgaColorKey"/> like string.
- /// </summary>
- /// <returns>String in ARGB format.</returns>
- public override string ToString()
- {
- return String.Format("{0}={1}, {2}={3}, {4}={5}, {6}={7}",
- nameof(A), a, nameof(R), r, nameof(G), g, nameof(B), b);
- }
- /// <summary>
- /// Convert <see cref="TgaColorKey"/> to byte array.
- /// </summary>
- /// <returns>Byte array with length = 4.</returns>
- public byte[] ToBytes()
- {
- return BitConverter.GetBytes(ToInt());
- }
- /// <summary>
- /// Gets <see cref="TgaColorKey"/> like GDI+ <see cref="Color"/>.
- /// </summary>
- /// <returns><see cref="Color"/> value of <see cref="TgaColorKey"/>.</returns>
- public Color ToColor()
- {
- return Color.FromArgb(a, r, g, b);
- }
- /// <summary>
- /// Gets <see cref="TgaColorKey"/> like ARGB <see cref="int"/>.
- /// </summary>
- /// <returns>ARGB <see cref="int"/> value of <see cref="TgaColorKey"/>.</returns>
- public int ToInt()
- {
- return ToColor().ToArgb();
- }
- }
- /// <summary>
- /// This field (5 bytes) and its sub-fields describe the color map (if any) used for the image.
- /// If the Color Map Type field is set to zero, indicating that no color map exists, then
- /// these 5 bytes should be set to zero. These bytes always must be written to the file.
- /// </summary>
- public class TgaColorMapSpec : ICloneable
- {
- ushort firstEntryIndex = 0;
- ushort colorMapLength = 0;
- TgaColorMapEntrySize colorMapEntrySize = TgaColorMapEntrySize.Other;
- /// <summary>
- /// Make new <see cref="TgaColorMapSpec"/>.
- /// </summary>
- public TgaColorMapSpec()
- {
- }
- /// <summary>
- /// Make <see cref="TgaColorMapSpec"/> from bytes.
- /// </summary>
- /// <param name="Bytes">Array of bytes(byte[5]).</param>
- public TgaColorMapSpec(byte[] Bytes)
- {
- if (Bytes == null)
- throw new ArgumentNullException(nameof(Bytes) + " = null!");
- if (Bytes.Length != Size)
- throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
- firstEntryIndex = BitConverter.ToUInt16(Bytes, 0);
- colorMapLength = BitConverter.ToUInt16(Bytes, 2);
- colorMapEntrySize = (TgaColorMapEntrySize)Bytes[4];
- }
- /// <summary>
- /// Field 4.1 (2 bytes):
- /// Index of the first color map entry. Index refers to the starting entry in loading
- /// the color map.
- /// <para>Example: If you would have 1024 entries in the entire color map but you only
- /// need to store 72 of those entries, this field allows you to start in the middle of
- /// the color-map (e.g., position 342).</para>
- /// </summary>
- public ushort FirstEntryIndex
- {
- get { return firstEntryIndex; }
- set { firstEntryIndex = value; }
- }
- /// <summary>
- /// Field 4.2 (2 bytes):
- /// Total number of color map entries included.
- /// </summary>
- public ushort ColorMapLength
- {
- get { return colorMapLength; }
- set { colorMapLength = value; }
- }
- /// <summary>
- /// Field 4.3 (1 byte):
- /// Establishes the number of bits per entry. Typically 15, 16, 24 or 32-bit values are used.
- /// <para>When working with VDA or VDA/D cards it is preferred that you select 16 bits(5 bits
- /// per primary with 1 bit to select interrupt control) and set the 16th bit to 0 so that the
- /// interrupt bit is disabled. Even if this field is set to 15 bits(5 bits per primary) you
- /// must still parse the color map data 16 bits at a time and ignore the 16th bit.</para>
- /// <para>When working with a TARGA M8 card you would select 24 bits (8 bits per primary)
- /// since the color map is defined as 256 entries of 24 bit color values.</para>
- /// When working with a TrueVista card(ATVista or NuVista) you would select 24-bit(8 bits per
- /// primary) or 32-bit(8 bits per primary including Alpha channel) depending on your
- /// application’s use of look-up tables. It is suggested that when working with 16-bit and
- /// 32-bit color images, you store them as True-Color images and do not use the color map
- /// field to store look-up tables. Please refer to the TGA Extensions for fields better suited
- /// to storing look-up table information.
- /// </summary>
- public TgaColorMapEntrySize ColorMapEntrySize
- {
- get { return colorMapEntrySize; }
- set { colorMapEntrySize = value; }
- }
- /// <summary>
- /// Gets TGA Field size in bytes.
- /// </summary>
- public const int Size = 5;
- /// <summary>
- /// Make full independed copy of <see cref="TgaColorMapSpec"/>.
- /// </summary>
- /// <returns>Copy of <see cref="TgaColorMapSpec"/></returns>
- public TgaColorMapSpec Clone()
- {
- return new TgaColorMapSpec(ToBytes());
- }
- /// <summary>
- /// Make full independed copy of <see cref="TgaColorMapSpec"/>.
- /// </summary>
- /// <returns>Copy of <see cref="TgaColorMapSpec"/></returns>
- object ICloneable.Clone()
- {
- return Clone();
- }
- public override bool Equals(object obj)
- {
- return ((obj is TgaColorMapSpec) ? Equals((TgaColorMapSpec)obj) : false);
- }
- public bool Equals(TgaColorMapSpec item)
- {
- return (firstEntryIndex == item.firstEntryIndex &&
- colorMapLength == item.colorMapLength &&
- colorMapEntrySize == item.colorMapEntrySize);
- }
- public static bool operator ==(TgaColorMapSpec item1, TgaColorMapSpec item2)
- {
- if (ReferenceEquals(item1, null))
- return ReferenceEquals(item2, null);
- if (ReferenceEquals(item2, null))
- return ReferenceEquals(item1, null);
- return item1.Equals(item2);
- }
- public static bool operator !=(TgaColorMapSpec item1, TgaColorMapSpec item2)
- {
- return !(item1 == item2);
- }
- public override int GetHashCode()
- {
- unchecked
- {
- return (firstEntryIndex << 16 | colorMapLength).GetHashCode() ^ colorMapEntrySize.GetHashCode();
- }
- }
- public override string ToString()
- {
- return String.Format("{0}={1}, {2}={3}, {4}={5}", nameof(FirstEntryIndex), FirstEntryIndex,
- nameof(ColorMapLength), ColorMapLength, nameof(ColorMapEntrySize), ColorMapEntrySize);
- }
- /// <summary>
- /// Convert ColorMapSpec to byte array.
- /// </summary>
- /// <returns>Byte array with length = 5.</returns>
- public byte[] ToBytes()
- {
- return BitConverterExt.ToBytes(firstEntryIndex, colorMapLength, (byte)colorMapEntrySize);
- }
- }
- public class TgaComment : ICloneable
- {
- const int StrNLen = 80; //80 ASCII chars + 1 '\0' = 81 per SrtN!
- string origString = String.Empty;
- char blankSpaceChar = TgaString.DefaultBlankSpaceChar;
- public TgaComment()
- {
- }
- public TgaComment(string Str, char BlankSpaceChar = '\0')
- {
- if (Str == null)
- throw new ArgumentNullException(nameof(Str) + " = null!");
- origString = Str;
- blankSpaceChar = BlankSpaceChar;
- }
- public TgaComment(byte[] Bytes)
- {
- if (Bytes == null)
- throw new ArgumentNullException(nameof(Bytes) + " = null!");
- if (Bytes.Length != Size)
- throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
- string s = Encoding.ASCII.GetString(Bytes, 0, StrNLen);
- s += Encoding.ASCII.GetString(Bytes, 81, StrNLen);
- s += Encoding.ASCII.GetString(Bytes, 162, StrNLen);
- s += Encoding.ASCII.GetString(Bytes, 243, StrNLen);
- switch (s[s.Length - 1])
- {
- case '\0':
- case ' ':
- blankSpaceChar = s[s.Length - 1];
- origString = s.TrimEnd(new char[] { s[s.Length - 1] });
- break;
- default:
- origString = s;
- break;
- }
- }
- /// <summary>
- /// Gets TGA Field size in bytes.
- /// </summary>
- public const int Size = 81 * 4;
- public string OriginalString
- {
- get { return origString; }
- set { origString = value; }
- }
- public char BlankSpaceChar
- {
- get { return blankSpaceChar; }
- set { blankSpaceChar = value; }
- }
- /// <summary>
- /// Make full independed copy of <see cref="TgaComment"/>.
- /// </summary>
- /// <returns>Copy of <see cref="TgaComment"/></returns>
- public TgaComment Clone()
- {
- return new TgaComment(origString, blankSpaceChar);
- }
- /// <summary>
- /// Make full independed copy of <see cref="TgaComment"/>.
- /// </summary>
- /// <returns>Copy of <see cref="TgaComment"/></returns>
- object ICloneable.Clone()
- {
- return Clone();
- }
- public override bool Equals(object obj)
- {
- return ((obj is TgaComment) ? Equals((TgaComment)obj) : false);
- }
- public bool Equals(TgaComment item)
- {
- return (origString == item.origString && blankSpaceChar == item.blankSpaceChar);
- }
- public static bool operator ==(TgaComment item1, TgaComment item2)
- {
- if (ReferenceEquals(item1, null))
- return ReferenceEquals(item2, null);
- if (ReferenceEquals(item2, null))
- return ReferenceEquals(item1, null);
- return item1.Equals(item2);
- }
- public static bool operator !=(TgaComment item1, TgaComment item2)
- {
- return !(item1 == item2);
- }
- public override int GetHashCode()
- {
- return origString.GetHashCode() ^ blankSpaceChar.GetHashCode();
- }
- /// <summary>
- /// Get ASCII-Like string with string-terminators, example: "Line1 \0\0 Line2 \0\0\0".
- /// </summary>
- /// <returns>String with replaced string-terminators to "\0".</returns>
- public override string ToString()
- {
- return Encoding.ASCII.GetString(ToBytes()).Replace("\0", @"\0");
- }
- /// <summary>
- /// Get ASCII-Like string to first string-terminator, example:
- /// "Some string \0 Some Data \0" - > "Some string".
- /// </summary>
- /// <returns>String to first string-terminator.</returns>
- public string GetString()
- {
- String Str = Encoding.ASCII.GetString(ToBytes());
- for (int i = 1; i < 4; i++)
- Str = Str.Insert((StrNLen + 1) * i + i - 1, "\n");
- return Str.Replace("\0", String.Empty).TrimEnd(new char[] { '\n' });
- }
- /// <summary>
- /// Convert <see cref="TgaComment"/> to byte array.
- /// </summary>
- /// <returns>Byte array, every byte is ASCII symbol.</returns>
- public byte[] ToBytes()
- {
- return ToBytes(origString, blankSpaceChar);
- }
- /// <summary>
- /// Convert <see cref="TgaComment"/> to byte array.
- /// </summary>
- /// <param name="Str">Input string.</param>
- /// <param name="BlankSpaceChar">Char for filling blank space in string.</param>
- /// <returns>Byte array, every byte is ASCII symbol.</returns>
- public static byte[] ToBytes(string Str, char BlankSpaceChar = '\0')
- {
- char[] C = new char[81 * 4];
- for (int i = 0; i < C.Length; i++)
- {
- if ((i + 82) % 81 == 0)
- C[i] = TgaString.DefaultEndingChar;
- else
- {
- int Index = i - i / 81;
- C[i] = (Index < Str.Length ? Str[Index] : BlankSpaceChar);
- }
- }
- return Encoding.ASCII.GetBytes(C);
- }
- }
- public class TgaDateTime : ICloneable
- {
- ushort month = 0;
- ushort day = 0;
- ushort year = 0;
- ushort hour = 0;
- ushort minute = 0;
- ushort second = 0;
- /// <summary>
- /// Make empty <see cref="TgaDateTime"/>.
- /// </summary>
- public TgaDateTime()
- {
- }
- /// <summary>
- /// Make <see cref="TgaDateTime"/> from <see cref="DateTime"/>.
- /// </summary>
- /// <param name="DateAndTime">Some <see cref="DateTime"/> variable.</param>
- public TgaDateTime(DateTime DateAndTime)
- {
- month = (ushort)DateAndTime.Month;
- day = (ushort)DateAndTime.Day;
- year = (ushort)DateAndTime.Year;
- hour = (ushort)DateAndTime.Hour;
- minute = (ushort)DateAndTime.Minute;
- second = (ushort)DateAndTime.Second;
- }
- /// <summary>
- /// Make <see cref="TgaDateTime"/> from ushort values.
- /// </summary>
- /// <param name="Month">Month (1 - 12).</param>
- /// <param name="Day">Day (1 - 31).</param>
- /// <param name="Year">Year (4 digit, ie. 1989).</param>
- /// <param name="Hour">Hour (0 - 23).</param>
- /// <param name="Minute">Minute (0 - 59).</param>
- /// <param name="Second">Second (0 - 59).</param>
- public TgaDateTime(ushort Month, ushort Day, ushort Year, ushort Hour, ushort Minute, ushort Second)
- {
- month = Month;
- day = Day;
- year = Year;
- hour = Hour;
- minute = Minute;
- second = Second;
- }
- /// <summary>
- /// Make <see cref="TgaDateTime"/> from bytes.
- /// </summary>
- /// <param name="Bytes">Array of bytes(byte[12]).</param>
- public TgaDateTime(byte[] Bytes)
- {
- if (Bytes == null)
- throw new ArgumentNullException(nameof(Bytes) + " = null!");
- else if (Bytes.Length != Size)
- throw new ArgumentOutOfRangeException(nameof(Bytes) + " must be equal " + Size + "!");
- month = BitConverter.ToUInt16(Bytes, 0);
- day = BitConverter.ToUInt16(Bytes, 2);
- year = BitConverter.ToUInt16(Bytes, 4);
- hour = BitConverter.ToUInt16(Bytes, 6);
- minute = BitConverter.ToUInt16(Bytes, 8);
- second = BitConverter.ToUInt16(Bytes, 10);
- }
- /// <summary>
- /// Gets or Sets month (1 - 12).
- /// </summary>
- public ushort Month
- {
- get { return month; }
- set { month = value; }
- }
- /// <summary>
- /// Gets or Sets day (1 - 31).
- /// </summary>
- public ushort Day
- {
- get { return day; }
- set { day = value; }
- }
- /// <summary>
- /// Gets or Sets year (4 digit, ie. 1989).
- /// </summary>
- public ushort Year
- {
- get { return year; }
- set { year = value; }
- }
- /// <summary>
- /// Gets or Sets hour (0 - 23).
- /// </summary>
- public ushort Hour
- {
- get { return hour; }
- set { hour = value; }
- }
- /// <summary>
- /// Gets or Sets minute (0 - 59).
- /// </summary>
- public ushort Minute
- {
- get { return minute; }
- set { minute = value; }
- }
- /// <summary>
- /// Gets or Sets second (0 - 59).
- /// </summary>
- public ushort Second
- {
- get { return second; }
- set { second = value; }
- }
- /// <summary>
- /// Gets TGA Field size in bytes.
- /// </summary>
- public const int Size = 12;
- /// <summary>
- /// Make full independed copy of <see cref="TgaDateTime"/>.
- /// </summary>
- /// <returns>Copy of <see cref="TgaDateTime"/></returns>
- public TgaDateTime Clone()
- {
- return new TgaDateTime(month, day, year, hour, minute, second);
- }
- /// <summary>
- /// Make full independed copy of <see cref="TgaDateTime"/>.
- /// </summary>
- /// <returns>Copy of <see cref="TgaDateTime"/></returns>
- object ICloneable.Clone()
- {
- return Clone();
- }
- public override bool Equals(object obj)
- {
- return ((obj is TgaDateTime) ? Equals((TgaDateTime)obj) : false);
- }
- public bool Equals(TgaDateTime item)
- {
- return (
- month == item.month &&
- day == item.day &&
- year == item.year &&
- hour == item.hour &&
- minute == item.minute &&
- second == item.second);
- }
- public static bool operator ==(TgaDateTime item1, TgaDateTime item2)
- {
- if (ReferenceEquals(item1, null))
- return ReferenceEquals(item2, null);
- if (ReferenceEquals(item2, null))
- return ReferenceEquals(item1, null);
- return item1.Equals(item2);
- }
- public static bool operator !=(TgaDateTime item1, TgaDateTime item2)
- {
- return !(item1 == item2);
- }
- public override int GetHashCode()
- {
- unchecked
- {
- int hash = 17;
- hash = hash * 23 + (month << 16 | hour).GetHashCode();
- hash = hash * 23 + (day << 16 | minute).GetHashCode();
- hash = hash * 23 + (year << 16 | second).GetHashCode();
- return hash;
- }
- }
- /// <summary>
- /// Gets <see cref="TgaDateTime"/> like string.
- /// </summary>
- /// <returns>String in "1990.01.23 1:02:03" format.</returns>
- public override string ToString()
- {
- return String.Format("{0:D4}.{1:D2}.{2:D2} {3}:{4:D2}:{5:D2}", year, month, day, hour, minute, second);
- }
- /// <summary>
- /// Convert <see cref="TgaDateTime"/> to byte array.
- /// </summary>
- /// <returns>Byte array with length = 12.</returns>
- public byte[] ToBytes()
- {
- return BitConverterExt.ToBytes(month, day, year, hour, minute, second);
- }
- /// <summary>
- /// Gets <see cref="TgaDateTime"/> like <see cref="DateTime"/>.
- /// </summary>
- /// <returns><see cref="DateTime"/> value of <see cref="TgaDateTime"/>.</returns>
- public DateTime ToDateTime()
- {
- return new DateTime(year, month, day, hour, minute, second);
- }
- }
- public class TgaDevEntry : ICloneable
- {
- // Directory
- ushort fieldTag = 0;
- uint fieldFileOffset = 0;
- // Field
- byte[] data = null;
- /// <summary>
- /// Make empty <see cref="TgaDevEntry"/>.
- /// </summary>
- public TgaDevEntry()
- {
- }
- /// <summary>
- /// Make <see cref="TgaDevEntry"/> from other <see cref="TgaDevEntry"/>.
- /// </summary>
- /// <param name="Entry">Some <see cref="TgaDevEntry"/> variable.</param>
- public TgaDevEntry(TgaDevEntry Entry)
- {
- if (Entry == null)
- throw new ArgumentNullException();
- fieldTag = Entry.fieldTag;
- fieldFileOffset = Entry.fieldFileOffset;
- data = BitConverterExt.ToBytes(Entry.data);
- }
- /// <summary>
- /// Make <see cref="TgaDevEntry"/> from <see cref="Tag"/>, <see cref="Offset"/> and <see cref="FieldSize"/>.
- /// </summary>
- /// <param name="Tag">TAG ID (0 - 65535). See <see cref="Tag"/>.</param>
- /// <param name="Offset">TAG file offset in bytes. See <see cref="Offset"/>.</param>
- /// <param name="Data">This is DevEntry Field Data. See <see cref="Data"/>.</param>
- public TgaDevEntry(ushort Tag, uint Offset, byte[] Data = null)
- {
- fieldTag = Tag;
- fieldFileOffset = Offset;
- data = Data;
- }
- /// <summary>
- /// Make <see cref="TgaDevEntry"/> from bytes.
- /// </summary>
- /// <param name="Bytes">Array of bytes(byte[6] or bigger, if <see cref="Data"/> exist).</param>
- public TgaDevEntry(byte[] Bytes)
- {
- if (Bytes == null)
- throw new ArgumentNullException(nameof(Bytes) + " = null!");
- else if (Bytes.Length < 6)
- throw new ArgumentOutOfRangeException(nameof(Bytes) + " must be >= 6!");
- fieldTag = BitConverter.ToUInt16(Bytes, 0);
- fieldFileOffset = BitConverter.ToUInt32(Bytes, 2);
- if (Bytes.Length > 6)
- data = BitConverterExt.GetElements(Bytes, 6, Bytes.Length - 6);
- }
- /// <summary>
- /// Each TAG is a value in the range of 0 to 65535. Values from 0 - 32767 are available for developer use,
- /// while values from 32768 - 65535 are reserved for Truevision.
- /// </summary>
- public ushort Tag
- {
- get { return fieldTag; }
- set { fieldTag = value; }
- }
- /// <summary>
- /// This OFFSET is a number of bytes from the beginning of the file to the start of the field
- /// referenced by the tag.
- /// </summary>
- public uint Offset
- {
- get { return fieldFileOffset; }
- set { fieldFileOffset = value; }
- }
- /// <summary>
- /// Field DATA.
- /// Although the size and format of the actual Developer Area fields are totally up to the developer,
- /// please define your formats to address future considerations you might have concerning your fields.
- /// This means that if you anticipate changing a field, build flexibility into the format to make these
- /// changes easy on other developers.Major changes to an existing TAG’s definition should never happen.
- /// </summary>
- public byte[] Data
- {
- get { return data; }
- set { data = value; }
- }
- /// <summary>
- /// The FIELD SIZE is a number of bytes in the field. Same like: <see cref="Data.Length"/>,
- /// if <see cref="Data"/> is null, return -1.
- /// </summary>
- public int FieldSize
- {
- get
- {
- if (Data == null)
- return -1;
- return Data.Length;
- }
- }
- /// <summary>
- /// Gets TGA <see cref="TgaDevEntry"/> size in bytes (Always constant and equal 10!).
- /// It is not <see cref="FieldSize"/>! It is just size of entry sizeof(ushort + uint + uint).
- /// </summary>
- public const int Size = 10;
- /// <summary>
- /// Make full independed copy of <see cref="TgaDevEntry"/>.
- /// </summary>
- /// <returns>Copy of <see cref="TgaDevEntry"/></returns>
- public TgaDevEntry Clone()
- {
- return new TgaDevEntry(this);
- }
- /// <summary>
- /// Make full independed copy of <see cref="TgaDevEntry"/>.
- /// </summary>
- /// <returns>Copy of <see cref="TgaDevEntry"/></returns>
- object ICloneable.Clone()
- {
- return Clone();
- }
- public override bool Equals(object obj)
- {
- return ((obj is TgaDevEntry) ? Equals((TgaDevEntry)obj) : false);
- }
- public bool Equals(TgaDevEntry item)
- {
- return (fieldTag == item.fieldTag &&
- fieldFileOffset == item.fieldFileOffset &&
- BitConverterExt.IsArraysEqual(data, item.data));
- }
- public static bool operator ==(TgaDevEntry item1, TgaDevEntry item2)
- {
- if (ReferenceEquals(item1, null))
- return ReferenceEquals(item2, null);
- if (ReferenceEquals(item2, null))
- return ReferenceEquals(item1, null);
- return item1.Equals(item2);
- }
- public static bool operator !=(TgaDevEntry item1, TgaDevEntry item2)
- {
- return !(item1 == item2);
- }
- public override int GetHashCode()
- {
- unchecked
- {
- int hash = 17;
- hash = hash * 23 + fieldTag.GetHashCode();
- hash = hash * 23 + fieldFileOffset.GetHashCode();
- if (data != null)
- for (int i = 0; i < data.Length; i++)
- hash = hash * 23 + data[i].GetHashCode();
- return hash;
- }
- }
- /// <summary>
- /// Gets <see cref="TgaDevEntry"/> like string.
- /// </summary>
- /// <returns>String in "Tag={0}, Offset={1}, FieldSize={2}" format.</returns>
- public override string ToString()
- {
- return String.Format("{0}={1}, {1}={2}, {3}={4}", nameof(Tag), fieldTag,
- nameof(Offset), fieldFileOffset, nameof(FieldSize), FieldSize);
- }
- /// <summary>
- /// Convert <see cref="TgaDevEntry"/> to byte array. (Not include <see cref="Data"/>!).
- /// </summary>
- /// <returns>Byte array with length = 10.</returns>
- public byte[] ToBytes()
- {
- return BitConverterExt.ToBytes(fieldTag, fieldFileOffset, (data == null ? 0 : data.Length));
- }
- } //Not full ToBytes()
- public class TgaFraction : ICloneable
- {
- ushort numerator = 0;
- ushort denominator = 0;
- /// <summary>
- /// Make <see cref="TgaFraction"/> from <see cref="Numerator"/> and <see cref="Denominator"/>.
- /// </summary>
- /// <param name="Numerator">Numerator value.</param>
- /// <param name="Denominator">Denominator value.</param>
- public TgaFraction(ushort Numerator = 0, ushort Denominator = 0)
- {
- numerator = Numerator;
- denominator = Denominator;
- }
- /// <summary>
- /// Make <see cref="TgaFraction"/> from bytes.
- /// </summary>
- /// <param name="Bytes">Array of bytes(byte[4]).</param>
- public TgaFraction(byte[] Bytes)
- {
- if (Bytes == null)
- throw new ArgumentNullException(nameof(Bytes) + " = null!");
- if (Bytes.Length != Size)
- throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
- numerator = BitConverter.ToUInt16(Bytes, 0);
- denominator = BitConverter.ToUInt16(Bytes, 2);
- }
- /// <summary>
- /// Gets or sets numerator value.
- /// </summary>
- public ushort Numerator
- {
- get { return numerator; }
- set { numerator = value; }
- }
- /// <summary>
- /// Gets or sets denominator value.
- /// </summary>
- public ushort Denominator
- {
- get { return denominator; }
- set { denominator = value; }
- }
- /// <summary>
- /// Get aspect ratio = <see cref="Numerator"/> / <see cref="Denominator"/>.
- /// </summary>
- public float AspectRatio
- {
- get
- {
- if (numerator == denominator)
- return 1f;
- return numerator / (float)denominator;
- }
- }
- /// <summary>
- /// Gets Empty <see cref="TgaFraction"/>, all values are 0.
- /// </summary>
- public static readonly TgaFraction Empty = new TgaFraction();
- /// <summary>
- /// Gets One <see cref="TgaFraction"/>, all values are 1 (ones, 1 / 1 = 1).
- /// </summary>
- public static readonly TgaFraction One = new TgaFraction(1, 1);
- /// <summary>
- /// Gets TGA Field size in bytes.
- /// </summary>
- public const int Size = 4;
- /// <summary>
- /// Make full independed copy of <see cref="TgaFraction"/>.
- /// </summary>
- /// <returns>Copy of <see cref="TgaFraction"/></returns>
- public TgaFraction Clone()
- {
- return new TgaFraction(numerator, denominator);
- }
- /// <summary>
- /// Make full independed copy of <see cref="TgaFraction"/>.
- /// </summary>
- /// <returns>Copy of <see cref="TgaFraction"/></returns>
- object ICloneable.Clone()
- {
- return Clone();
- }
- public override bool Equals(object obj)
- {
- return ((obj is TgaFraction) ? Equals((TgaFraction)obj) : false);
- }
- public bool Equals(TgaFraction item)
- {
- return (numerator == item.numerator && denominator == item.denominator);
- }
- public static bool operator ==(TgaFraction item1, TgaFraction item2)
- {
- if (ReferenceEquals(item1, null))
- return ReferenceEquals(item2, null);
- if (ReferenceEquals(item2, null))
- return ReferenceEquals(item1, null);
- return item1.Equals(item2);
- }
- public static bool operator !=(TgaFraction item1, TgaFraction item2)
- {
- return !(item1 == item2);
- }
- public override int GetHashCode()
- {
- return (numerator << 16 | denominator).GetHashCode();
- }
- /// <summary>
- /// Gets <see cref="TgaFraction"/> like string.
- /// </summary>
- /// <returns>String in "Numerator=1, Denominator=2" format.</returns>
- public override string ToString()
- {
- return String.Format("{0}={1}, {2}={3}", nameof(Numerator), numerator,
- nameof(Denominator), denominator);
- }
- /// <summary>
- /// Convert <see cref="TgaFraction"/> to byte array.
- /// </summary>
- /// <returns>Byte array with length = 4.</returns>
- public byte[] ToBytes()
- {
- return BitConverterExt.ToBytes(numerator, denominator);
- }
- }
- /// <summary>
- /// Contains image origin bits and alpha channel bits(or number of overlay bits)
- /// </summary>
- public class TgaImageDescriptor : ICloneable
- {
- TgaImgOrigin imageOrigin = 0; //bits 5-4
- byte alphaChannelBits = 0; //bits 3-0
- /// <summary>
- /// Make empty <see cref="TgaImageDescriptor"/>.
- /// </summary>
- public TgaImageDescriptor()
- {
- }
- /// <summary>
- /// Make <see cref="TgaImageDescriptor"/> from bytes.
- /// </summary>
- /// <param name="b">ImageDescriptor byte with reserved 7-6 bits, bits 5-4 used for
- /// <see cref="ImageOrigin"/>, 3-0 used as alpha channel bits or number of overlay bits.</param>
- public TgaImageDescriptor(byte b)
- {
- imageOrigin = (TgaImgOrigin)((b & 0x30) >> 4);
- alphaChannelBits = (byte)(b & 0x0F);
- }
- /// <summary>
- /// Gets or Sets Image Origin bits (select from enum only, don'n use 5-4 bits!).
- /// </summary>
- public TgaImgOrigin ImageOrigin
- {
- get { return imageOrigin; }
- set { imageOrigin = value; }
- }
- /// <summary>
- /// Gets or Sets alpha channel bits or number of overlay bits.
- /// </summary>
- public byte AlphaChannelBits
- {
- get { return alphaChannelBits; }
- set { alphaChannelBits = value; }
- }
- /// <summary>
- /// Gets TGA Field size in bytes.
- /// </summary>
- public const int Size = 1;
- /// <summary>
- /// Make full copy of <see cref="TgaImageDescriptor"/>.
- /// </summary>
- /// <returns>Full independent copy of <see cref="TgaImageDescriptor"/>.</returns>
- public TgaImageDescriptor Clone()
- {
- return new TgaImageDescriptor(ToByte());
- }
- /// <summary>
- /// Make full copy of <see cref="TgaImageDescriptor"/>.
- /// </summary>
- /// <returns>Full independent copy of <see cref="TgaImageDescriptor"/>.</returns>
- object ICloneable.Clone()
- {
- return Clone();
- }
- public override bool Equals(object obj)
- {
- return ((obj is TgaImageDescriptor) ? Equals((TgaImageDescriptor)obj) : false);
- }
- public bool Equals(TgaImageDescriptor item)
- {
- return (imageOrigin == item.imageOrigin && alphaChannelBits == item.alphaChannelBits);
- }
- public static bool operator ==(TgaImageDescriptor item1, TgaImageDescriptor item2)
- {
- if (ReferenceEquals(item1, null))
- return ReferenceEquals(item2, null);
- if (ReferenceEquals(item2, null))
- return ReferenceEquals(item1, null);
- return item1.Equals(item2);
- }
- public static bool operator !=(TgaImageDescriptor item1, TgaImageDescriptor item2)
- {
- return !(item1 == item2);
- }
- public override int GetHashCode()
- {
- unchecked
- {
- return ((int)ImageOrigin << 4 | alphaChannelBits).GetHashCode();
- }
- }
- public override string ToString()
- {
- return String.Format("{0}={1}, {2}={3}, ImageDescriptor_AsByte={4}", nameof(ImageOrigin),
- imageOrigin, nameof(AlphaChannelBits), alphaChannelBits, ToByte());
- }
- /// <summary>
- /// Gets ImageDescriptor byte.
- /// </summary>
- /// <returns>ImageDescriptor byte with reserved 7-6 bits, bits 5-4 used for imageOrigin,
- /// 3-0 used as alpha channel bits or number of overlay bits.</returns>
- public byte ToByte()
- {
- return (byte)(((int)imageOrigin << 4) | alphaChannelBits);
- }
- }
- /// <summary>
- /// Image Specification - Field 5 (10 bytes):
- /// <para>This field and its sub-fields describe the image screen location, size and pixel depth.
- /// These information is always written to the file.</para>
- /// </summary>
- public class TgaImageSpec : ICloneable
- {
- ushort x_Origin = 0;
- ushort y_Origin = 0;
- ushort imageWidth = 0;
- ushort imageHeight = 0;
- TgaPixelDepth pixelDepth = TgaPixelDepth.Other;
- TgaImageDescriptor imageDescriptor = new TgaImageDescriptor();
- public TgaImageSpec()
- {
- }
- /// <summary>
- /// Make ImageSpec from values.
- /// </summary>
- /// <param name="X_Origin">These specify the absolute horizontal coordinate for the lower
- /// left corner of the image as it is positioned on a display device having an origin at
- /// the lower left of the screen(e.g., the TARGA series).</param>
- /// <param name="Y_Origin">These specify the absolute vertical coordinate for the lower
- /// left corner of the image as it is positioned on a display device having an origin at
- /// the lower left of the screen(e.g., the TARGA series).</param>
- /// <param name="ImageWidth">This field specifies the width of the image in pixels.</param>
- /// <param name="ImageHeight">This field specifies the height of the image in pixels.</param>
- /// <param name="PixelDepth">This field indicates the number of bits per pixel. This number
- /// includes the Attribute or Alpha channel bits. Common values are 8, 16, 24 and 32 but
- /// other pixel depths could be used.</param>
- /// <param name="ImageDescriptor">Contains image origin bits and alpha channel bits
- /// (or number of overlay bits).</param>
- public TgaImageSpec(ushort X_Origin, ushort Y_Origin, ushort ImageWidth, ushort ImageHeight,
- TgaPixelDepth PixelDepth, TgaImageDescriptor ImageDescriptor)
- {
- x_Origin = X_Origin;
- y_Origin = Y_Origin;
- imageWidth = ImageWidth;
- imageHeight = ImageHeight;
- pixelDepth = PixelDepth;
- imageDescriptor = ImageDescriptor;
- }
- /// <summary>
- /// Make ImageSpec from bytes.
- /// </summary>
- /// <param name="Bytes">Array of bytes(byte[10]).</param>
- public TgaImageSpec(byte[] Bytes)
- {
- if (Bytes == null)
- throw new ArgumentNullException(nameof(Bytes) + " = null!");
- if (Bytes.Length != Size)
- throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
- x_Origin = BitConverter.ToUInt16(Bytes, 0);
- y_Origin = BitConverter.ToUInt16(Bytes, 2);
- imageWidth = BitConverter.ToUInt16(Bytes, 4);
- imageHeight = BitConverter.ToUInt16(Bytes, 6);
- pixelDepth = (TgaPixelDepth)Bytes[8];
- imageDescriptor = new TgaImageDescriptor(Bytes[9]);
- }
- /// <summary>
- /// These specify the absolute horizontal coordinate for the lower left corner of the image
- /// as it is positioned on a display device having an origin at the lower left of the
- /// screen(e.g., the TARGA series).
- /// </summary>
- public ushort X_Origin
- {
- get { return x_Origin; }
- set { x_Origin = value; }
- }
- /// <summary>
- /// These specify the absolute vertical coordinate for the lower left corner of the image
- /// as it is positioned on a display device having an origin at the lower left of the
- /// screen(e.g., the TARGA series).
- /// </summary>
- public ushort Y_Origin
- {
- get { return y_Origin; }
- set { y_Origin = value; }
- }
- /// <summary>
- /// This field specifies the width of the image in pixels.
- /// </summary>
- public ushort ImageWidth
- {
- get { return imageWidth; }
- set { imageWidth = value; }
- }
- /// <summary>
- /// This field specifies the height of the image in pixels.
- /// </summary>
- public ushort ImageHeight
- {
- get { return imageHeight; }
- set { imageHeight = value; }
- }
- /// <summary>
- /// This field indicates the number of bits per pixel. This number includes the Attribute or
- /// Alpha channel bits. Common values are 8, 16, 24 and 32 but other pixel depths could be used.
- /// </summary>
- public TgaPixelDepth PixelDepth
- {
- get { return pixelDepth; }
- set { pixelDepth = value; }
- }
- /// <summary>
- /// Contains image origin bits and alpha channel bits(or number of overlay bits).
- /// </summary>
- public TgaImageDescriptor ImageDescriptor
- {
- get { return imageDescriptor; }
- set { imageDescriptor = value; }
- }
- /// <summary>
- /// Gets TGA Field size in bytes.
- /// </summary>
- public const int Size = 10;
- /// <summary>
- /// Make full copy of <see cref="TgaImageDescriptor"/>.
- /// </summary>
- /// <returns></returns>
- public TgaImageSpec Clone()
- {
- return new TgaImageSpec(ToBytes());
- }
- /// <summary>
- /// Make full copy of <see cref="TgaImageDescriptor"/>.
- /// </summary>
- /// <returns></returns>
- object ICloneable.Clone()
- {
- return Clone();
- }
- public override bool Equals(object obj)
- {
- return ((obj is TgaImageSpec) ? Equals((TgaImageSpec)obj) : false);
- }
- public bool Equals(TgaImageSpec item)
- {
- return (
- x_Origin == item.x_Origin &&
- y_Origin == item.y_Origin &&
- imageWidth == item.imageWidth &&
- imageHeight == item.imageHeight &&
- pixelDepth == item.pixelDepth &&
- imageDescriptor == item.imageDescriptor);
- }
- public static bool operator ==(TgaImageSpec item1, TgaImageSpec item2)
- {
- if (ReferenceEquals(item1, null))
- return ReferenceEquals(item2, null);
- if (ReferenceEquals(item2, null))
- return ReferenceEquals(item1, null);
- return item1.Equals(item2);
- }
- public static bool operator !=(TgaImageSpec item1, TgaImageSpec item2)
- {
- return !(item1 == item2);
- }
- public override int GetHashCode()
- {
- unchecked
- {
- int hash = 17;
- hash = hash * 23 + x_Origin.GetHashCode();
- hash = hash * 23 + y_Origin.GetHashCode();
- hash = hash * 23 + imageWidth.GetHashCode();
- hash = hash * 23 + imageHeight.GetHashCode();
- hash = hash * 23 + pixelDepth.GetHashCode();
- if (imageDescriptor != null)
- hash = hash * 23 + imageDescriptor.GetHashCode();
- return hash;
- }
- }
- public override string ToString()
- {
- return String.Format("{0}={1}, {2}={3}, {4}={5}, {6}={7}, {8}={9}, {10}={11}",
- nameof(X_Origin), x_Origin,
- nameof(Y_Origin), y_Origin,
- nameof(ImageWidth), imageWidth,
- nameof(ImageHeight), imageHeight,
- nameof(PixelDepth), pixelDepth,
- nameof(ImageDescriptor), imageDescriptor);
- }
- /// <summary>
- /// Convert <see cref="TgaImageSpec"/> to byte array.
- /// </summary>
- /// <returns>Byte array with length = 10.</returns>
- public byte[] ToBytes()
- {
- return BitConverterExt.ToBytes(x_Origin, y_Origin, imageWidth, imageHeight,
- (byte)pixelDepth, (imageDescriptor == null ? byte.MinValue : imageDescriptor.ToByte()));
- }
- }
- /// <summary>
- /// Postage Stamp Image (MaxSize 64x64, uncompressed, PixelDepth like in full image).
- /// </summary>
- public class TgaPostageStampImage : ICloneable
- {
- byte width = 0;
- byte height = 0;
- byte[] data = null;
- public TgaPostageStampImage()
- {
- }
- /// <summary>
- /// Make <see cref="TgaPostageStampImage"/> from bytes array.
- /// </summary>
- /// <param name="Bytes">Bytes array, first 2 bytes are <see cref="Width"/> and <see cref="Height"/>,
- /// next bytes - image data.</param>
- public TgaPostageStampImage(byte[] Bytes)
- {
- if (Bytes == null)
- throw new ArgumentNullException(nameof(Bytes) + " = null!");
- if (Bytes.Length < 2)
- throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be >= " + 2 + "!");
- width = Bytes[0];
- height = Bytes[1];
- if (Bytes.Length > 2)
- data = BitConverterExt.GetElements(Bytes, 2, Bytes.Length - 2);
- }
- /// <summary>
- /// Make <see cref="TgaPostageStampImage"/> from bytes and size.
- /// </summary>
- /// <param name="Width">Image Width.</param>
- /// <param name="Height">Image Height.</param>
- /// <param name="Bytes">Postage Stamp Image Data.</param>
- public TgaPostageStampImage(byte Width, byte Height, byte[] Bytes)
- {
- if (Bytes == null)
- throw new ArgumentNullException(nameof(Bytes) + " = null!");
- width = Width;
- height = Height;
- data = Bytes;
- }
- /// <summary>
- /// Postage Stamp Image Data
- /// </summary>
- public byte[] Data
- {
- get { return data; }
- set { data = value; }
- }
- /// <summary>
- /// Postage Stamp Image Width (maximum = 64).
- /// </summary>
- public byte Width
- {
- get { return width; }
- set { width = value; }
- }
- /// <summary>
- /// Postage Stamp Image Height (maximum = 64).
- /// </summary>
- public byte Height
- {
- get { return height; }
- set { height = value; }
- }
- /// <summary>
- /// Make full copy of <see cref="TgaPostageStampImage"/>.
- /// </summary>
- /// <returns>Full independent copy of <see cref="TgaPostageStampImage"/>.</returns>
- public TgaPostageStampImage Clone()
- {
- return new TgaPostageStampImage(width, height, BitConverterExt.ToBytes(data));
- }
- /// <summary>
- /// Make full copy of <see cref="TgaPostageStampImage"/>.
- /// </summary>
- /// <returns>Full independent copy of <see cref="TgaPostageStampImage"/>.</returns>
- object ICloneable.Clone()
- {
- return Clone();
- }
- public override bool Equals(object obj)
- {
- return ((obj is TgaPostageStampImage) ? Equals((TgaPostageStampImage)obj) : false);
- }
- public bool Equals(TgaPostageStampImage item)
- {
- return width == item.width && height == item.height && BitConverterExt.IsArraysEqual(data, item.data);
- }
- public static bool operator ==(TgaPostageStampImage item1, TgaPostageStampImage item2)
- {
- if (ReferenceEquals(item1, null))
- return ReferenceEquals(item2, null);
- if (ReferenceEquals(item2, null))
- return ReferenceEquals(item1, null);
- return item1.Equals(item2);
- }
- public static bool operator !=(TgaPostageStampImage item1, TgaPostageStampImage item2)
- {
- return !(item1 == item2);
- }
- public override int GetHashCode()
- {
- unchecked
- {
- int hash = 27;
- hash = (13 * hash) + width.GetHashCode();
- hash = (13 * hash) + height.GetHashCode();
- if (data != null)
- for (int i = 0; i < data.Length; i++)
- hash = (13 * hash) + data[i].GetHashCode();
- return hash;
- }
- }
- public override string ToString()
- {
- return String.Format("{0}={1}, {2}={3}, DataLength={4}",
- nameof(Width), width, nameof(Height), height, (data == null ? -1 : data.Length));
- }
- /// <summary>
- /// Convert <see cref="TgaPostageStampImage"/> to byte array.
- /// </summary>
- /// <returns>Byte array.</returns>
- public byte[] ToBytes()
- {
- return BitConverterExt.ToBytes(width, height, data);
- }
- }
- public class TgaSoftVersion : ICloneable
- {
- ushort versionNumber = 0;
- char versionLetter = ' ';
- /// <summary>
- /// Gets Empty <see cref="TgaSoftVersion"/>, <see cref="VersionLetter"/> = ' ' (space).
- /// </summary>
- public TgaSoftVersion()
- {
- }
- /// <summary>
- /// Make <see cref="TgaSoftVersion"/> from string.
- /// </summary>
- /// <param name="Str">Input string, example: "123d".</param>
- public TgaSoftVersion(string Str)
- {
- if (Str == null)
- throw new ArgumentNullException();
- if (Str.Length < 3 || Str.Length > 4)
- throw new ArgumentOutOfRangeException(nameof(Str.Length) + " must be equal 3 or 4!");
- bool Res = ushort.TryParse(Str.Substring(0, 3), out versionNumber);
- if (Res && Str.Length == 4)
- versionLetter = Str[3];
- }
- /// <summary>
- /// Make <see cref="TgaSoftVersion"/> from bytes.
- /// </summary>
- /// <param name="Bytes">Bytes array (byte[3]).</param>
- public TgaSoftVersion(byte[] Bytes)
- {
- if (Bytes == null)
- throw new ArgumentNullException(nameof(Bytes) + " = null!");
- if (Bytes.Length != Size)
- throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
- versionNumber = BitConverter.ToUInt16(Bytes, 0);
- versionLetter = Encoding.ASCII.GetString(Bytes, 2, 1)[0];
- }
- public TgaSoftVersion(ushort VersionNumber, char VersionLetter = ' ')
- {
- versionNumber = VersionNumber;
- versionLetter = VersionLetter;
- }
- public ushort VersionNumber
- {
- get { return versionNumber; }
- set { versionNumber = value; }
- }
- public char VersionLetter
- {
- get { return versionLetter; }
- set { versionLetter = value; }
- }
- /// <summary>
- /// Gets TGA Field size in bytes.
- /// </summary>
- public const int Size = 3;
- /// <summary>
- /// Make full copy of <see cref="TgaSoftVersion"/>.
- /// </summary>
- /// <returns></returns>
- public TgaSoftVersion Clone()
- {
- return new TgaSoftVersion(versionNumber, versionLetter);
- }
- /// <summary>
- /// Make full copy of <see cref="TgaSoftVersion"/>.
- /// </summary>
- /// <returns></returns>
- object ICloneable.Clone()
- {
- return Clone();
- }
- public override bool Equals(object obj)
- {
- return ((obj is TgaSoftVersion) ? Equals((TgaSoftVersion)obj) : false);
- }
- public bool Equals(TgaSoftVersion item)
- {
- return (versionNumber == item.versionNumber && versionLetter == item.versionLetter);
- }
- public static bool operator ==(TgaSoftVersion item1, TgaSoftVersion item2)
- {
- if (ReferenceEquals(item1, null))
- return ReferenceEquals(item2, null);
- if (ReferenceEquals(item2, null))
- return ReferenceEquals(item1, null);
- return item1.Equals(item2);
- }
- public static bool operator !=(TgaSoftVersion item1, TgaSoftVersion item2)
- {
- return !(item1 == item2);
- }
- public override int GetHashCode()
- {
- return versionNumber.GetHashCode() ^ versionLetter.GetHashCode();
- }
- public override string ToString()
- {
- return (versionNumber.ToString("000") + versionLetter).TrimEnd(new char[] { ' ', '\0' });
- }
- /// <summary>
- /// Convert <see cref="TgaSoftVersion"/> to byte array.
- /// </summary>
- /// <returns>Byte array, <see cref="VersionNumber"/> (2 bytes) and
- /// <see cref="VersionLetter"/> (ASCII symbol).</returns>
- public byte[] ToBytes()
- {
- return ToBytes(versionNumber, versionLetter);
- }
- /// <summary>
- /// Convert <see cref="TgaSoftVersion"/> to byte array.
- /// </summary>
- /// <param name="VersionNumber">Set 123 for 1.23 version.</param>
- /// <param name="VersionLetter">Version letter, example: for 'a' - "1.23a".</param>
- /// <returns>Byte array, <see cref="VersionNumber"/> (2 bytes) and <see cref="VersionLetter"/> (ASCII symbol).</returns>
- public static byte[] ToBytes(ushort VersionNumber, char VersionLetter = ' ')
- {
- return BitConverterExt.ToBytes(VersionNumber, Encoding.ASCII.GetBytes(VersionLetter.ToString()));
- }
- }
- /// <summary>
- /// Use it for working with ASCII strings in TGA files.
- /// </summary>
- public class TgaString : ICloneable
- {
- public const string XFileSignatuteConst = "TRUEVISION-XFILE";
- public const string DotSymbolConst = ".";
- string origString = String.Empty;
- int length = 0;
- char blankSpaceChar = DefaultBlankSpaceChar;
- bool useEnding = false;
- public TgaString(bool UseEnding = false)
- {
- useEnding = UseEnding;
- }
- public TgaString(byte[] Bytes, bool UseEnding = false)
- {
- if (Bytes == null)
- throw new ArgumentNullException(nameof(Bytes) + " = null!");
- length = Bytes.Length;
- useEnding = UseEnding;
- string s = Encoding.ASCII.GetString(Bytes, 0, Bytes.Length - (useEnding ? 1 : 0));
- if (s.Length > 0)
- switch (s[s.Length - 1])
- {
- case '\0':
- case ' ':
- blankSpaceChar = s[s.Length - 1];
- origString = s.TrimEnd(new char[] { s[s.Length - 1] });
- break;
- default:
- origString = s;
- break;
- }
- }
- public TgaString(int Length, bool UseEnding = false)
- {
- length = Length;
- useEnding = UseEnding;
- }
- public TgaString(string Str, int Length, bool UseEnding = false, char BlankSpaceChar = '\0')
- {
- if (Str == null)
- throw new ArgumentNullException(nameof(Str) + " = null!");
- origString = Str;
- length = Length;
- blankSpaceChar = BlankSpaceChar;
- useEnding = UseEnding;
- }
- public string OriginalString
- {
- get { return origString; }
- set { origString = value; }
- }
- public int Length
- {
- get { return length; }
- set { length = value; }
- }
- public char BlankSpaceChar
- {
- get { return blankSpaceChar; }
- set { blankSpaceChar = value; }
- }
- public bool UseEndingChar
- {
- get { return useEnding; }
- set { useEnding = value; }
- }
- /// <summary>
- /// Gets ending char, default '\0'.
- /// </summary>
- public static readonly char DefaultEndingChar = '\0';
- /// <summary>
- /// Gets blank space char, value = '\0'.
- /// </summary>
- public static readonly char DefaultBlankSpaceChar = '\0';
- /// <summary>
- /// Gets Empty <see cref="TgaString"/>.
- /// </summary>
- public static readonly TgaString Empty = new TgaString();
- /// <summary>
- /// Gets <see cref="TgaString"/> with <see cref="DefaultEndingChar"/> = '\0' and <see cref="UseEndingChar"/> = true.
- /// </summary>
- public static readonly TgaString ZeroTerminator = new TgaString(true);
- /// <summary>
- /// Gets "." <see cref="TgaString"/> with dot (period) symbol.
- /// </summary>
- public static readonly TgaString DotSymbol = new TgaString(DotSymbolConst, DotSymbolConst.Length);
- /// <summary>
- /// Gets "TRUEVISION-XFILE" <see cref="TgaString"/> (TGA File Format Version 2.0 signatute).
- /// </summary>
- public static readonly TgaString XFileSignatute = new TgaString(XFileSignatuteConst, XFileSignatuteConst.Length);
- /// <summary>
- /// Make full independed copy of <see cref="TgaString"/>.
- /// </summary>
- /// <returns>Copy of <see cref="TgaString"/></returns>
- public TgaString Clone()
- {
- return new TgaString(origString, length, useEnding, blankSpaceChar);
- }
- /// <summary>
- /// Make full independed copy of <see cref="TgaString"/>.
- /// </summary>
- /// <returns>Copy of <see cref="TgaString"/></returns>
- object ICloneable.Clone()
- {
- return Clone();
- }
- public override bool Equals(object obj)
- {
- return ((obj is TgaString) ? Equals((TgaString)obj) : false);
- }
- public bool Equals(TgaString item)
- {
- return (
- origString == item.origString &&
- length == item.length &&
- blankSpaceChar == item.blankSpaceChar &&
- useEnding == item.useEnding);
- }
- public static bool operator ==(TgaString item1, TgaString item2)
- {
- if (ReferenceEquals(item1, null))
- return ReferenceEquals(item2, null);
- if (ReferenceEquals(item2, null))
- return ReferenceEquals(item1, null);
- return item1.Equals(item2);
- }
- public static bool operator !=(TgaString item1, TgaString item2)
- {
- return !(item1 == item2);
- }
- public static TgaString operator +(TgaString item1, TgaString item2)
- {
- if (ReferenceEquals(item1, null) || ReferenceEquals(item2, null))
- throw new ArgumentNullException();
- return new TgaString(BitConverterExt.ToBytes(item1.ToBytes(), item2.ToBytes()));
- }
- public override int GetHashCode()
- {
- unchecked
- {
- int hash = 17;
- hash = hash * 23 + origString.GetHashCode();
- hash = hash * 23 + length.GetHashCode();
- hash = hash * 23 + blankSpaceChar.GetHashCode();
- hash = hash * 23 + useEnding.GetHashCode();
- return hash;
- }
- }
- /// <summary>
- /// Get ASCII-Like string with string-terminators, example: "Some string\0\0\0\0\0".
- /// </summary>
- /// <returns>String with replaced string-terminators to "\0".</returns>
- public override string ToString()
- {
- return Encoding.ASCII.GetString(ToBytes()).Replace("\0", @"\0");
- }
- /// <summary>
- /// Get ASCII-Like string to first string-terminator, example:
- /// "Some string \0 Some Data \0" - > "Some string".
- /// </summary>
- /// <returns>String to first string-terminator.</returns>
- public string GetString()
- {
- String Str = Encoding.ASCII.GetString(ToBytes());
- int EndIndex = Str.IndexOf('\0');
- if (EndIndex != -1)
- Str = Str.Substring(0, EndIndex);
- return Str;
- }
- /// <summary>
- /// Convert <see cref="TgaString"/> to byte array.
- /// </summary>
- /// <returns>Byte array, every byte is ASCII symbol.</returns>
- public byte[] ToBytes()
- {
- return ToBytes(origString, length, useEnding, blankSpaceChar);
- }
- /// <summary>
- /// Convert <see cref="TgaString"/> to byte array.
- /// </summary>
- /// <param name="str">Input string.</param>
- /// <param name="Length">Length of output ASCII string with Ending char (if used).</param>
- /// <param name="UseEnding">Add <see cref="EndingChr"/> to string or not?</param>
- /// <param name="BlankSpaceChar">Char for filling blank space in string. If this char is '-' (only for example!),
- /// for string "ABC" with <see cref="Length"/> = 7, with <see cref="UseEnding"/> = true,
- /// <see cref="DefaultEndingChar"/> is '\0', result string is "ABC---\0".</param>
- /// <returns>Byte array, every byte is ASCII symbol.</returns>
- public static byte[] ToBytes(string str, int Length, bool UseEnding = true, char BlankSpaceChar = '\0')
- {
- char[] C = new char[Math.Max(Length, (UseEnding ? 1 : 0))];
- for (int i = 0; i < C.Length; i++)
- C[i] = (i < str.Length ? str[i] : BlankSpaceChar);
- if (UseEnding)
- C[C.Length - 1] = DefaultEndingChar;
- return Encoding.ASCII.GetBytes(C);
- }
- }
- public class TgaTime : ICloneable
- {
- ushort hours = 0;
- ushort minutes = 0;
- ushort seconds = 0;
- /// <summary>
- /// Make empty <see cref="TgaTime"/>.
- /// </summary>
- public TgaTime()
- {
- }
- /// <summary>
- /// Make <see cref="TgaTime"/> from <see cref="TimeSpan"/>.
- /// </summary>
- /// <param name="Time">Some <see cref="TimeSpan"/> variable.</param>
- public TgaTime(TimeSpan Time)
- {
- hours = (ushort)Time.TotalHours;
- minutes = (ushort)Time.Minutes;
- seconds = (ushort)Time.Seconds;
- }
- /// <summary>
- /// Make <see cref="TgaTime"/> from ushort values.
- /// </summary>
- /// <param name="Hours">Hour (0 - 65535).</param>
- /// <param name="Minutes">Minute (0 - 59).</param>
- /// <param name="Seconds">Second (0 - 59).</param>
- public TgaTime(ushort Hours, ushort Minutes, ushort Seconds)
- {
- hours = Hours;
- minutes = Minutes;
- seconds = Seconds;
- }
- /// <summary>
- /// Make <see cref="TgaTime"/> from bytes.
- /// </summary>
- /// <param name="Bytes">Array of bytes(byte[6]).</param>
- public TgaTime(byte[] Bytes)
- {
- if (Bytes == null)
- throw new ArgumentNullException(nameof(Bytes) + " = null!");
- else if (Bytes.Length != Size)
- throw new ArgumentOutOfRangeException(nameof(Bytes) + " must be equal " + Size + "!");
- hours = BitConverter.ToUInt16(Bytes, 0);
- minutes = BitConverter.ToUInt16(Bytes, 2);
- seconds = BitConverter.ToUInt16(Bytes, 4);
- }
- /// <summary>
- /// Gets or Sets hour (0 - 65535).
- /// </summary>
- public ushort Hours
- {
- get { return hours; }
- set { hours = value; }
- }
- /// <summary>
- /// Gets or Sets minute (0 - 59).
- /// </summary>
- public ushort Minutes
- {
- get { return minutes; }
- set { minutes = value; }
- }
- /// <summary>
- /// Gets or Sets second (0 - 59).
- /// </summary>
- public ushort Seconds
- {
- get { return seconds; }
- set { seconds = value; }
- }
- /// <summary>
- /// Gets TGA Field size in bytes.
- /// </summary>
- public const int Size = 6;
- /// <summary>
- /// Make full independed copy of <see cref="TgaTime"/>.
- /// </summary>
- /// <returns>Copy of <see cref="TgaTime"/></returns>
- public TgaTime Clone()
- {
- return new TgaTime(hours, minutes, seconds);
- }
- /// <summary>
- /// Make full independed copy of <see cref="TgaTime"/>.
- /// </summary>
- /// <returns>Copy of <see cref="TgaTime"/></returns>
- object ICloneable.Clone()
- {
- return Clone();
- }
- public override bool Equals(object obj)
- {
- return ((obj is TgaTime) ? Equals((TgaTime)obj) : false);
- }
- public bool Equals(TgaTime item)
- {
- return (hours == item.hours && minutes == item.minutes && seconds == item.seconds);
- }
- public static bool operator ==(TgaTime item1, TgaTime item2)
- {
- if (ReferenceEquals(item1, null))
- return ReferenceEquals(item2, null);
- if (ReferenceEquals(item2, null))
- return ReferenceEquals(item1, null);
- return item1.Equals(item2);
- }
- public static bool operator !=(TgaTime item1, TgaTime item2)
- {
- return !(item1 == item2);
- }
- public override int GetHashCode()
- {
- unchecked
- {
- int hash = 17;
- hash = hash * 23 + hours.GetHashCode();
- hash = hash * 23 + (minutes << 16 | seconds).GetHashCode();
- return hash;
- }
- }
- /// <summary>
- /// Gets <see cref="TgaTime"/> like string.
- /// </summary>
- /// <returns>String in "H:M:S" format.</returns>
- public override string ToString()
- {
- return String.Format("{0}:{1}:{2}", hours, minutes, seconds);
- }
- /// <summary>
- /// Convert <see cref="TgaTime"/> to byte array.
- /// </summary>
- /// <returns>Byte array with length = 6.</returns>
- public byte[] ToBytes()
- {
- return BitConverterExt.ToBytes(hours, minutes, seconds);
- }
- /// <summary>
- /// Gets <see cref="TgaTime"/> like <see cref="TimeSpan"/>.
- /// </summary>
- /// <returns><see cref="TimeSpan"/> value of <see cref="TgaTime"/>.</returns>
- public TimeSpan ToTimeSpan()
- {
- return new TimeSpan(hours, minutes, seconds);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////
- /// <summary>
- /// File Header Area (18 bytes)
- /// </summary>
- public class TgaHeader : ICloneable
- {
- byte idLength = 0;
- TgaColorMapType colorMapType = TgaColorMapType.NoColorMap;
- TgaImageType imageType = TgaImageType.NoImageData;
- TgaColorMapSpec colorMapSpec = new TgaColorMapSpec();
- TgaImageSpec imageSpec = new TgaImageSpec();
- /// <summary>
- /// Make empty <see cref="TgaHeader"/>.
- /// </summary>
- public TgaHeader()
- {
- }
- /// <summary>
- /// Make <see cref="TgaHeader"/> from bytes.
- /// </summary>
- /// <param name="Bytes">Bytes array (byte[18]).</param>
- public TgaHeader(byte[] Bytes)
- {
- if (Bytes == null)
- throw new ArgumentNullException(nameof(Bytes) + " = null!");
- if (Bytes.Length != Size)
- throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
- idLength = Bytes[0];
- colorMapType = (TgaColorMapType)Bytes[1];
- imageType = (TgaImageType)Bytes[2];
- colorMapSpec = new TgaColorMapSpec(BitConverterExt.GetElements(Bytes, 3, TgaColorMapSpec.Size));
- imageSpec = new TgaImageSpec(BitConverterExt.GetElements(Bytes, 8, TgaImageSpec.Size));
- }
- /// <summary>
- /// ID Length - Field 1 (1 byte):
- /// This field identifies the number of bytes contained in the <see cref="ImageID"/> Field.
- /// The maximum number of characters is 255. A value of zero indicates that no Image ID
- /// field is included with the image.
- /// </summary>
- public byte IDLength
- {
- get { return idLength; }
- set { idLength = value; }
- }
- /// <summary>
- /// Color Map Type - Field 2 (1 byte):
- /// This field indicates the type of color map (if any) included with the image.
- /// There are currently 2 defined values for this field:
- /// <para>0 - indicates that no color-map data is included with this image;</para>
- /// <para>1 - indicates that a color-map is included with this image.</para>
- /// </summary>
- public TgaColorMapType ColorMapType
- {
- get { return colorMapType; }
- set { colorMapType = value; }
- }
- /// <summary>
- /// Image Type - Field 3 (1 byte):
- /// <para>The TGA File Format can be used to store Pseudo-Color, True-Color and Direct-Color images
- /// of various pixel depths.</para>
- /// </summary>
- public TgaImageType ImageType
- {
- get { return imageType; }
- set { imageType = value; }
- }
- /// <summary>
- /// Color Map Specification - Field 4 (5 bytes):
- /// <para>This field and its sub-fields describe the color map (if any) used for the image.
- /// If the Color Map Type field is set to zero, indicating that no color map exists, then
- /// these 5 bytes should be set to zero. These bytes always must be written to the file.</para>
- /// </summary>
- public TgaColorMapSpec ColorMapSpec
- {
- get { return colorMapSpec; }
- set { colorMapSpec = value; }
- }
- /// <summary>
- /// Image Specification - Field 5 (10 bytes):
- /// <para>This field and its sub-fields describe the image screen location, size and pixel depth.
- /// These information is always written to the file.</para>
- /// </summary>
- public TgaImageSpec ImageSpec
- {
- get { return imageSpec; }
- set { imageSpec = value; }
- }
- /// <summary>
- /// Gets TGA Header Section size in bytes.
- /// </summary>
- public const int Size = 18;
- /// <summary>
- /// Make full copy of <see cref="TgaHeader"/>.
- /// </summary>
- /// <returns>Full independent copy of <see cref="TgaHeader"/>.</returns>
- public TgaHeader Clone()
- {
- return new TgaHeader(ToBytes());
- }
- /// <summary>
- /// Make full copy of <see cref="TgaHeader"/>.
- /// </summary>
- /// <returns>Full independent copy of <see cref="TgaHeader"/>.</returns>
- object ICloneable.Clone()
- {
- return Clone();
- }
- public override bool Equals(object obj)
- {
- return ((obj is TgaHeader) ? Equals((TgaHeader)obj) : false);
- }
- public bool Equals(TgaHeader item)
- {
- return (idLength == item.idLength &&
- colorMapType == item.colorMapType &&
- imageType == item.imageType &&
- colorMapSpec == item.colorMapSpec &&
- imageSpec == item.imageSpec);
- }
- public static bool operator ==(TgaHeader item1, TgaHeader item2)
- {
- if (ReferenceEquals(item1, null))
- return ReferenceEquals(item2, null);
- if (ReferenceEquals(item2, null))
- return ReferenceEquals(item1, null);
- return item1.Equals(item2);
- }
- public static bool operator !=(TgaHeader item1, TgaHeader item2)
- {
- return !(item1 == item2);
- }
- public override int GetHashCode()
- {
- unchecked
- {
- int hash = 17;
- hash = hash * 23 + (idLength << 24 | (byte)colorMapType << 8 | (byte)imageType).GetHashCode();
- if (colorMapSpec != null)
- hash = hash * 23 + colorMapSpec.GetHashCode();
- if (imageSpec != null)
- hash = hash * 23 + imageSpec.GetHashCode();
- return hash;
- }
- }
- public override string ToString()
- {
- return String.Format("{0}={1}, {2}={3}, {4}={5}, {6}={7}, {8}={9}",
- nameof(IDLength), idLength,
- nameof(ColorMapType), colorMapType,
- nameof(ImageType), imageType,
- nameof(ColorMapSpec), colorMapSpec,
- nameof(ImageSpec), imageSpec);
- }
- /// <summary>
- /// Convert <see cref="TgaHeader"/> to byte array.
- /// </summary>
- /// <returns>Byte array with size equal <see cref="Size"/>.</returns>
- public byte[] ToBytes()
- {
- return BitConverterExt.ToBytes(idLength, (byte)colorMapType, (byte)imageType,
- (colorMapSpec == null ? new byte[TgaColorMapSpec.Size] : colorMapSpec.ToBytes()),
- (imageSpec == null ? new byte[TgaImageSpec.Size] : imageSpec.ToBytes()));
- }
- }
- /// <summary>
- /// Image Or ColorMap Area
- /// </summary>
- public class TgaImgOrColMap : ICloneable
- {
- TgaString imageID = null;
- byte[] colorMapData = null;
- byte[] imageData = null;
- /// <summary>
- /// Make empty <see cref="TgaImgOrColMap"/>.
- /// </summary>
- public TgaImgOrColMap()
- {
- }
- /// <summary>
- /// Make <see cref="TgaImgOrColMap"/> from arrays.
- /// </summary>
- /// <param name="ImageID">This optional field contains identifying information about the image.
- /// The maximum length for this field is 255 bytes. Refer to <see cref="TgaHeader.IDLength"/>
- /// for the length of this field. If field 1 is set to Zero indicating that no Image ID exists
- /// then these bytes are not written to the file.</param>
- /// <param name="ColorMapData">Color Map Data, see <see cref="ColorMapData"/> description.</param>
- /// <param name="ImageData">Image Data, see <see cref="ImageData"/> description.</param>
- public TgaImgOrColMap(TgaString ImageID, byte[] ColorMapData, byte[] ImageData)
- {
- imageID = ImageID;
- colorMapData = ColorMapData;
- imageData = ImageData;
- }
- /// <summary>
- /// Image ID - Field 6 (variable):
- /// <para>This optional field contains identifying information about the image. The maximum length
- /// for this field is 255 bytes. Refer to <see cref="TgaHeader.IDLength"/> for the length of this
- /// field. If field 1 is set to Zero indicating that no Image ID exists then these bytes are not
- /// written to the file. Can have text inside (ASCII).</para>
- /// </summary>
- public TgaString ImageID
- {
- get { return imageID; }
- set { imageID = value; }
- }
- /// <summary>
- /// Color Map Data - Field 7 (variable):
- /// <para>If the Color Map Type(field 2) field is set to zero indicating that no Color-Map
- /// exists then this field will not be present (i.e., no bytes written to the file).</para>
- /// <para>This variable-length field contains the actual color map information (LUT data).
- /// Field 4.3 specifies the width in bits of each color map entry while Field 4.2 specifies
- /// the number of color map entries in this field. These two fields together are used to
- /// determine the number of bytes contained in field 7.</para>
- /// <para>Each color map entry is stored using an integral number of bytes.The RGB specification
- /// for each color map entry is stored in successive bit-fields in the multi-byte entries.
- /// Each color bit-field is assumed to be MIN(Field4.3/3, 8) bits in length. If Field 4.3
- /// contains 24, then each color specification is 8 bits in length; if Field 4.3 contains 32,
- /// then each color specification is also 8 bits (32/3 gives 10, but 8 is smaller).
- /// Unused bit(s) in the multi-byte entries are assumed to specify attribute bits. The
- /// attribute bit field is often called the Alpha Channel, Overlay Bit(s) or Interrupt Bit(s).</para>
- /// For the TARGA M-8, ATVista and NuVista, the number of bits in a color map specification is
- /// 24 (or 32). The red, green, and blue components are each represented by one byte.
- /// </summary>
- public byte[] ColorMapData
- {
- get { return colorMapData; }
- set { colorMapData = value; }
- }
- /// <summary>
- /// Image Data - Field 8 (variable):
- /// <para>This field contains (Width)x(Height) pixels. Each pixel specifies image data in one
- /// of the following formats:</para>
- /// <para>a single color-map index for Pseudo-Color;
- /// Attribute, Red, Green and Blue ordered data for True-Color;
- /// and independent color-map indices for Direct-Color.</para>
- /// <para>The values for Width and Height are specified in Fields 5.3 and 5.4 respectively.
- /// The number of attribute and color-definition bits for each pixel are defined in Fields 5.6
- /// and 5.5, respectively.Each pixel is stored as an integral number of bytes.</para>
- /// </summary>
- public byte[] ImageData
- {
- get { return imageData; }
- set { imageData = value; }
- }
- /// <summary>
- /// Make full copy of <see cref="TgaImgOrColMap"/>.
- /// </summary>
- /// <returns>Full independed copy of <see cref="TgaImgOrColMap"/>.</returns>
- public TgaImgOrColMap Clone()
- {
- return new TgaImgOrColMap(
- (imageID == null ? null : imageID.Clone()),
- (colorMapData == null ? null : (byte[])colorMapData.Clone()),
- (imageData == null ? null : (byte[])imageData.Clone()));
- }
- /// <summary>
- /// Make full copy of <see cref="TgaImgOrColMap"/>.
- /// </summary>
- /// <returns>Full independed copy of <see cref="TgaImgOrColMap"/>.</returns>
- object ICloneable.Clone()
- {
- return Clone();
- }
- public override bool Equals(object obj)
- {
- return ((obj is TgaImgOrColMap) ? Equals((TgaImgOrColMap)obj) : false);
- }
- public bool Equals(TgaImgOrColMap item)
- {
- return imageID == item.imageID &&
- BitConverterExt.IsArraysEqual(colorMapData, item.colorMapData) &&
- BitConverterExt.IsArraysEqual(imageData, item.imageData);
- }
- public static bool operator ==(TgaImgOrColMap item1, TgaImgOrColMap item2)
- {
- if (ReferenceEquals(item1, null))
- return ReferenceEquals(item2, null);
- if (ReferenceEquals(item2, null))
- return ReferenceEquals(item1, null);
- return item1.Equals(item2);
- }
- public static bool operator !=(TgaImgOrColMap item1, TgaImgOrColMap item2)
- {
- return !(item1 == item2);
- }
- public override int GetHashCode()
- {
- unchecked
- {
- int hash = 27;
- if (imageID != null)
- hash = (13 * hash) + imageID.GetHashCode();
- if (colorMapData != null)
- for (int i = 0; i < colorMapData.Length; i++)
- hash = (13 * hash) + colorMapData[i].GetHashCode();
- if (imageData != null)
- for (int i = 0; i < imageData.Length; i++)
- hash = (13 * hash) + imageData[i].GetHashCode();
- return hash;
- }
- }
- } //No ToBytes()
- /// <summary>
- /// Developer Area
- /// </summary> //?
- public class TgaDevArea : ICloneable
- {
- List<TgaDevEntry> entries = new List<TgaDevEntry>();
- public TgaDevArea()
- {
- }
- public TgaDevArea(List<TgaDevEntry> Entries)
- {
- if (Entries == null)
- throw new ArgumentNullException(nameof(Entries) + " = null!");
- entries = Entries;
- }
- /// <summary>
- /// Developer Data - Field 9 (variable):
- /// </summary>
- public List<TgaDevEntry> Entries
- {
- get { return entries; }
- set { entries = value; }
- }
- public int Count
- {
- get { return entries.Count; }
- }
- public TgaDevEntry this[int index]
- {
- get { return entries[index]; }
- set { entries[index] = value; }
- }
- /// <summary>
- /// Make full copy of <see cref="TgaDevArea"/>.
- /// </summary>
- /// <returns>Full independent copy of <see cref="TgaDevArea"/>.</returns>
- public TgaDevArea Clone()
- {
- if (entries == null)
- return new TgaDevArea(null);
- List<TgaDevEntry> L = new List<TgaDevEntry>();
- for (int i = 0; i < entries.Count; i++)
- L.Add(entries[i].Clone());
- return new TgaDevArea(L);
- }
- /// <summary>
- /// Make full copy of <see cref="TgaDevArea"/>.
- /// </summary>
- /// <returns>Full independent copy of <see cref="TgaDevArea"/>.</returns>
- object ICloneable.Clone()
- {
- return Clone();
- }
- public override bool Equals(object obj)
- {
- return ((obj is TgaDevArea) ? Equals((TgaDevArea)obj) : false);
- }
- public bool Equals(TgaDevArea item)
- {
- return BitConverterExt.IsListsEqual(entries, item.entries);
- }
- public static bool operator ==(TgaDevArea item1, TgaDevArea item2)
- {
- if (ReferenceEquals(item1, null))
- return ReferenceEquals(item2, null);
- if (ReferenceEquals(item2, null))
- return ReferenceEquals(item1, null);
- return item1.Equals(item2);
- }
- public static bool operator !=(TgaDevArea item1, TgaDevArea item2)
- {
- return !(item1 == item2);
- }
- public override int GetHashCode()
- {
- unchecked
- {
- int hash = 27;
- if (entries != null)
- for (int i = 0; i < entries.Count; i++)
- hash = (13 * hash) + entries[i].GetHashCode();
- return hash;
- }
- }
- /// <summary>
- /// Convert <see cref="TgaDevArea"/> (without Fields Data, only Directory!) to byte array.
- /// </summary>
- /// <returns>Byte array, Len = (NUMBER_OF_TAGS_IN_THE_DIRECTORY * 10) + 2 bytes in size.
- /// The "+ 2" includes the 2 bytes for the number of tags in the directory.</returns>
- public byte[] ToBytes()
- {
- if (entries == null)
- throw new Exception(nameof(Entries) + " = null!");
- ushort NumberOfEntries = (ushort)Math.Min(ushort.MaxValue, entries.Count);
- List<byte> DevDir = new List<byte>(BitConverter.GetBytes(NumberOfEntries));
- for (int i = 0; i < entries.Count; i++)
- {
- DevDir.AddRange(BitConverter.GetBytes(entries[i].Tag));
- DevDir.AddRange(BitConverter.GetBytes(entries[i].Offset));
- DevDir.AddRange(BitConverter.GetBytes(entries[i].FieldSize));
- }
- return DevDir.ToArray();
- }
- } //Not full ToBytes()
- /// <summary>
- /// Extension Area
- /// </summary>
- public class TgaExtArea : ICloneable
- {
- public const int MinSize = 495; //bytes
- ushort extensionSize = MinSize;
- TgaString authorName = new TgaString(41, true);
- TgaComment authorComments = new TgaComment();
- TgaDateTime dateTimeStamp = new TgaDateTime();
- TgaString jobNameOrID = new TgaString(41, true);
- TgaTime jobTime = new TgaTime();
- TgaString softwareID = new TgaString(41, true);
- TgaSoftVersion softVersion = new TgaSoftVersion();
- TgaColorKey keyColor = new TgaColorKey();
- TgaFraction pixelAspectRatio = TgaFraction.Empty;
- TgaFraction gammaValue = TgaFraction.Empty;
- uint colorCorrectionOffset = 0;
- uint postageStampOffset = 0;
- uint scanLineOffset = 0;
- TgaAttrType attributesType = TgaAttrType.NoAlpha;
- uint[] scanLineTable = null;
- TgaPostageStampImage postageStampImage = null;
- ushort[] colorCorrectionTable = null;
- byte[] otherDataInExtensionArea = null;
- public TgaExtArea()
- {
- }
- /// <summary>
- /// Make <see cref="TgaExtArea"/> from bytes. Warning: <see cref="ScanLineTable"/>,
- /// <see cref="PostageStampImage"/>, <see cref="ColorCorrectionTable"/> not included,
- /// because thea are can be not in the Extension Area of TGA file!
- /// </summary>
- /// <param name="Bytes">Bytes of <see cref="TgaExtArea"/>.</param>
- /// <param name="SLT">Scan Line Table.</param>
- /// <param name="PostImg">Postage Stamp Image.</param>
- /// <param name="CCT">Color Correction Table.</param>
- public TgaExtArea(byte[] Bytes, uint[] SLT = null, TgaPostageStampImage PostImg = null, ushort[] CCT = null)
- {
- if (Bytes == null)
- throw new ArgumentNullException(nameof(Bytes) + " = null!");
- if (Bytes.Length < MinSize)
- throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be >= " + MinSize + "!");
- extensionSize = BitConverter.ToUInt16(Bytes, 0);
- authorName = new TgaString(BitConverterExt.GetElements(Bytes, 2, 41), true);
- authorComments = new TgaComment(BitConverterExt.GetElements(Bytes, 43, TgaComment.Size));
- dateTimeStamp = new TgaDateTime(BitConverterExt.GetElements(Bytes, 367, TgaDateTime.Size));
- jobNameOrID = new TgaString(BitConverterExt.GetElements(Bytes, 379, 41), true);
- jobTime = new TgaTime(BitConverterExt.GetElements(Bytes, 420, TgaTime.Size));
- softwareID = new TgaString(BitConverterExt.GetElements(Bytes, 426, 41), true);
- softVersion = new TgaSoftVersion(BitConverterExt.GetElements(Bytes, 467, TgaSoftVersion.Size));
- keyColor = new TgaColorKey(BitConverterExt.GetElements(Bytes, 470, TgaColorKey.Size));
- pixelAspectRatio = new TgaFraction(BitConverterExt.GetElements(Bytes, 474, TgaFraction.Size));
- gammaValue = new TgaFraction(BitConverterExt.GetElements(Bytes, 478, TgaFraction.Size));
- colorCorrectionOffset = BitConverter.ToUInt32(Bytes, 482);
- postageStampOffset = BitConverter.ToUInt32(Bytes, 486);
- scanLineOffset = BitConverter.ToUInt32(Bytes, 490);
- attributesType = (TgaAttrType)Bytes[494];
- if (extensionSize > MinSize)
- otherDataInExtensionArea = BitConverterExt.GetElements(Bytes, 495, Bytes.Length - MinSize);
- scanLineTable = SLT;
- postageStampImage = PostImg;
- colorCorrectionTable = CCT;
- }
- #region Properties
- /// <summary>
- /// Extension Size - Field 10 (2 Bytes):
- /// This field is a SHORT field which specifies the number of BYTES in the fixedlength portion of
- /// the Extension Area. For Version 2.0 of the TGA File Format, this number should be set to 495.
- /// If the number found in this field is not 495, then the file will be assumed to be of a
- /// version other than 2.0. If it ever becomes necessary to alter this number, the change
- /// will be controlled by Truevision, and will be accompanied by a revision to the TGA File
- /// Format with an accompanying change in the version number.
- /// </summary>
- public ushort ExtensionSize
- {
- get { return extensionSize; }
- set { extensionSize = value; }
- }
- /// <summary>
- /// Author Name - Field 11 (41 Bytes):
- /// Bytes 2-42 - This field is an ASCII field of 41 bytes where the last byte must be a null
- /// (binary zero). This gives a total of 40 ASCII characters for the name. If the field is used,
- /// it should contain the name of the person who created the image (author). If the field is not
- /// used, you may fill it with nulls or a series of blanks(spaces) terminated by a null.
- /// The 41st byte must always be a null.
- /// </summary>
- public TgaString AuthorName
- {
- get { return authorName; }
- set { authorName = value; }
- }
- /// <summary>
- /// Author Comments - Field 12 (324 Bytes):
- /// Bytes 43-366 - This is an ASCII field consisting of 324 bytes which are organized as four lines
- /// of 80 characters, each followed by a null terminator.This field is provided, in addition to the
- /// original IMAGE ID field(in the original TGA format), because it was determined that a few
- /// developers had used the IMAGE ID field for their own purposes.This field gives the developer
- /// four lines of 80 characters each, to use as an Author Comment area. Each line is fixed to 81
- /// bytes which makes access to the four lines easy.Each line must be terminated by a null.
- /// If you do not use all 80 available characters in the line, place the null after the last
- /// character and blank or null fill the rest of the line. The 81st byte of each of the four
- /// lines must be null.
- /// </summary>
- public TgaComment AuthorComments
- {
- get { return authorComments; }
- set { authorComments = value; }
- }
- /// <summary>
- /// Date/Time Stamp - Field 13 (12 Bytes):
- /// Bytes 367-378 - This field contains a series of 6 SHORT values which define the integer
- /// value for the date and time that the image was saved. This data is formatted as follows:
- /// <para>SHORT 0: Month(1 - 12)</para>
- /// <para>SHORT 1: Day(1 - 31)</para>
- /// <para>SHORT 2: Year(4 digit, ie. 1989)</para>
- /// <para>SHORT 3: Hour(0 - 23)</para>
- /// <para>SHORT 4: Minute(0 - 59)</para>
- /// <para>SHORT 5: Second(0 - 59)</para>
- /// Even though operating systems typically time- and date-stamp files, this feature is
- /// provided because the operating system may change the time and date stamp if the file is
- /// copied. By using this area, you are guaranteed an unmodified region for date and time
- /// recording. If the fields are not used, you should fill them with binary zeros (0).
- /// </summary>
- public TgaDateTime DateTimeStamp
- {
- get { return dateTimeStamp; }
- set { dateTimeStamp = value; }
- }
- /// <summary>
- /// Job Name/ID - Field 14 (41 Bytes):
- /// Bytes 379-419 - This field is an ASCII field of 41 bytes where the last byte must be
- /// a binary zero. This gives a total of 40 ASCII characters for the job name or the ID.
- /// If the field is used, it should contain a name or id tag which refers to the job with
- /// which the image was associated.This allows production companies (and others) to tie
- /// images with jobs by using this field as a job name (i.e., CITY BANK) or job id number
- /// (i.e., CITY023). If the field is not used, you may fill it with a null terminated series
- /// of blanks (spaces) or nulls. In any case, the 41st byte must be a null.
- /// </summary>
- public TgaString JobNameOrID
- {
- get { return jobNameOrID; }
- set { jobNameOrID = value; }
- }
- /// <summary>
- /// Job Time - Field 15 (6 Bytes):
- /// Bytes 420-425 - This field contains a series of 3 SHORT values which define the integer
- /// value for the job elapsed time when the image was saved.This data is formatted as follows:
- /// <para>SHORT 0: Hours(0 - 65535)</para>
- /// <para>SHORT 1: Minutes(0 - 59)</para>
- /// <para>SHORT 2: Seconds(0 - 59)</para>
- /// The purpose of this field is to allow production houses (and others) to keep a running total
- /// of the amount of time invested in a particular image. This may be useful for billing, costing,
- /// and time estimating. If the fields are not used, you should fill them with binary zeros (0).
- /// </summary>
- public TgaTime JobTime
- {
- get { return jobTime; }
- set { jobTime = value; }
- }
- /// <summary>
- /// Software ID - Field 16 (41 Bytes):
- /// Bytes 426-466 - This field is an ASCII field of 41 bytes where the last byte must be
- /// a binary zero (null). This gives a total of 40 ASCII characters for the Software ID.
- /// The purpose of this field is to allow software to determine and record with what program
- /// a particular image was created.If the field is not used, you may fill it with a
- /// null terminated series of blanks (spaces) or nulls. The 41st byte must always be a null.
- /// </summary>
- public TgaString SoftwareID
- {
- get { return softwareID; }
- set { softwareID = value; }
- }
- /// <summary>
- /// Software Version - Field 17 (3 Bytes):
- /// Bytes 467-469 - This field consists of two sub-fields, a SHORT and an ASCII BYTE.
- /// The purpose of this field is to define the version of software defined by the
- /// “Software ID” field above. The SHORT contains the version number as a binary
- /// integer times 100.
- /// <para>Therefore, software version 4.17 would be the integer value 417.This allows for
- /// two decimal positions of sub-version.The ASCII BYTE supports developers who also
- /// tag a release letter to the end. For example, if the version number is 1.17b, then
- /// the SHORT would contain 117. and the ASCII BYTE would contain “b”.
- /// The organization is as follows:</para>
- /// <para>SHORT (Bytes 0 - 1): Version Number * 100</para>
- /// <para>BYTE(Byte 2): Version Letter</para>
- /// If you do not use this field, set the SHORT to binary zero, and the BYTE to a space(“ “)
- /// </summary>
- public TgaSoftVersion SoftVersion
- {
- get { return softVersion; }
- set { softVersion = value; }
- }
- /// <summary>
- /// Key Color - Field 18 (4 Bytes):
- /// Bytes 470-473 - This field contains a long value which is the key color in effect at
- /// the time the image is saved. The format is in A:R:G:B where ‘A’ (most significant byte)
- /// is the alpha channel key color(if you don’t have an alpha channel in your application,
- /// keep this byte zero [0]).
- /// <para>The Key Color can be thought of as the ‘background color’ or ‘transparent color’.
- /// This is the color of the ‘non image’ area of the screen, and the same color that the
- /// screen would be cleared to if erased in the application. If you don’t use this field,
- /// set it to all zeros (0). Setting the field to all zeros is the same as selecting a key
- /// color of black.</para>
- /// A good example of a key color is the ‘transparent color’ used in TIPS™ for WINDOW loading/saving.
- /// </summary>
- public TgaColorKey KeyColor
- {
- get { return keyColor; }
- set { keyColor = value; }
- }
- /// <summary>
- /// Pixel Aspect Ratio - Field 19 (4 Bytes):
- /// Bytes 474-477 - This field contains two SHORT sub-fields, which when taken together
- /// specify a pixel size ratio.The format is as follows:
- /// <para>SHORT 0: Pixel Ratio Numerator(pixel width)</para>
- /// <para>SHORT 1: Pixel Ratio Denominator(pixel height)</para>
- /// These sub-fields may be used to determine the aspect ratio of a pixel. This is useful when
- /// it is important to preserve the proper aspect ratio of the saved image. If the two values
- /// are set to the same non-zero value, then the image is composed of square pixels. A zero
- /// in the second sub-field (denominator) indicates that no pixel aspect ratio is specified.
- /// </summary>
- public TgaFraction PixelAspectRatio
- {
- get { return pixelAspectRatio; }
- set { pixelAspectRatio = value; }
- }
- /// <summary>
- /// Gamma Value - Field 20 (4 Bytes):
- /// Bytes 478-481 - This field contains two SHORT sub-fields, which when taken together in a ratio,
- /// provide a fractional gamma value.The format is as follows:
- /// <para>SHORT 0: Gamma Numerator</para>
- /// <para>SHORT 1: Gamma Denominator</para>
- /// The resulting value should be in the range of 0.0 to 10.0, with only one decimal place of
- /// precision necessary. An uncorrected image (an image with no gamma) should have the value 1.0 as
- /// the result.This may be accomplished by placing thesame, non-zero values in both positions
- /// (i.e., 1/1). If you decide to totally ignore this field, please set the denominator (the second
- /// SHORT) to the value zero. This will indicate that the Gamma Value field is not being used.
- /// </summary>
- public TgaFraction GammaValue
- {
- get { return gammaValue; }
- set { gammaValue = value; }
- }
- /// <summary>
- /// Color Correction Offset - Field 21 (4 Bytes):
- /// Bytes 482-485 - This field is a 4-byte field containing a single offset value. This is an offset
- /// from the beginning of the file to the start of the Color Correction table. This table may be
- /// written anywhere between the end of the Image Data field (field 8) and the start of the TGA
- /// File Footer. If the image has no Color Correction Table or if the Gamma Value setting is
- /// sufficient, set this value to zero and do not write a Correction Table anywhere.
- /// </summary>
- public uint ColorCorrectionTableOffset
- {
- get { return colorCorrectionOffset; }
- set { colorCorrectionOffset = value; }
- }
- /// <summary>
- /// Postage Stamp Offset - Field 22 (4 Bytes):
- /// Bytes 486-489 - This field is a 4-byte field containing a single offset value. This is an offset
- /// from the beginning of the file to the start of the Postage Stamp Image. The Postage Stamp Image
- /// must be written after Field 25 (Scan Line Table) but before the start of the TGA File Footer.
- /// If no postage stamp is stored, set this field to the value zero (0).
- /// </summary>
- public uint PostageStampOffset
- {
- get { return postageStampOffset; }
- set { postageStampOffset = value; }
- }
- /// <summary>
- /// Scan Line Offset - Field 23 (4 Bytes):
- /// Bytes 490-493 - This field is a 4-byte field containing a single offset value. This is an
- /// offset from the beginning of the file to the start of the Scan Line Table.
- /// </summary>
- public uint ScanLineOffset
- {
- get { return scanLineOffset; }
- set { scanLineOffset = value; }
- }
- /// <summary>
- /// Attributes Type - Field 24 (1 Byte):
- /// Byte 494 - This single byte field contains a value which specifies the type of Alpha channel
- /// data contained in the file. Value Meaning:
- /// <para>0: no Alpha data included (bits 3-0 of field 5.6 should also be set to zero)</para>
- /// <para>1: undefined data in the Alpha field, can be ignored</para>
- /// <para>2: undefined data in the Alpha field, but should be retained</para>
- /// <para>3: useful Alpha channel data is present</para>
- /// <para>4: pre-multiplied Alpha(see description below)</para>
- /// <para>5 -127: RESERVED</para>
- /// <para>128-255: Un-assigned</para>
- /// <para>Pre-multiplied Alpha Example: Suppose the Alpha channel data is being used to specify the
- /// opacity of each pixel(for use when the image is overlayed on another image), where 0 indicates
- /// that the pixel is completely transparent and a value of 1 indicates that the pixel is
- /// completely opaque(assume all component values have been normalized).</para>
- /// <para>A quadruple(a, r, g, b) of( 0.5, 1, 0, 0) would indicate that the pixel is pure red with a
- /// transparency of one-half. For numerous reasons(including image compositing) is is better to
- /// pre-multiply the individual color components with the value in the Alpha channel.</para>
- /// A pre-multiplication of the above would produce a quadruple(0.5, 0.5, 0, 0).
- /// A value of 3 in the Attributes Type Field(field 23) would indicate that the color components
- /// of the pixel have already been scaled by the value in the Alpha channel.
- /// </summary>
- public TgaAttrType AttributesType
- {
- get { return attributesType; }
- set { attributesType = value; }
- }
- /// <summary>
- /// Scan Line Table - Field 25 (Variable):
- /// This information is provided, at the developers’ request, for two purposes:
- /// <para>1) To make random access of compressed images easy.</para>
- /// <para>2) To allow “giant picture” access in smaller “chunks”.</para>
- /// This table should contain a series of 4-byte offsets.Each offset you write should point to the
- /// start of the next scan line, in the order that the image was saved (i.e., top down or bottom up).
- /// The offset should be from the start of the file.Therefore, you will have a four byte value for
- /// each scan line in your image. This means that if your image is 768 pixels tall, you will have 768,
- /// 4-byte offset pointers (for a total of 3072 bytes). This size is not extreme, and thus this table
- /// can be built and maintained in memory, and then written out at the proper time.
- /// </summary>
- public uint[] ScanLineTable
- {
- get { return scanLineTable; }
- set { scanLineTable = value; }
- }
- /// <summary>
- /// Postage Stamp Image - Field 26 (Variable):
- /// The Postage Stamp area is a smaller representation of the original image. This is useful for
- /// “browsing” a collection of image files. If your application can deal with a postage stamp image,
- /// it is recommended that you create one using sub-sampling techniques to create the best
- /// representation possible. The postage stamp image must be stored in the same format as the normal
- /// image specified in the file, but without any compression. The first byte of the postage stamp
- /// image specifies the X size of the stamp in pixels, the second byte of the stamp image specifies the
- /// Y size of the stamp in pixels. Truevision does not recommend stamps larger than 64x64 pixels, and
- /// suggests that any stamps stored larger be clipped. Obviously, the storage of the postage stamp
- /// relies heavily on the storage of the image. The two images are stored using the same format under
- /// the assumption that if you can read the image, then you can read the postage stamp. If the original
- /// image is color mapped, DO NOT average the postage stamp, as you will create new colors not in your map.
- /// </summary>
- public TgaPostageStampImage PostageStampImage
- {
- get { return postageStampImage; }
- set { postageStampImage = value; }
- }
- /// <summary>
- /// Color Correction Table - Field 27 (2K Bytes):
- /// The Color Correction Table is a block of 256 x 4 SHORT values, where each set of
- /// four contiguous values are the desired A:R:G:B correction for that entry. This
- /// allows the user to store a correction table for image remapping or LUT driving.
- /// Since each color in the block is a SHORT, the maximum value for a color gun (i.e.,
- /// A, R, G, B) is 65535, and the minimum value is zero.Therefore, BLACK maps to
- /// 0, 0, 0, 0 and WHITE maps to 65535, 65535, 65535, 65535.
- /// </summary>
- public ushort[] ColorCorrectionTable
- {
- get { return colorCorrectionTable; }
- set { colorCorrectionTable = value; }
- }
- /// <summary>
- /// Other Data In Extension Area (if <see cref="ExtensionSize"/> > 495).
- /// </summary>
- public byte[] OtherDataInExtensionArea
- {
- get { return otherDataInExtensionArea; }
- set { otherDataInExtensionArea = value; }
- }
- #endregion
- /// <summary>
- /// Make full copy of <see cref="TgaExtArea"/>.
- /// </summary>
- /// <returns>Full independent copy of <see cref="TgaExtArea"/>.</returns>
- public TgaExtArea Clone()
- {
- TgaExtArea NewExtArea = new TgaExtArea();
- NewExtArea.extensionSize = extensionSize;
- NewExtArea.authorName = authorName.Clone();
- NewExtArea.authorComments = authorComments.Clone();
- NewExtArea.dateTimeStamp = dateTimeStamp.Clone();
- NewExtArea.jobNameOrID = jobNameOrID.Clone();
- NewExtArea.jobTime = jobTime.Clone();
- NewExtArea.softwareID = softwareID.Clone();
- NewExtArea.softVersion = softVersion.Clone();
- NewExtArea.keyColor = keyColor.Clone();
- NewExtArea.pixelAspectRatio = pixelAspectRatio.Clone();
- NewExtArea.gammaValue = gammaValue.Clone();
- NewExtArea.colorCorrectionOffset = colorCorrectionOffset;
- NewExtArea.postageStampOffset = postageStampOffset;
- NewExtArea.scanLineOffset = scanLineOffset;
- NewExtArea.attributesType = attributesType;
- if (scanLineTable != null)
- NewExtArea.scanLineTable = (uint[])scanLineTable.Clone();
- if (postageStampImage != null)
- NewExtArea.postageStampImage = new TgaPostageStampImage(postageStampImage.ToBytes());
- if (colorCorrectionTable != null)
- NewExtArea.colorCorrectionTable = (ushort[])colorCorrectionTable.Clone();
- if (otherDataInExtensionArea != null)
- NewExtArea.otherDataInExtensionArea = (byte[])otherDataInExtensionArea.Clone();
- return NewExtArea;
- }
- /// <summary>
- /// Make full copy of <see cref="TgaExtArea"/>.
- /// </summary>
- /// <returns>Full independent copy of <see cref="TgaExtArea"/>.</returns>
- object ICloneable.Clone()
- {
- return Clone();
- }
- public override bool Equals(object obj)
- {
- return ((obj is TgaExtArea) ? Equals((TgaExtArea)obj) : false);
- }
- public bool Equals(TgaExtArea item)
- {
- return (extensionSize == item.extensionSize &&
- authorName == item.authorName &&
- authorComments == item.authorComments &&
- dateTimeStamp == item.dateTimeStamp &&
- jobNameOrID == item.jobNameOrID &&
- jobTime == item.jobTime &&
- softwareID == item.softwareID &&
- softVersion == item.softVersion &&
- keyColor == item.keyColor &&
- pixelAspectRatio == item.pixelAspectRatio &&
- gammaValue == item.gammaValue &&
- colorCorrectionOffset == item.colorCorrectionOffset &&
- postageStampOffset == item.postageStampOffset &&
- scanLineOffset == item.scanLineOffset &&
- attributesType == item.attributesType &&
- BitConverterExt.IsArraysEqual(scanLineTable, item.scanLineTable) &&
- postageStampImage == item.postageStampImage &&
- BitConverterExt.IsArraysEqual(colorCorrectionTable, item.colorCorrectionTable) &&
- BitConverterExt.IsArraysEqual(otherDataInExtensionArea, item.otherDataInExtensionArea));
- }
- public static bool operator ==(TgaExtArea item1, TgaExtArea item2)
- {
- if (ReferenceEquals(item1, null))
- return ReferenceEquals(item2, null);
- if (ReferenceEquals(item2, null))
- return ReferenceEquals(item1, null);
- return item1.Equals(item2);
- }
- public static bool operator !=(TgaExtArea item1, TgaExtArea item2)
- {
- return !(item1 == item2);
- }
- public override int GetHashCode()
- {
- unchecked
- {
- int hash = 27;
- hash = (13 * hash) + extensionSize.GetHashCode();
- hash = (13 * hash) + authorName.GetHashCode();
- hash = (13 * hash) + authorComments.GetHashCode();
- hash = (13 * hash) + dateTimeStamp.GetHashCode();
- hash = (13 * hash) + jobNameOrID.GetHashCode();
- hash = (13 * hash) + jobTime.GetHashCode();
- hash = (13 * hash) + softwareID.GetHashCode();
- hash = (13 * hash) + softVersion.GetHashCode();
- hash = (13 * hash) + keyColor.GetHashCode();
- hash = (13 * hash) + pixelAspectRatio.GetHashCode();
- hash = (13 * hash) + gammaValue.GetHashCode();
- hash = (13 * hash) + colorCorrectionOffset.GetHashCode();
- hash = (13 * hash) + postageStampOffset.GetHashCode();
- hash = (13 * hash) + scanLineOffset.GetHashCode();
- hash = (13 * hash) + attributesType.GetHashCode();
- if (scanLineTable != null)
- for (int i = 0; i < scanLineTable.Length; i++)
- hash = (13 * hash) + scanLineTable[i].GetHashCode();
- if (postageStampImage != null)
- hash = (13 * hash) + postageStampImage.GetHashCode();
- if (colorCorrectionTable != null)
- for (int i = 0; i < colorCorrectionTable.Length; i++)
- hash = (13 * hash) + colorCorrectionTable[i].GetHashCode();
- if (otherDataInExtensionArea != null)
- for (int i = 0; i < otherDataInExtensionArea.Length; i++)
- hash = (13 * hash) + otherDataInExtensionArea[i].GetHashCode();
- return hash;
- }
- }
- /// <summary>
- /// Convert <see cref="TgaExtArea"/> to byte array. Warning: <see cref="ScanLineTable"/>,
- /// <see cref="PostageStampImage"/>, <see cref="ColorCorrectionTable"/> not included,
- /// because thea are can be not in the Extension Area of TGA file!
- /// </summary>
- /// <returns>Byte array.</returns>
- public byte[] ToBytes()
- {
- #region Exceptions check
- if (authorName == null)
- authorName = new TgaString(41, true);
- if (authorComments == null)
- authorComments = new TgaComment();
- if (dateTimeStamp == null)
- dateTimeStamp = new TgaDateTime(DateTime.UtcNow);
- if (jobNameOrID == null)
- jobNameOrID = new TgaString(41, true);
- if (jobTime == null)
- jobTime = new TgaTime();
- if (softwareID == null)
- softwareID = new TgaString(41, true);
- if (softVersion == null)
- softVersion = new TgaSoftVersion();
- if (keyColor == null)
- keyColor = new TgaColorKey();
- if (pixelAspectRatio == null)
- pixelAspectRatio = new TgaFraction();
- if (gammaValue == null)
- gammaValue = new TgaFraction();
- #endregion
- return BitConverterExt.ToBytes(
- extensionSize,
- authorName.ToBytes(),
- authorComments.ToBytes(),
- dateTimeStamp.ToBytes(),
- jobNameOrID.ToBytes(),
- jobTime.ToBytes(),
- softwareID.ToBytes(),
- softVersion.ToBytes(),
- keyColor.ToBytes(),
- pixelAspectRatio.ToBytes(),
- gammaValue.ToBytes(),
- colorCorrectionOffset,
- postageStampOffset,
- scanLineOffset,
- (byte)attributesType,
- otherDataInExtensionArea);
- }
- } //Not full ToBytes()
- /// <summary>
- /// File Footer Area
- /// </summary>
- public class TgaFooter : ICloneable
- {
- uint extAreaOffset = 0;
- uint devDirOffset = 0;
- TgaString signature = TgaString.XFileSignatute;
- TgaString reservedChar = TgaString.DotSymbol;
- TgaString zeroStrTerminator = TgaString.ZeroTerminator;
- /// <summary>
- /// Make NewXFile format TGA Footer with <see cref="ExtensionAreaOffset"/> = 0 and
- /// <see cref="DeveloperDirectoryOffset"/> = 0.
- /// </summary>
- public TgaFooter()
- {
- }
- /// <summary>
- /// Make <see cref="TgaFooter"/> from values.
- /// </summary>
- /// <param name="ExtOff">Extension Area Offset, offset from the beginning of the file.</param>
- /// <param name="DevDirOff">Developer Directory Offset, offset from the beginning of the file.</param>
- /// <param name="Sign">New TGA format signature.</param>
- /// <param name="ReservChr">Reserved Character - ASCII character “.” (period).</param>
- /// <param name="Termin">Binary Zero Terminator, a binary zero which acts as a final terminator.</param>
- public TgaFooter(uint ExtOff, uint DevDirOff, TgaString Sign, TgaString ReservChr, TgaString Termin)
- {
- extAreaOffset = ExtOff;
- devDirOffset = DevDirOff;
- signature = Sign;
- reservedChar = ReservChr;
- zeroStrTerminator = Termin;
- }
- /// <summary>
- /// Make <see cref="TgaFooter"/> from bytes (if signature is right).
- /// </summary>
- /// <param name="Bytes">Bytes array (byte[26]).</param>
- public TgaFooter(byte[] Bytes)
- {
- if (Bytes == null)
- throw new ArgumentNullException(nameof(Bytes) + " = null!");
- if (Bytes.Length != Size)
- throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
- extAreaOffset = BitConverter.ToUInt32(Bytes, 0);
- devDirOffset = BitConverter.ToUInt32(Bytes, 4);
- signature = new TgaString(BitConverterExt.GetElements(Bytes, 8, TgaString.XFileSignatuteConst.Length));
- reservedChar = new TgaString(new byte[] { Bytes[24] });
- zeroStrTerminator = new TgaString(new byte[] { Bytes[25] });
- }
- /// <summary>
- /// Byte 0-3 - Extension Area Offset - Field 28
- /// The first four bytes (bytes 0-3, the first LONG) of the TGA File Footer contain an
- /// offset from the beginning of the file to the start of the Extension Area. Simply
- /// SEEK to this location to position to the start of the Extension Area. If the
- /// Extension Area Offset is zero, no Extension Area exists in the file.
- /// </summary>
- public uint ExtensionAreaOffset
- {
- get { return extAreaOffset; }
- set { extAreaOffset = value; }
- }
- /// <summary>
- /// Byte 4-7 - Developer Directory Offset - Field 29
- /// The next four bytes(bytes 4-7, the second LONG) contain an offset from the
- /// beginning of the file to the start of the Developer Directory. If the Developer
- /// Directory Offset is zero, then the Developer Area does not exist.
- /// </summary>
- public uint DeveloperDirectoryOffset
- {
- get { return devDirOffset; }
- set { devDirOffset = value; }
- }
- /// <summary>
- /// Byte 8-23 - Signature - Field 30
- /// This string is exactly 16 bytes long and is formatted exactly as shown below
- /// capital letters), with a hyphen between “TRUEVISION” and “XFILE.” If the
- /// signature is detected, the file is assumed to be of the New TGA format and MAY,
- /// therefore, contain the Developer Area and/or the Extension Area fields.If the
- /// signature is not found, then the file is assumed to be in the Original TGA format.
- /// </summary>
- public TgaString Signature
- {
- get { return signature; }
- set { signature = value; }
- }
- /// <summary>
- /// Byte 24 - Reserved Character - Field 31
- /// Byte 24 is an ASCII character “.” (period). This character MUST BE a period or
- /// the file is not considered a proper TGA file.
- /// </summary>
- public TgaString ReservedCharacter
- {
- get { return reservedChar; }
- set { reservedChar = value; }
- }
- /// <summary>
- /// Byte 25 - Binary Zero String Terminator - Field 32
- /// Byte 25 is a binary zero which acts as a final terminator and allows the entire TGA
- /// File Footer to be read and utilized as a “C” string.
- /// </summary>
- public TgaString BinaryZeroStringTerminator
- {
- get { return zeroStrTerminator; }
- set { zeroStrTerminator = value; }
- }
- /// <summary>
- /// Make full copy of <see cref="TgaFooter"/>.
- /// </summary>
- /// <returns></returns>
- public TgaFooter Clone()
- {
- return new TgaFooter(extAreaOffset, devDirOffset, signature.Clone(),
- reservedChar.Clone(), zeroStrTerminator.Clone());
- }
- /// <summary>
- /// Make full copy of <see cref="TgaFooter"/>.
- /// </summary>
- /// <returns></returns>
- object ICloneable.Clone()
- {
- return Clone();
- }
- /// <summary>
- /// Gets TGA Footer Section size in bytes.
- /// </summary>
- public const int Size = 26;
- public override bool Equals(object obj)
- {
- return ((obj is TgaFooter) ? Equals((TgaFooter)obj) : false);
- }
- public bool Equals(TgaFooter item)
- {
- return (extAreaOffset == item.extAreaOffset &&
- devDirOffset == item.devDirOffset &&
- signature == item.signature &&
- reservedChar == item.reservedChar &&
- zeroStrTerminator == item.zeroStrTerminator);
- }
- public static bool operator ==(TgaFooter item1, TgaFooter item2)
- {
- if (ReferenceEquals(item1, null))
- return ReferenceEquals(item2, null);
- if (ReferenceEquals(item2, null))
- return ReferenceEquals(item1, null);
- return item1.Equals(item2);
- }
- public static bool operator !=(TgaFooter item1, TgaFooter item2)
- {
- return !(item1 == item2);
- }
- public override int GetHashCode()
- {
- unchecked
- {
- int hash = 17;
- hash = hash * 23 + extAreaOffset.GetHashCode();
- hash = hash * 23 + devDirOffset.GetHashCode();
- if (signature != null)
- hash = hash * 23 + signature.GetHashCode();
- if (reservedChar != null)
- hash = hash * 23 + reservedChar.GetHashCode();
- if (zeroStrTerminator != null)
- hash = hash * 23 + zeroStrTerminator.GetHashCode();
- return hash;
- }
- }
- public override string ToString()
- {
- return String.Format("{0}={1}, {2}={3}, FullSignature={4}",
- nameof(ExtensionAreaOffset), extAreaOffset, nameof(DeveloperDirectoryOffset), devDirOffset,
- (signature + reservedChar + zeroStrTerminator).ToString());
- }
- /// <summary>
- /// Convert <see cref="TgaFooter"/> to byte array.
- /// </summary>
- /// <returns>Byte array with size equal <see cref="Size"/>.</returns>
- public byte[] ToBytes()
- {
- return BitConverterExt.ToBytes(extAreaOffset, devDirOffset,
- signature.ToBytes(), reservedChar.ToBytes(), zeroStrTerminator.ToBytes());
- }
- /// <summary>
- /// Is footer is real footer of TGA File Format Version 2.0?
- /// Checking by <see cref="TgaString.XFileSignatute"/>.
- /// </summary>
- public bool IsFooterCorrect
- {
- get { return signature == TgaString.XFileSignatute; }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////
- /// <summary>
- /// Simplify ByteConversion operations, like concatination of byte arrays, comparing and other.
- /// </summary>
- public static class BitConverterExt
- {
- /// <summary>
- /// Combine byte, byte[], (u)short, (u)int, (u)long values to byte[] array.
- /// </summary>
- /// <param name="obj">Array of byte, byte[], (u)short, (u)int, (u)long values.</param>
- /// <returns>Array of bytes, null when some object is null.</returns>
- public static byte[] ToBytes(params object[] obj)
- {
- if (obj == null)
- return null;
- List<byte> BytesList = new List<byte>();
- for (int i = 0; i < obj.Length; i++)
- {
- if (obj[i] == null)
- continue;
- else if (obj[i] is byte)
- BytesList.Add((byte)obj[i]);
- else if (obj[i] is byte[])
- BytesList.AddRange((byte[])obj[i]);
- else if (obj[i] is short)
- BytesList.AddRange(BitConverter.GetBytes((short)obj[i]));
- else if (obj[i] is ushort)
- BytesList.AddRange(BitConverter.GetBytes((ushort)obj[i]));
- else if (obj[i] is int)
- BytesList.AddRange(BitConverter.GetBytes((int)obj[i]));
- else if (obj[i] is uint)
- BytesList.AddRange(BitConverter.GetBytes((uint)obj[i]));
- else if (obj[i] is long)
- BytesList.AddRange(BitConverter.GetBytes((long)obj[i]));
- else if (obj[i] is ulong)
- BytesList.AddRange(BitConverter.GetBytes((ulong)obj[i]));
- }
- return BytesList.ToArray();
- }
- /// <summary>
- /// Copies a range of elements from an Array starting at the specified source index.
- /// The length and the index are specified as 32-bit integers.
- /// </summary>
- /// <param name="SrcArray">The <see cref="Array"/> that contains the data to copy.</param>
- /// <param name="Offset">A 32-bit integer that represents the index in the
- /// <see cref="SrcArray"/> at which copying begins.</param>
- /// <param name="Count">A 32-bit integer that represents the number of elements to copy.</param>
- /// <returns></returns>
- public static T[] GetElements<T>(T[] SrcArray, int Offset, int Count)
- {
- if (SrcArray == null)
- throw new ArgumentNullException(nameof(SrcArray) + " is null!");
- if (Offset >= SrcArray.Length || Offset < 0)
- throw new ArgumentOutOfRangeException(nameof(Offset) + " has wrong value!");
- if (Count <= 0 || Offset + Count > SrcArray.Length)
- throw new ArgumentOutOfRangeException(nameof(Count) + " has wrong value!");
- T[] Buff = new T[Count];
- Array.Copy(SrcArray, Offset, Buff, 0, Buff.Length);
- return Buff;
- }
- /// <summary>
- /// Compare N-dimensional Arrays.
- /// </summary>
- /// <typeparam name="T">Arrays Type.</typeparam>
- /// <param name="item1">First Array.</param>
- /// <param name="item2">Second Array.</param>
- /// <returns>True, if Arrays are equal.</returns>
- public static bool IsArraysEqual<T>(T[] item1, T[] item2)
- {
- if (ReferenceEquals(item1, item2))
- return true;
- if (item1 == null || item2 == null)
- return false;
- if (item1.Length != item2.Length)
- return false;
- EqualityComparer<T> comparer = EqualityComparer<T>.Default;
- for (int i = 0; i < item1.Length; i++)
- if (!comparer.Equals(item1[i], item2[i]))
- return false;
- return true;
- }
- /// <summary>
- /// Compare Lists.
- /// </summary>
- /// <typeparam name="T">List Type.</typeparam>
- /// <param name="item1">First List.</param>
- /// <param name="item2">Second List.</param>
- /// <returns>True, if Lists are equal.</returns>
- public static bool IsListsEqual<T>(List<T> item1, List<T> item2)
- {
- if (ReferenceEquals(item1, item2))
- return true;
- if (item1 == null || item2 == null)
- return false;
- if (item1.Count != item2.Count)
- return false;
- for (int i = 0; i < item1.Count; i++)
- if (!item1[i].Equals(item2[i]))
- return false;
- return true;
- }
- /// <summary>
- /// Compare elements in one Array with different offsets.
- /// </summary>
- /// <typeparam name="T">Array type.</typeparam>
- /// <param name="Arr">Some Array.</param>
- /// <param name="Offset1">First offset.</param>
- /// <param name="Offset2">Second offset.</param>
- /// <param name="Count">Elements count which must be compared.</param>
- /// <returns></returns>
- public static bool IsElementsEqual<T>(T[] Arr, int Offset1, int Offset2, int Count)
- {
- if (Arr == null)
- throw new ArgumentNullException(nameof(Arr) + " is null!");
- if (Offset1 >= Arr.Length || Offset1 < 0)
- throw new ArgumentOutOfRangeException(nameof(Offset1) + " has wrong value!");
- if (Offset2 >= Arr.Length || Offset2 < 0)
- throw new ArgumentOutOfRangeException(nameof(Offset2) + " has wrong value!");
- if (Count <= 0 || Offset1 + Count > Arr.Length || Offset2 + Count > Arr.Length)
- throw new ArgumentOutOfRangeException(nameof(Count) + " has wrong value!");
- if (Offset1 == Offset2)
- return true;
- for (int i = 0; i < Count; i++)
- if (!Arr[Offset1 + i].Equals(Arr[Offset2 + i]))
- return false;
- return true;
- }
- }
- #endregion
- public class TGA : ICloneable
- {
- public TgaHeader Header = new TgaHeader();
- public TgaImgOrColMap ImageOrColorMapArea = new TgaImgOrColMap();
- public TgaDevArea DevArea = null;
- public TgaExtArea ExtArea = null;
- public TgaFooter Footer = null;
- #region TGA Creation, Loading, Saving (all are public, have reference to private metods).
- /// <summary>
- /// Create new empty <see cref="TGA"/> istance.
- /// </summary>
- public TGA()
- {
- }
- /// <summary>
- /// Create <see cref="TGA"/> instance with some params. If it must have ColorMap,
- /// check all ColorMap fields and settings after.
- /// </summary>
- /// <param name="Width">Image Width.</param>
- /// <param name="Height">Image Height.</param>
- /// <param name="PixDepth">Image Pixel Depth (bits / pixel), set ColorMap bpp after, if needed!</param>
- /// <param name="ImgType">Image Type (is RLE compressed, ColorMapped or GrayScaled).</param>
- /// <param name="AttrBits">Set numder of Attrbute bits (Alpha channel bits), default: 0, 1, 8.</param>
- /// <param name="NewFormat">Use new 2.0 TGA XFile format?</param>
- public TGA(ushort Width, ushort Height, TgaPixelDepth PixDepth = TgaPixelDepth.Bpp24,
- TgaImageType ImgType = TgaImageType.Uncompressed_TrueColor, byte AttrBits = 0, bool NewFormat = true)
- {
- if (Width <= 0 || Height <= 0 || PixDepth == TgaPixelDepth.Other)
- {
- Width = Height = 0;
- PixDepth = TgaPixelDepth.Other;
- ImgType = TgaImageType.NoImageData;
- AttrBits = 0;
- }
- else
- {
- int BytesPerPixel = (int)Math.Ceiling((double)PixDepth / 8.0);
- ImageOrColorMapArea.ImageData = new byte[Width * Height * BytesPerPixel];
- if (ImgType == TgaImageType.Uncompressed_ColorMapped || ImgType == TgaImageType.RLE_ColorMapped)
- {
- Header.ColorMapType = TgaColorMapType.ColorMap;
- Header.ColorMapSpec.FirstEntryIndex = 0;
- Header.ColorMapSpec.ColorMapEntrySize = (TgaColorMapEntrySize)Math.Ceiling((double)PixDepth / 8);
- }
- }
- Header.ImageType = ImgType;
- Header.ImageSpec.ImageWidth = Width;
- Header.ImageSpec.ImageHeight = Height;
- Header.ImageSpec.PixelDepth = PixDepth;
- Header.ImageSpec.ImageDescriptor.AlphaChannelBits = AttrBits;
- if (NewFormat)
- {
- Footer = new TgaFooter();
- ExtArea = new TgaExtArea();
- ExtArea.DateTimeStamp = new TgaDateTime(DateTime.UtcNow);
- ExtArea.AttributesType = (AttrBits > 0 ? TgaAttrType.UsefulAlpha : TgaAttrType.NoAlpha);
- }
- }
- /// <summary>
- /// Make <see cref="TGA"/> from some <see cref="TGA"/> instance.
- /// Equal to <see cref="TGA.Clone()"/> function.
- /// </summary>
- /// <param name="tga">Original <see cref="TGA"/> instance.</param>
- public TGA(TGA tga)
- {
- Header = tga.Header.Clone();
- ImageOrColorMapArea = tga.ImageOrColorMapArea.Clone();
- DevArea = tga.DevArea.Clone();
- ExtArea = tga.ExtArea.Clone();
- Footer = tga.Footer.Clone();
- }
- /// <summary>
- /// Load <see cref="TGA"/> from file.
- /// </summary>
- /// <param name="filename">Full path to TGA file.</param>
- /// <returns>Loaded <see cref="TGA"/> file.</returns>
- public TGA(string filename)
- {
- LoadFunc(filename);
- }
- /// <summary>
- /// Make <see cref="TGA"/> from bytes array.
- /// </summary>
- /// <param name="bytes">Bytes array (same like TGA File).</param>
- public TGA(byte[] bytes)
- {
- LoadFunc(bytes);
- }
- /// <summary>
- /// Make <see cref="TGA"/> from <see cref="Stream"/>.
- /// For file opening better use <see cref="FromFile(string)"/>.
- /// </summary>
- /// <param name="stream">Some stream. You can use a lot of Stream types, but Stream must support:
- /// <see cref="Stream.CanSeek"/> and <see cref="Stream.CanRead"/>.</param>
- public TGA(Stream stream)
- {
- LoadFunc(stream);
- }
- /// <summary>
- /// Make <see cref="TGA"/> from <see cref="Bitmap"/>.
- /// </summary>
- /// <param name="bmp">Input Bitmap, supported a lot of bitmaps types: 8/15/16/24/32 Bpp's.</param>
- /// <param name="UseRLE">Use RLE Compression?</param>
- /// <param name="NewFormat">Use new 2.0 TGA XFile format?</param>
- /// <param name="ColorMap2BytesEntry">Is Color Map Entry size equal 15 or 16 Bpp, else - 24 or 32.</param>
- public TGA(Bitmap bmp, bool UseRLE = false, bool NewFormat = true, bool ColorMap2BytesEntry = false)
- {
- LoadFunc(bmp, UseRLE, NewFormat, ColorMap2BytesEntry);
- }
- /// <summary>
- /// Load <see cref="TGA"/> from file.
- /// </summary>
- /// <param name="filename">Full path to TGA file.</param>
- /// <returns>Loaded <see cref="TGA"/> file.</returns>
- public static TGA FromFile(string filename)
- {
- return new TGA(filename);
- }
- /// <summary>
- /// Make <see cref="TGA"/> from bytes array.
- /// </summary>
- /// <param name="bytes">Bytes array (same like TGA File).</param>
- public static TGA FromBytes(byte[] bytes)
- {
- return new TGA(bytes);
- }
- /// <summary>
- /// Make <see cref="TGA"/> from <see cref="Stream"/>.
- /// For file opening better use <see cref="FromFile(string)"/>.
- /// </summary>
- /// <param name="stream">Some stream. You can use a lot of Stream types, but Stream must support:
- /// <see cref="Stream.CanSeek"/> and <see cref="Stream.CanRead"/>.</param>
- public static TGA FromStream(Stream stream)
- {
- return new TGA(stream);
- }
- /// <summary>
- /// Make <see cref="TGA"/> from <see cref="Bitmap"/>.
- /// </summary>
- /// <param name="bmp">Input Bitmap, supported a lot of bitmaps types: 8/15/16/24/32 Bpp's.</param>
- /// <param name="UseRLE">Use RLE Compression?</param>
- /// <param name="NewFormat">Use new 2.0 TGA XFile format?</param>
- /// <param name="ColorMap2BytesEntry">Is Color Map Entry size equal 15 or 16 Bpp, else - 24 or 32.</param>
- public static TGA FromBitmap(Bitmap bmp, bool UseRLE = false,
- bool NewFormat = true, bool ColorMap2BytesEntry = false)
- {
- return new TGA(bmp, UseRLE, NewFormat, ColorMap2BytesEntry);
- }
- /// <summary>
- /// Save <see cref="TGA"/> to file.
- /// </summary>
- /// <param name="filename">Full path to file.</param>
- /// <returns>Return "true", if all done or "false", if failed.</returns>
- public bool Save(string filename)
- {
- try
- {
- bool Result = false;
- using (FileStream Fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None))
- {
- using (MemoryStream Ms = new MemoryStream())
- {
- Result = SaveFunc(Ms);
- Ms.WriteTo(Fs);
- Fs.Flush();
- }
- }
- return Result;
- }
- catch
- {
- return false;
- }
- }
- /// <summary>
- /// Save <see cref="TGA"/> to <see cref="Stream"/>.
- /// </summary>
- /// <param name="stream">Some stream, it must support: <see cref="Stream.CanWrite"/>.</param>
- /// <returns>Return "true", if all done or "false", if failed.</returns>
- public bool Save(Stream stream)
- {
- return SaveFunc(stream);
- }
- #endregion
- /// <summary>
- /// Gets or Sets Image Width (see <see cref="Header.ImageSpec.ImageWidth"/>).
- /// </summary>
- public ushort Width
- {
- get { return Header.ImageSpec.ImageWidth; }
- set { Header.ImageSpec.ImageWidth = value; }
- }
- /// <summary>
- /// Gets or Sets Image Height (see <see cref="Header.ImageSpec.ImageHeight"/>).
- /// </summary>
- public ushort Height
- {
- get { return Header.ImageSpec.ImageHeight; }
- set { Header.ImageSpec.ImageHeight = value; }
- }
- /// <summary>
- /// Gets or Sets <see cref="TGA"/> image Size.
- /// </summary>
- public Size Size
- {
- get { return new Size(Header.ImageSpec.ImageWidth, Header.ImageSpec.ImageHeight); }
- set
- {
- Header.ImageSpec.ImageWidth = (ushort)value.Width;
- Header.ImageSpec.ImageHeight = (ushort)value.Height;
- }
- }
- /// <summary>
- /// Make full independed copy of <see cref="TGA"/>.
- /// </summary>
- /// <returns>Full independed copy of <see cref="TGA"/>.</returns>
- public TGA Clone()
- {
- return new TGA(this);
- }
- object ICloneable.Clone()
- {
- return Clone();
- }
- /// <summary>
- /// Flip <see cref="TGA"/> directions, for more info see <see cref="TgaImgOrigin"/>.
- /// </summary>
- /// <param name="Horizontal">Flip horizontal.</param>
- /// <param name="Vertical">Flip vertical.</param>
- public void Flip(bool Horizontal = false, bool Vertical = false)
- {
- int NewOrigin = (int)Header.ImageSpec.ImageDescriptor.ImageOrigin;
- NewOrigin = NewOrigin ^ ((Vertical ? 0x20 : 0) | (Horizontal ? 0x10 : 0));
- Header.ImageSpec.ImageDescriptor.ImageOrigin = (TgaImgOrigin)NewOrigin;
- }
- /// <summary>
- /// Get information from TGA image.
- /// </summary>
- /// <returns>MultiLine string with info fields (one per line).</returns>
- public string GetInfo()
- {
- StringBuilder SB = new StringBuilder();
- SB.AppendLine("Header:");
- SB.AppendLine("\tID Length = " + Header.IDLength);
- SB.AppendLine("\tImage Type = " + Header.ImageType);
- SB.AppendLine("\tHeader -> ImageSpec:");
- SB.AppendLine("\t\tImage Width = " + Header.ImageSpec.ImageWidth);
- SB.AppendLine("\t\tImage Height = " + Header.ImageSpec.ImageHeight);
- SB.AppendLine("\t\tPixel Depth = " + Header.ImageSpec.PixelDepth);
- SB.AppendLine("\t\tImage Descriptor (AsByte) = " + Header.ImageSpec.ImageDescriptor.ToByte());
- SB.AppendLine("\t\tImage Descriptor -> AttributeBits = " + Header.ImageSpec.ImageDescriptor.AlphaChannelBits);
- SB.AppendLine("\t\tImage Descriptor -> ImageOrigin = " + Header.ImageSpec.ImageDescriptor.ImageOrigin);
- SB.AppendLine("\t\tX_Origin = " + Header.ImageSpec.X_Origin);
- SB.AppendLine("\t\tY_Origin = " + Header.ImageSpec.Y_Origin);
- SB.AppendLine("\tColorMap Type = " + Header.ColorMapType);
- SB.AppendLine("\tHeader -> ColorMapSpec:");
- SB.AppendLine("\t\tColorMap Entry Size = " + Header.ColorMapSpec.ColorMapEntrySize);
- SB.AppendLine("\t\tColorMap Length = " + Header.ColorMapSpec.ColorMapLength);
- SB.AppendLine("\t\tFirstEntry Index = " + Header.ColorMapSpec.FirstEntryIndex);
- SB.AppendLine("\nImage / Color Map Area:");
- if (Header.IDLength > 0 && ImageOrColorMapArea.ImageID != null)
- SB.AppendLine("\tImage ID = \"" + ImageOrColorMapArea.ImageID.GetString() + "\"");
- else
- SB.AppendLine("\tImage ID = null");
- if (ImageOrColorMapArea.ImageData != null)
- SB.AppendLine("\tImage Data Length = " + ImageOrColorMapArea.ImageData.Length);
- else
- SB.AppendLine("\tImage Data = null");
- if (ImageOrColorMapArea.ColorMapData != null)
- SB.AppendLine("\tColorMap Data Length = " + ImageOrColorMapArea.ColorMapData.Length);
- else
- SB.AppendLine("\tColorMap Data = null");
- SB.AppendLine("\nDevelopers Area:");
- if (DevArea != null)
- SB.AppendLine("\tCount = " + DevArea.Count);
- else
- SB.AppendLine("\tDevArea = null");
- SB.AppendLine("\nExtension Area:");
- if (ExtArea != null)
- {
- SB.AppendLine("\tExtension Size = " + ExtArea.ExtensionSize);
- SB.AppendLine("\tAuthor Name = \"" + ExtArea.AuthorName.GetString() + "\"");
- SB.AppendLine("\tAuthor Comments = \"" + ExtArea.AuthorComments.GetString() + "\"");
- SB.AppendLine("\tDate / Time Stamp = " + ExtArea.DateTimeStamp);
- SB.AppendLine("\tJob Name / ID = \"" + ExtArea.JobNameOrID.GetString() + "\"");
- SB.AppendLine("\tJob Time = " + ExtArea.JobTime);
- SB.AppendLine("\tSoftware ID = \"" + ExtArea.SoftwareID.GetString() + "\"");
- SB.AppendLine("\tSoftware Version = \"" + ExtArea.SoftVersion + "\"");
- SB.AppendLine("\tKey Color = " + ExtArea.KeyColor);
- SB.AppendLine("\tPixel Aspect Ratio = " + ExtArea.PixelAspectRatio);
- SB.AppendLine("\tGamma Value = " + ExtArea.GammaValue);
- SB.AppendLine("\tColor Correction Table Offset = " + ExtArea.ColorCorrectionTableOffset);
- SB.AppendLine("\tPostage Stamp Offset = " + ExtArea.PostageStampOffset);
- SB.AppendLine("\tScan Line Offset = " + ExtArea.ScanLineOffset);
- SB.AppendLine("\tAttributes Type = " + ExtArea.AttributesType);
- if (ExtArea.ScanLineTable != null)
- SB.AppendLine("\tScan Line Table = " + ExtArea.ScanLineTable.Length);
- else
- SB.AppendLine("\tScan Line Table = null");
- if (ExtArea.PostageStampImage != null)
- SB.AppendLine("\tPostage Stamp Image: " + ExtArea.PostageStampImage.ToString());
- else
- SB.AppendLine("\tPostage Stamp Image = null");
- SB.AppendLine("\tColor Correction Table = " + (ExtArea.ColorCorrectionTable != null));
- }
- else
- SB.AppendLine("\tExtArea = null");
- SB.AppendLine("\nFooter:");
- if (Footer != null)
- {
- SB.AppendLine("\tExtension Area Offset = " + Footer.ExtensionAreaOffset);
- SB.AppendLine("\tDeveloper Directory Offset = " + Footer.DeveloperDirectoryOffset);
- SB.AppendLine("\tSignature (Full) = \"" + Footer.Signature.ToString() +
- Footer.ReservedCharacter.ToString() + Footer.BinaryZeroStringTerminator.ToString() + "\"");
- }
- else
- SB.AppendLine("\tFooter = null");
- return SB.ToString();
- }
- /// <summary>
- /// Check and update all fields with data length and offsets.
- /// </summary>
- /// <returns>Return "true", if all OK or "false", if checking failed.</returns>
- public bool CheckAndUpdateOffsets(out string ErrorStr)
- {
- ErrorStr = String.Empty;
- if (Header == null)
- {
- ErrorStr = "Header = null";
- return false;
- }
- if (ImageOrColorMapArea == null)
- {
- ErrorStr = "ImageOrColorMapArea = null";
- return false;
- }
- uint Offset = TgaHeader.Size; // Virtual Offset
- #region Header
- if (ImageOrColorMapArea.ImageID != null)
- {
- int StrMaxLen = 255;
- if (ImageOrColorMapArea.ImageID.UseEndingChar)
- StrMaxLen--;
- Header.IDLength = (byte)Math.Min(ImageOrColorMapArea.ImageID.OriginalString.Length, StrMaxLen);
- ImageOrColorMapArea.ImageID.Length = Header.IDLength;
- Offset += Header.IDLength;
- }
- else
- Header.IDLength = 0;
- #endregion
- #region ColorMap
- if (Header.ColorMapType != TgaColorMapType.NoColorMap)
- {
- if (Header.ColorMapSpec == null)
- {
- ErrorStr = "Header.ColorMapSpec = null";
- return false;
- }
- if (Header.ColorMapSpec.ColorMapLength == 0)
- {
- ErrorStr = "Header.ColorMapSpec.ColorMapLength = 0";
- return false;
- }
- if (ImageOrColorMapArea.ColorMapData == null)
- {
- ErrorStr = "ImageOrColorMapArea.ColorMapData = null";
- return false;
- }
- int CmBytesPerPixel = (int)Math.Ceiling((double)Header.ColorMapSpec.ColorMapEntrySize / 8.0);
- int LenBytes = Header.ColorMapSpec.ColorMapLength * CmBytesPerPixel;
- if (LenBytes != ImageOrColorMapArea.ColorMapData.Length)
- {
- ErrorStr = "ImageOrColorMapArea.ColorMapData.Length has wrong size!";
- return false;
- }
- Offset += (uint)ImageOrColorMapArea.ColorMapData.Length;
- }
- #endregion
- #region Image Data
- int BytesPerPixel = 0;
- if (Header.ImageType != TgaImageType.NoImageData)
- {
- if (Header.ImageSpec == null)
- {
- ErrorStr = "Header.ImageSpec = null";
- return false;
- }
- if (Header.ImageSpec.ImageWidth == 0 || Header.ImageSpec.ImageHeight == 0)
- {
- ErrorStr = "Header.ImageSpec.ImageWidth = 0 or Header.ImageSpec.ImageHeight = 0";
- return false;
- }
- if (ImageOrColorMapArea.ImageData == null)
- {
- ErrorStr = "ImageOrColorMapArea.ImageData = null";
- return false;
- }
- BytesPerPixel = (int)Math.Ceiling((double)Header.ImageSpec.PixelDepth / 8.0);
- if (Width * Height * BytesPerPixel != ImageOrColorMapArea.ImageData.Length)
- {
- ErrorStr = "ImageOrColorMapArea.ImageData.Length has wrong size!";
- return false;
- }
- if (Header.ImageType >= TgaImageType.RLE_ColorMapped &&
- Header.ImageType <= TgaImageType.RLE_BlackWhite)
- {
- byte[] RLE = RLE_Encode(ImageOrColorMapArea.ImageData, Width, Height);
- if (RLE == null)
- {
- ErrorStr = "RLE Compressing error! Check Image Data size.";
- return false;
- }
- Offset += (uint)RLE.Length;
- RLE = null;
- }
- else
- Offset += (uint)ImageOrColorMapArea.ImageData.Length;
- }
- #endregion
- #region Footer, DevArea, ExtArea
- if (Footer != null)
- {
- #region DevArea
- if (DevArea != null)
- {
- int DevAreaCount = DevArea.Count;
- for (int i = 0; i < DevAreaCount; i++)
- if (DevArea[i] == null || DevArea[i].FieldSize <= 0) //Del Empty Entries
- {
- DevArea.Entries.RemoveAt(i);
- DevAreaCount--;
- i--;
- }
- if (DevArea.Count <= 0)
- Footer.DeveloperDirectoryOffset = 0;
- if (DevArea.Count > 2)
- {
- DevArea.Entries.Sort((a, b) => { return a.Tag.CompareTo(b.Tag); });
- for (int i = 0; i < DevArea.Count - 1; i++)
- if (DevArea[i].Tag == DevArea[i + 1].Tag)
- {
- ErrorStr = "DevArea Enties has same Tags!";
- return false;
- }
- }
- for (int i = 0; i < DevArea.Count; i++)
- {
- DevArea[i].Offset = Offset;
- Offset += (uint)DevArea[i].FieldSize;
- }
- Footer.DeveloperDirectoryOffset = Offset;
- Offset += (uint)(DevArea.Count * 10 + 2);
- }
- else
- Footer.DeveloperDirectoryOffset = 0;
- #endregion
- #region ExtArea
- if (ExtArea != null)
- {
- ExtArea.ExtensionSize = TgaExtArea.MinSize;
- if (ExtArea.OtherDataInExtensionArea != null)
- ExtArea.ExtensionSize += (ushort)ExtArea.OtherDataInExtensionArea.Length;
- ExtArea.DateTimeStamp = new TgaDateTime(DateTime.UtcNow);
- Footer.ExtensionAreaOffset = Offset;
- Offset += ExtArea.ExtensionSize;
- #region ScanLineTable
- if (ExtArea.ScanLineTable == null)
- ExtArea.ScanLineOffset = 0;
- else
- {
- if (ExtArea.ScanLineTable.Length != Height)
- {
- ErrorStr = "ExtArea.ScanLineTable.Length != Height";
- return false;
- }
- ExtArea.ScanLineOffset = Offset;
- Offset += (uint)(ExtArea.ScanLineTable.Length * 4);
- }
- #endregion
- #region PostageStampImage
- if (ExtArea.PostageStampImage == null)
- ExtArea.PostageStampOffset = 0;
- else
- {
- if (ExtArea.PostageStampImage.Width == 0 || ExtArea.PostageStampImage.Height == 0)
- {
- ErrorStr = "ExtArea.PostageStampImage Width or Height is equal 0!";
- return false;
- }
- if (ExtArea.PostageStampImage.Data == null)
- {
- ErrorStr = "ExtArea.PostageStampImage.Data == null";
- return false;
- }
- int PImgSB = ExtArea.PostageStampImage.Width * ExtArea.PostageStampImage.Height * BytesPerPixel;
- if (Header.ImageType != TgaImageType.NoImageData &&
- ExtArea.PostageStampImage.Data.Length != PImgSB)
- {
- ErrorStr = "ExtArea.PostageStampImage.Data.Length is wrong!";
- return false;
- }
- ExtArea.PostageStampOffset = Offset;
- Offset += (uint)(ExtArea.PostageStampImage.Data.Length);
- }
- #endregion
- #region ColorCorrectionTable
- if (ExtArea.ColorCorrectionTable == null)
- ExtArea.ColorCorrectionTableOffset = 0;
- else
- {
- if (ExtArea.ColorCorrectionTable.Length != 1024)
- {
- ErrorStr = "ExtArea.ColorCorrectionTable.Length != 256 * 4";
- return false;
- }
- ExtArea.ColorCorrectionTableOffset = Offset;
- Offset += (uint)(ExtArea.ColorCorrectionTable.Length * 2);
- }
- #endregion
- }
- else
- Footer.ExtensionAreaOffset = 0;
- #endregion
- #region Footer
- if (Footer.ToBytes().Length != TgaFooter.Size)
- {
- ErrorStr = "Footer.Length is wrong!";
- return false;
- }
- Offset += TgaFooter.Size;
- #endregion
- }
- #endregion
- return true;
- }
- #region Convert
- /// <summary>
- /// Convert <see cref="TGA"/> to <see cref="Bitmap"/>.
- /// </summary>
- /// <param name="ForceUseAlpha">Force use alpha channel.</param>
- /// <returns>Bitmap or null, on error.</returns>
- public Bitmap ToBitmap(bool ForceUseAlpha = false)
- {
- return ToBitmapFunc(ForceUseAlpha, false);
- }
- /// <summary>
- /// Convert <see cref="TGA"/> to bytes array.
- /// </summary>
- /// <returns>Bytes array, (equal to saved file, but in memory) or null (on error).</returns>
- public byte[] ToBytes()
- {
- try
- {
- byte[] Bytes;
- using (MemoryStream ms = new MemoryStream())
- {
- Save(ms);
- Bytes = ms.ToArray();
- ms.Flush();
- }
- return Bytes;
- }
- catch
- {
- return null;
- }
- }
- /// <summary>
- /// Convert TGA Image to new XFile format (v2.0).
- /// </summary>
- public void ToNewFormat()
- {
- if (Footer == null)
- Footer = new TgaFooter();
- if (ExtArea == null)
- {
- ExtArea = new TgaExtArea();
- ExtArea.DateTimeStamp = new TgaDateTime(DateTime.UtcNow);
- if (Header.ImageSpec.ImageDescriptor.AlphaChannelBits > 0)
- ExtArea.AttributesType = TgaAttrType.UsefulAlpha;
- else
- ExtArea.AttributesType = TgaAttrType.NoAlpha;
- }
- }
- #endregion
- #region Private functions
- bool LoadFunc(string filename)
- {
- if (!File.Exists(filename))
- throw new FileNotFoundException("File: \"" + filename + "\" not found!");
- try
- {
- using (FileStream FS = new FileStream(filename, FileMode.Open, FileAccess.Read))
- return LoadFunc(FS);
- }
- catch
- {
- return false;
- }
- }
- bool LoadFunc(byte[] bytes)
- {
- if (bytes == null)
- throw new ArgumentNullException();
- try
- {
- using (MemoryStream FS = new MemoryStream(bytes, false))
- return LoadFunc(FS);
- }
- catch
- {
- return false;
- }
- }
- bool LoadFunc(Stream stream)
- {
- if (stream == null)
- throw new ArgumentNullException();
- if (!(stream.CanRead && stream.CanSeek))
- throw new FileLoadException("Stream reading or seeking is not avaiable!");
- try
- {
- stream.Seek(0, SeekOrigin.Begin);
- BinaryReader Br = new BinaryReader(stream);
- Header = new TgaHeader(Br.ReadBytes(TgaHeader.Size));
- if (Header.IDLength > 0)
- ImageOrColorMapArea.ImageID = new TgaString(Br.ReadBytes(Header.IDLength));
- if (Header.ColorMapSpec.ColorMapLength > 0)
- {
- int CmBytesPerPixel = (int)Math.Ceiling((double)Header.ColorMapSpec.ColorMapEntrySize / 8.0);
- int LenBytes = Header.ColorMapSpec.ColorMapLength * CmBytesPerPixel;
- ImageOrColorMapArea.ColorMapData = Br.ReadBytes(LenBytes);
- }
- #region Read Image Data
- int BytesPerPixel = (int)Math.Ceiling((double)Header.ImageSpec.PixelDepth / 8.0);
- if (Header.ImageType != TgaImageType.NoImageData)
- {
- int ImageDataSize = Width * Height * BytesPerPixel;
- switch (Header.ImageType)
- {
- case TgaImageType.RLE_ColorMapped:
- case TgaImageType.RLE_TrueColor:
- case TgaImageType.RLE_BlackWhite:
- int DataOffset = 0;
- byte PacketInfo;
- int PacketCount;
- byte[] RLE_Bytes, RLE_Part;
- ImageOrColorMapArea.ImageData = new byte[ImageDataSize];
- do
- {
- PacketInfo = Br.ReadByte(); //1 type bit and 7 count bits. Len = Count + 1.
- PacketCount = (PacketInfo & 127) + 1;
- if (PacketInfo >= 128) // bit7 = 1, RLE
- {
- RLE_Bytes = new byte[PacketCount * BytesPerPixel];
- RLE_Part = Br.ReadBytes(BytesPerPixel);
- for (int i = 0; i < RLE_Bytes.Length; i++)
- RLE_Bytes[i] = RLE_Part[i % BytesPerPixel];
- }
- else // RAW format
- RLE_Bytes = Br.ReadBytes(PacketCount * BytesPerPixel);
- Buffer.BlockCopy(RLE_Bytes, 0, ImageOrColorMapArea.ImageData, DataOffset, RLE_Bytes.Length);
- DataOffset += RLE_Bytes.Length;
- }
- while (DataOffset < ImageDataSize);
- RLE_Bytes = null;
- break;
- case TgaImageType.Uncompressed_ColorMapped:
- case TgaImageType.Uncompressed_TrueColor:
- case TgaImageType.Uncompressed_BlackWhite:
- ImageOrColorMapArea.ImageData = Br.ReadBytes(ImageDataSize);
- break;
- }
- }
- #endregion
- #region Try parse Footer
- stream.Seek(-TgaFooter.Size, SeekOrigin.End);
- uint FooterOffset = (uint)stream.Position;
- TgaFooter MbFooter = new TgaFooter(Br.ReadBytes(TgaFooter.Size));
- if (MbFooter.IsFooterCorrect)
- {
- Footer = MbFooter;
- uint DevDirOffset = Footer.DeveloperDirectoryOffset;
- uint ExtAreaOffset = Footer.ExtensionAreaOffset;
- #region If Dev Area exist, read it.
- if (DevDirOffset != 0)
- {
- stream.Seek(DevDirOffset, SeekOrigin.Begin);
- DevArea = new TgaDevArea();
- uint NumberOfTags = Br.ReadUInt16();
- ushort[] Tags = new ushort[NumberOfTags];
- uint[] TagOffsets = new uint[NumberOfTags];
- uint[] TagSizes = new uint[NumberOfTags];
- for (int i = 0; i < NumberOfTags; i++)
- {
- Tags[i] = Br.ReadUInt16();
- TagOffsets[i] = Br.ReadUInt32();
- TagSizes[i] = Br.ReadUInt32();
- }
- for (int i = 0; i < NumberOfTags; i++)
- {
- stream.Seek(TagOffsets[i], SeekOrigin.Begin);
- var Ent = new TgaDevEntry(Tags[i], TagOffsets[i], Br.ReadBytes((int)TagSizes[i]));
- DevArea.Entries.Add(Ent);
- }
- Tags = null;
- TagOffsets = null;
- TagSizes = null;
- }
- #endregion
- #region If Ext Area exist, read it.
- if (ExtAreaOffset != 0)
- {
- stream.Seek(ExtAreaOffset, SeekOrigin.Begin);
- ushort ExtAreaSize = Math.Max((ushort)TgaExtArea.MinSize, Br.ReadUInt16());
- stream.Seek(ExtAreaOffset, SeekOrigin.Begin);
- ExtArea = new TgaExtArea(Br.ReadBytes(ExtAreaSize));
- if (ExtArea.ScanLineOffset > 0)
- {
- stream.Seek(ExtArea.ScanLineOffset, SeekOrigin.Begin);
- ExtArea.ScanLineTable = new uint[Height];
- for (int i = 0; i < ExtArea.ScanLineTable.Length; i++)
- ExtArea.ScanLineTable[i] = Br.ReadUInt32();
- }
- if (ExtArea.PostageStampOffset > 0)
- {
- stream.Seek(ExtArea.PostageStampOffset, SeekOrigin.Begin);
- byte W = Br.ReadByte();
- byte H = Br.ReadByte();
- int ImgDataSize = W * H * BytesPerPixel;
- if (ImgDataSize > 0)
- ExtArea.PostageStampImage = new TgaPostageStampImage(W, H, Br.ReadBytes(ImgDataSize));
- }
- if (ExtArea.ColorCorrectionTableOffset > 0)
- {
- stream.Seek(ExtArea.ColorCorrectionTableOffset, SeekOrigin.Begin);
- ExtArea.ColorCorrectionTable = new ushort[256 * 4];
- for (int i = 0; i < ExtArea.ColorCorrectionTable.Length; i++)
- ExtArea.ColorCorrectionTable[i] = Br.ReadUInt16();
- }
- }
- #endregion
- }
- #endregion
- Br.Close();
- return true;
- }
- catch
- {
- return false;
- }
- }
- bool LoadFunc(Bitmap bmp, bool UseRLE = false, bool NewFormat = true, bool ColorMap2BytesEntry = false)
- {
- if (bmp == null)
- throw new ArgumentNullException();
- try
- {
- Header.ImageSpec.ImageWidth = (ushort)bmp.Width;
- Header.ImageSpec.ImageHeight = (ushort)bmp.Height;
- Header.ImageSpec.ImageDescriptor.ImageOrigin = TgaImgOrigin.TopLeft;
- switch (bmp.PixelFormat)
- {
- case PixelFormat.Indexed:
- case PixelFormat.Gdi:
- case PixelFormat.Alpha:
- case PixelFormat.Undefined:
- case PixelFormat.PAlpha:
- case PixelFormat.Extended:
- case PixelFormat.Max:
- case PixelFormat.Canonical:
- case PixelFormat.Format16bppRgb565:
- default:
- throw new FormatException(nameof(PixelFormat) + " is not supported!");
- case PixelFormat.Format1bppIndexed:
- case PixelFormat.Format4bppIndexed:
- case PixelFormat.Format8bppIndexed:
- case PixelFormat.Format16bppGrayScale:
- case PixelFormat.Format16bppRgb555:
- case PixelFormat.Format16bppArgb1555:
- case PixelFormat.Format24bppRgb:
- case PixelFormat.Format32bppRgb:
- case PixelFormat.Format32bppArgb:
- case PixelFormat.Format32bppPArgb:
- case PixelFormat.Format48bppRgb:
- case PixelFormat.Format64bppArgb:
- case PixelFormat.Format64bppPArgb:
- int bpp = Math.Max(8, Image.GetPixelFormatSize(bmp.PixelFormat));
- int BytesPP = bpp / 8;
- if (bmp.PixelFormat == PixelFormat.Format16bppRgb555)
- bpp = 15;
- bool IsAlpha = Image.IsAlphaPixelFormat(bmp.PixelFormat);
- bool IsPreAlpha = IsAlpha && bmp.PixelFormat.ToString().EndsWith("PArgb");
- bool IsColorMapped = bmp.PixelFormat.ToString().EndsWith("Indexed");
- Header.ImageSpec.PixelDepth = (TgaPixelDepth)(BytesPP * 8);
- if (IsAlpha)
- {
- Header.ImageSpec.ImageDescriptor.AlphaChannelBits = (byte)(BytesPP * 2);
- if (bmp.PixelFormat == PixelFormat.Format16bppArgb1555)
- Header.ImageSpec.ImageDescriptor.AlphaChannelBits = 1;
- }
- #region ColorMap
- bool IsGrayImage = (bmp.PixelFormat == PixelFormat.Format16bppGrayScale | IsColorMapped);
- if (IsColorMapped && bmp.Palette != null)
- {
- Color[] Colors = bmp.Palette.Entries;
- #region Analyze ColorMapType
- int AlphaSum = 0;
- bool ColorMapUseAlpha = false;
- for (int i = 0; i < Colors.Length; i++)
- {
- IsGrayImage &= (Colors[i].R == Colors[i].G && Colors[i].G == Colors[i].B);
- ColorMapUseAlpha |= (Colors[i].A < 248);
- AlphaSum |= Colors[i].A;
- }
- ColorMapUseAlpha &= (AlphaSum > 0);
- int CMapBpp = (ColorMap2BytesEntry ? 15 : 24) + (ColorMapUseAlpha ? (ColorMap2BytesEntry ? 1 : 8) : 0);
- int CMBytesPP = (int)Math.Ceiling(CMapBpp / 8.0);
- #endregion
- Header.ColorMapSpec.ColorMapLength = Math.Min((ushort)Colors.Length, ushort.MaxValue);
- Header.ColorMapSpec.ColorMapEntrySize = (TgaColorMapEntrySize)CMapBpp;
- ImageOrColorMapArea.ColorMapData = new byte[Header.ColorMapSpec.ColorMapLength * CMBytesPP];
- byte[] CMapEntry = new byte[CMBytesPP];
- const float To5Bit = 32f / 256f; // Scale value from 8 to 5 bits.
- for (int i = 0; i < Colors.Length; i++)
- {
- switch (Header.ColorMapSpec.ColorMapEntrySize)
- {
- case TgaColorMapEntrySize.A1R5G5B5:
- case TgaColorMapEntrySize.X1R5G5B5:
- int R = (int)(Colors[i].R * To5Bit);
- int G = (int)(Colors[i].G * To5Bit) << 5;
- int B = (int)(Colors[i].B * To5Bit) << 10;
- int A = 0;
- if (Header.ColorMapSpec.ColorMapEntrySize == TgaColorMapEntrySize.A1R5G5B5)
- A = ((Colors[i].A & 0x80) << 15);
- CMapEntry = BitConverter.GetBytes(A | R | G | B);
- break;
- case TgaColorMapEntrySize.R8G8B8:
- CMapEntry[0] = Colors[i].B;
- CMapEntry[1] = Colors[i].G;
- CMapEntry[2] = Colors[i].R;
- break;
- case TgaColorMapEntrySize.A8R8G8B8:
- CMapEntry[0] = Colors[i].B;
- CMapEntry[1] = Colors[i].G;
- CMapEntry[2] = Colors[i].R;
- CMapEntry[3] = Colors[i].A;
- break;
- case TgaColorMapEntrySize.Other:
- default:
- break;
- }
- Buffer.BlockCopy(CMapEntry, 0, ImageOrColorMapArea.ColorMapData, i * CMBytesPP, CMBytesPP);
- }
- }
- #endregion
- #region ImageType
- if (UseRLE)
- {
- if (IsGrayImage)
- Header.ImageType = TgaImageType.RLE_BlackWhite;
- else if (IsColorMapped)
- Header.ImageType = TgaImageType.RLE_ColorMapped;
- else
- Header.ImageType = TgaImageType.RLE_TrueColor;
- }
- else
- {
- if (IsGrayImage)
- Header.ImageType = TgaImageType.Uncompressed_BlackWhite;
- else if (IsColorMapped)
- Header.ImageType = TgaImageType.Uncompressed_ColorMapped;
- else
- Header.ImageType = TgaImageType.Uncompressed_TrueColor;
- }
- Header.ColorMapType = (IsColorMapped ? TgaColorMapType.ColorMap : TgaColorMapType.NoColorMap);
- #endregion
- #region NewFormat
- if (NewFormat)
- {
- Footer = new TgaFooter();
- ExtArea = new TgaExtArea();
- ExtArea.DateTimeStamp = new TgaDateTime(DateTime.UtcNow);
- if (IsAlpha)
- {
- ExtArea.AttributesType = TgaAttrType.UsefulAlpha;
- if (IsPreAlpha)
- ExtArea.AttributesType = TgaAttrType.PreMultipliedAlpha;
- }
- else
- {
- ExtArea.AttributesType = TgaAttrType.NoAlpha;
- if (Header.ImageSpec.ImageDescriptor.AlphaChannelBits > 0)
- ExtArea.AttributesType = TgaAttrType.UndefinedAlphaButShouldBeRetained;
- }
- }
- #endregion
- #region Bitmap width is aligned by 32 bits = 4 bytes! Delete it.
- int StrideBytes = bmp.Width * BytesPP;
- int PaddingBytes = (int)Math.Ceiling(StrideBytes / 4.0) * 4 - StrideBytes;
- byte[] ImageData = new byte[(StrideBytes + PaddingBytes) * bmp.Height];
- Rectangle Re = new Rectangle(0, 0, bmp.Width, bmp.Height);
- BitmapData BmpData = bmp.LockBits(Re, ImageLockMode.ReadOnly, bmp.PixelFormat);
- Marshal.Copy(BmpData.Scan0, ImageData, 0, ImageData.Length);
- bmp.UnlockBits(BmpData);
- BmpData = null;
- if (PaddingBytes > 0) //Need delete bytes align
- {
- ImageOrColorMapArea.ImageData = new byte[StrideBytes * bmp.Height];
- for (int i = 0; i < bmp.Height; i++)
- Buffer.BlockCopy(ImageData, i * (StrideBytes + PaddingBytes),
- ImageOrColorMapArea.ImageData, i * StrideBytes, StrideBytes);
- }
- else
- ImageOrColorMapArea.ImageData = ImageData;
- ImageData = null;
- // Not official supported, but works (tested on 16bpp GrayScale test images)!
- if (bmp.PixelFormat == PixelFormat.Format16bppGrayScale)
- {
- for (long i = 0; i < ImageOrColorMapArea.ImageData.Length; i++)
- ImageOrColorMapArea.ImageData[i] ^= byte.MaxValue;
- }
- #endregion
- break;
- }
- return true;
- }
- catch
- {
- return false;
- }
- }
- bool SaveFunc(Stream stream)
- {
- try
- {
- if (stream == null)
- throw new ArgumentNullException();
- if (!(stream.CanWrite && stream.CanSeek))
- throw new FileLoadException("Stream writing or seeking is not avaiable!");
- string CheckResult;
- if (!CheckAndUpdateOffsets(out CheckResult))
- return false;
- BinaryWriter Bw = new BinaryWriter(stream);
- Bw.Write(Header.ToBytes());
- if (ImageOrColorMapArea.ImageID != null)
- Bw.Write(ImageOrColorMapArea.ImageID.ToBytes());
- if (Header.ColorMapType != TgaColorMapType.NoColorMap)
- Bw.Write(ImageOrColorMapArea.ColorMapData);
- #region ImageData
- if (Header.ImageType != TgaImageType.NoImageData)
- {
- if (Header.ImageType >= TgaImageType.RLE_ColorMapped &&
- Header.ImageType <= TgaImageType.RLE_BlackWhite)
- Bw.Write(RLE_Encode(ImageOrColorMapArea.ImageData, Width, Height));
- else
- Bw.Write(ImageOrColorMapArea.ImageData);
- }
- #endregion
- #region Footer
- if (Footer != null)
- {
- #region DevArea
- if (DevArea != null)
- {
- for (int i = 0; i < DevArea.Count; i++)
- Bw.Write(DevArea[i].Data);
- Bw.Write((ushort)DevArea.Count);
- for (int i = 0; i < DevArea.Count; i++)
- {
- Bw.Write(DevArea[i].Tag);
- Bw.Write(DevArea[i].Offset);
- Bw.Write(DevArea[i].FieldSize);
- }
- }
- #endregion
- #region ExtArea
- if (ExtArea != null)
- {
- Bw.Write(ExtArea.ToBytes());
- if (ExtArea.ScanLineTable != null)
- for (int i = 0; i < ExtArea.ScanLineTable.Length; i++)
- Bw.Write(ExtArea.ScanLineTable[i]);
- if (ExtArea.PostageStampImage != null)
- Bw.Write(ExtArea.PostageStampImage.ToBytes());
- if (ExtArea.ColorCorrectionTable != null)
- for (int i = 0; i < ExtArea.ColorCorrectionTable.Length; i++)
- Bw.Write(ExtArea.ColorCorrectionTable[i]);
- }
- #endregion
- Bw.Write(Footer.ToBytes());
- }
- #endregion
- Bw.Flush();
- stream.Flush();
- return true;
- }
- catch
- {
- return false;
- }
- }
- /// <summary>
- /// Encode image with RLE compression (used RLE per line)!
- /// </summary>
- /// <param name="ImageData">Image data, bytes array with size = Width * Height * BytesPerPixel.</param>
- /// <param name="Width">Image Width, must be > 0.</param>
- /// <param name="Height">Image Height, must be > 0.</param>
- /// <returns>Bytes array with RLE compressed image data.</returns>
- byte[] RLE_Encode(byte[] ImageData, int Width, int Height)
- {
- if (ImageData == null)
- throw new ArgumentNullException(nameof(ImageData) + "in null!");
- if (Width <= 0 || Height <= 0)
- throw new ArgumentOutOfRangeException(nameof(Width) + " and " + nameof(Height) + " must be > 0!");
- int Bpp = ImageData.Length / Width / Height; // Bytes per pixel
- int ScanLineSize = Width * Bpp;
- if (ScanLineSize * Height != ImageData.Length)
- throw new ArgumentOutOfRangeException("ImageData has wrong Length!");
- try
- {
- int Count = 0;
- int Pos = 0;
- bool IsRLE = false;
- List<byte> Encoded = new List<byte>();
- byte[] RowData = new byte[ScanLineSize];
- for (int y = 0; y < Height; y++)
- {
- Pos = 0;
- Buffer.BlockCopy(ImageData, y * ScanLineSize, RowData, 0, ScanLineSize);
- while (Pos < ScanLineSize)
- {
- if (Pos >= ScanLineSize - Bpp)
- {
- Encoded.Add(0);
- Encoded.AddRange(BitConverterExt.GetElements(RowData, Pos, Bpp));
- Pos += Bpp;
- break;
- }
- Count = 0; //1
- IsRLE = BitConverterExt.IsElementsEqual(RowData, Pos, Pos + Bpp, Bpp);
- for (int i = Pos + Bpp; i < Math.Min(Pos + 128 * Bpp, ScanLineSize) - Bpp; i += Bpp)
- {
- if (IsRLE ^ BitConverterExt.IsElementsEqual(RowData, (IsRLE ? Pos : i), i + Bpp, Bpp))
- {
- //Count--;
- break;
- }
- else
- Count++;
- }
- int CountBpp = (Count + 1) * Bpp;
- Encoded.Add((byte)(IsRLE ? Count | 128 : Count));
- Encoded.AddRange(BitConverterExt.GetElements(RowData, Pos, (IsRLE ? Bpp : CountBpp)));
- Pos += CountBpp;
- }
- }
- return Encoded.ToArray();
- }
- catch
- {
- return null;
- }
- }
- /// <summary>
- /// Convert <see cref="TGA"/> to <see cref="Bitmap"/>.
- /// </summary>
- /// <param name="ForceUseAlpha">Force use alpha channel.</param>
- /// <param name="PostageStampImage">Get Postage Stamp Image (Thumb) or get main image?</param>
- /// <returns>Bitmap or null, on error.</returns>
- Bitmap ToBitmapFunc(bool ForceUseAlpha = false, bool PostageStampImage = false)
- {
- try
- {
- #region UseAlpha?
- bool UseAlpha = true;
- if (ExtArea != null)
- {
- switch (ExtArea.AttributesType)
- {
- case TgaAttrType.NoAlpha:
- case TgaAttrType.UndefinedAlphaCanBeIgnored:
- case TgaAttrType.UndefinedAlphaButShouldBeRetained:
- UseAlpha = false;
- break;
- case TgaAttrType.UsefulAlpha:
- case TgaAttrType.PreMultipliedAlpha:
- default:
- break;
- }
- }
- UseAlpha = (Header.ImageSpec.ImageDescriptor.AlphaChannelBits > 0 && UseAlpha) | ForceUseAlpha;
- #endregion
- #region IsGrayImage
- bool IsGrayImage = Header.ImageType == TgaImageType.RLE_BlackWhite ||
- Header.ImageType == TgaImageType.Uncompressed_BlackWhite;
- #endregion
- #region Get PixelFormat
- PixelFormat PixFormat = PixelFormat.Format24bppRgb;
- switch (Header.ImageSpec.PixelDepth)
- {
- case TgaPixelDepth.Bpp8:
- PixFormat = PixelFormat.Format8bppIndexed;
- break;
- case TgaPixelDepth.Bpp16:
- if (IsGrayImage)
- PixFormat = PixelFormat.Format16bppGrayScale;
- else
- PixFormat = (UseAlpha ? PixelFormat.Format16bppArgb1555 : PixelFormat.Format16bppRgb555);
- break;
- case TgaPixelDepth.Bpp24:
- PixFormat = PixelFormat.Format24bppRgb;
- break;
- case TgaPixelDepth.Bpp32:
- if (UseAlpha)
- {
- var f = Footer;
- if (ExtArea?.AttributesType == TgaAttrType.PreMultipliedAlpha)
- PixFormat = PixelFormat.Format32bppPArgb;
- else
- PixFormat = PixelFormat.Format32bppArgb;
- }
- else
- PixFormat = PixelFormat.Format32bppRgb;
- break;
- default:
- PixFormat = PixelFormat.Undefined;
- break;
- }
- #endregion
- ushort BMP_Width = (PostageStampImage ? ExtArea.PostageStampImage.Width : Width);
- ushort BMP_Height = (PostageStampImage ? ExtArea.PostageStampImage.Height : Height);
- Bitmap BMP = new Bitmap(BMP_Width, BMP_Height, PixFormat);
- #region ColorMap and GrayPalette
- if (Header.ColorMapType == TgaColorMapType.ColorMap &&
- (Header.ImageType == TgaImageType.RLE_ColorMapped ||
- Header.ImageType == TgaImageType.Uncompressed_ColorMapped))
- {
- ColorPalette ColorMap = BMP.Palette;
- Color[] CMapColors = ColorMap.Entries;
- switch (Header.ColorMapSpec.ColorMapEntrySize)
- {
- case TgaColorMapEntrySize.X1R5G5B5:
- case TgaColorMapEntrySize.A1R5G5B5:
- const float To8Bit = 255f / 31f; // Scale value from 5 to 8 bits.
- for (int i = 0; i < Math.Min(CMapColors.Length, Header.ColorMapSpec.ColorMapLength); i++)
- {
- ushort A1R5G5B5 = BitConverter.ToUInt16(ImageOrColorMapArea.ColorMapData, i * 2);
- int A = (UseAlpha ? (A1R5G5B5 & 0x8000) >> 15 : 1) * 255; // (0 or 1) * 255
- int R = (int)(((A1R5G5B5 & 0x7C00) >> 10) * To8Bit);
- int G = (int)(((A1R5G5B5 & 0x3E0) >> 5) * To8Bit);
- int B = (int)((A1R5G5B5 & 0x1F) * To8Bit);
- CMapColors[i] = Color.FromArgb(A, R, G, B);
- }
- break;
- case TgaColorMapEntrySize.R8G8B8:
- for (int i = 0; i < Math.Min(CMapColors.Length, Header.ColorMapSpec.ColorMapLength); i++)
- {
- int Index = i * 3; //RGB = 3 bytes
- int R = ImageOrColorMapArea.ColorMapData[Index + 2];
- int G = ImageOrColorMapArea.ColorMapData[Index + 1];
- int B = ImageOrColorMapArea.ColorMapData[Index];
- CMapColors[i] = Color.FromArgb(R, G, B);
- }
- break;
- case TgaColorMapEntrySize.A8R8G8B8:
- for (int i = 0; i < Math.Min(CMapColors.Length, Header.ColorMapSpec.ColorMapLength); i++)
- {
- int ARGB = BitConverter.ToInt32(ImageOrColorMapArea.ColorMapData, i * 4);
- CMapColors[i] = Color.FromArgb(UseAlpha ? ARGB | (0xFF << 24) : ARGB);
- }
- break;
- default:
- ColorMap = null;
- break;
- }
- if (ColorMap != null)
- BMP.Palette = ColorMap;
- }
- if (PixFormat == PixelFormat.Format8bppIndexed && IsGrayImage)
- {
- ColorPalette GrayPalette = BMP.Palette;
- Color[] GrayColors = GrayPalette.Entries;
- for (int i = 0; i < GrayColors.Length; i++)
- GrayColors[i] = Color.FromArgb(i, i, i);
- BMP.Palette = GrayPalette;
- }
- #endregion
- #region Bitmap width must by aligned (align value = 32 bits = 4 bytes)!
- byte[] ImageData;
- int BytesPerPixel = (int)Math.Ceiling((double)Header.ImageSpec.PixelDepth / 8.0);
- int StrideBytes = BMP.Width * BytesPerPixel;
- int PaddingBytes = (int)Math.Ceiling(StrideBytes / 4.0) * 4 - StrideBytes;
- if (PaddingBytes > 0) //Need bytes align
- {
- ImageData = new byte[(StrideBytes + PaddingBytes) * BMP.Height];
- for (int i = 0; i < BMP.Height; i++)
- Buffer.BlockCopy(PostageStampImage ? ExtArea.PostageStampImage.Data :
- ImageOrColorMapArea.ImageData, i * StrideBytes, ImageData,
- i * (StrideBytes + PaddingBytes), StrideBytes);
- }
- else
- ImageData = BitConverterExt.ToBytes(PostageStampImage ? ExtArea.PostageStampImage.Data :
- ImageOrColorMapArea.ImageData);
- // Not official supported, but works (tested on 2 test images)!
- if (PixFormat == PixelFormat.Format16bppGrayScale)
- {
- for (long i = 0; i < ImageData.Length; i++)
- ImageData[i] ^= byte.MaxValue;
- }
- #endregion
- Rectangle Re = new Rectangle(0, 0, BMP.Width, BMP.Height);
- BitmapData BmpData = BMP.LockBits(Re, ImageLockMode.WriteOnly, BMP.PixelFormat);
- Marshal.Copy(ImageData, 0, BmpData.Scan0, ImageData.Length);
- BMP.UnlockBits(BmpData);
- ImageData = null;
- BmpData = null;
- if (ExtArea != null && ExtArea.KeyColor.ToInt() != 0)
- BMP.MakeTransparent(ExtArea.KeyColor.ToColor());
- #region Flip Image
- switch (Header.ImageSpec.ImageDescriptor.ImageOrigin)
- {
- case TgaImgOrigin.BottomLeft:
- BMP.RotateFlip(RotateFlipType.RotateNoneFlipY);
- break;
- case TgaImgOrigin.BottomRight:
- BMP.RotateFlip(RotateFlipType.RotateNoneFlipXY);
- break;
- case TgaImgOrigin.TopLeft:
- default:
- break;
- case TgaImgOrigin.TopRight:
- BMP.RotateFlip(RotateFlipType.RotateNoneFlipX);
- break;
- }
- #endregion
- return BMP;
- }
- catch
- {
- return null;
- }
- }
- #endregion
- #region Explicit
- public static explicit operator Bitmap(TGA tga)
- {
- return tga.ToBitmap();
- }
- public static explicit operator TGA(Bitmap bmp)
- {
- return FromBitmap(bmp);
- }
- #endregion
- #region PostageStamp Image
- /// <summary>
- /// Convert <see cref="TgaPostageStampImage"/> to <see cref="Bitmap"/>.
- /// </summary>
- /// <param name="ForceUseAlpha">Force use alpha channel.</param>
- /// <returns>Bitmap or null.</returns>
- public Bitmap GetPostageStampImage(bool ForceUseAlpha = false)
- {
- if (ExtArea == null || ExtArea.PostageStampImage == null || ExtArea.PostageStampImage.Data == null ||
- ExtArea.PostageStampImage.Width <= 0 || ExtArea.PostageStampImage.Height <= 0)
- return null;
- return ToBitmapFunc(ForceUseAlpha, true);
- }
- /// <summary>
- /// Update Postage Stamp Image or set it.
- /// </summary>
- public void UpdatePostageStampImage()
- {
- if (Header.ImageType == TgaImageType.NoImageData)
- {
- if (ExtArea != null)
- ExtArea.PostageStampImage = null;
- return;
- }
- ToNewFormat();
- if (ExtArea.PostageStampImage == null)
- ExtArea.PostageStampImage = new TgaPostageStampImage();
- int PS_Width = Header.ImageSpec.ImageWidth;
- int PS_Height = Header.ImageSpec.ImageHeight;
- if (Width > 64 || Height > 64)
- {
- float AspectRatio = Width / (float)Height;
- PS_Width = (byte)(64f * (AspectRatio < 1f ? AspectRatio : 1f));
- PS_Height = (byte)(64f / (AspectRatio > 1f ? AspectRatio : 1f));
- }
- PS_Width = Math.Max(PS_Width, 4);
- PS_Height = Math.Max(PS_Height, 4);
- ExtArea.PostageStampImage.Width = (byte)PS_Width;
- ExtArea.PostageStampImage.Height = (byte)PS_Height;
- int BytesPerPixel = (int)Math.Ceiling((double)Header.ImageSpec.PixelDepth / 8.0);
- ExtArea.PostageStampImage.Data = new byte[PS_Width * PS_Height * BytesPerPixel];
- float WidthCoef = Width / (float)PS_Width;
- float HeightCoef = Height / (float)PS_Height;
- for (int y = 0; y < PS_Height; y++)
- {
- int Y_Offset = (int)(y * HeightCoef) * Width * BytesPerPixel;
- int y_Offset = y * PS_Width * BytesPerPixel;
- for (int x = 0; x < PS_Width; x++)
- {
- Buffer.BlockCopy(ImageOrColorMapArea.ImageData, Y_Offset + (int)(x * WidthCoef) * BytesPerPixel,
- ExtArea.PostageStampImage.Data, y_Offset + x * BytesPerPixel, BytesPerPixel);
- }
- }
- }
- public void DeletePostageStampImage()
- {
- if (ExtArea != null)
- ExtArea.PostageStampImage = null;
- }
- #endregion
- }
- }
|