PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /usr/share/perl5/vendor_perl/Image
Viewing File: /usr/share/perl5/vendor_perl/Image/Xpm.pm
package Image::Xpm; # Documented at the __END__ use strict; use vars qw($VERSION @ISA); $VERSION = '1.13'; use Image::Base; @ISA = qw(Image::Base); use Carp qw(carp croak); use Symbol (); ### Data structures # # We will call the characters that are used to signify a particular colour the # cc's. # # -palette is a hash keyed by the cc's whose values are hashes of palette # colours, e.g. key x colour pairs. # # -cindex hash is a hash keyed by colour name ('#ffffff', 'blue' etc) whose # values are the cc's in the palette that represent that colour. Note that the # colour names are all lowercased even if they are mixed case in the palette # itself. # # -pixels is a string of cc's which is effectively a vector of 8, 16, 24, 32 # bits, etc. # # -extlines are lines of text used for any extensions; if we read any in we # hold them with the image and write them out if the image is saved, but we do # not process them. # Private class data # If you inherit don't clobber these fields! my @FIELD = qw(-file -width -height -ncolours -cpp -hotx -hoty -cc -palette -cindex -pixels -extname -extlines -comments -commentpixel -commentcolour); # States for parsing an xpm file my $STATE_START = 0; my $STATE_IN_COMMENT = 1; my $STATE_ARRAY = 2; my $STATE_VALUES = 3; my $STATE_COLOURS = 4; my $STATE_PIXELS = 5; my $STATE_EXTENSIONS = 6; my $STATE_FINISH = 7; my $MAX_CH = 255; my $CH_BS = 127; my $CH_BSLASH = 92; my $CH_QUOTE = 39; my $CH_DQUOTE = 34; my $CH_SPACE = 32; my $UNSET = -1; ### Private methods # # _get object inherited # _set object inherited # _nextcc object # _add_colour object # _add_color object sub _nextcc { # Object method my $self = shift; # my $class = ref($self) || $self; while (exists $self->{-palette}{$self->{-cc}}) { my @ch = unpack "C$self->{-cpp}", $self->{-cc}; my $found = 0; foreach my $i (reverse 0..$self->{-cpp} - 1) { if ($ch[$i] < $MAX_CH) { $ch[$i]++; $ch[$i]++ # Skip BS, \, ' and " -- using magic nums for speed while $ch[$i] == $CH_BS or $ch[$i] == $CH_BSLASH or $ch[$i] == $CH_QUOTE or $ch[$i] == $CH_DQUOTE; $found++; last; # Finish as soon as we've incremented something } else { $ch[$i] = $CH_SPACE; # Skip control chars } } croak "_nextcc() ran out of palette characters" unless $found; $self->{-cc} = pack "C$self->{-cpp}", @ch; } croak "_nextcc() cpp is too small" if length($self->{-cc}) > $self->{-cpp}; $self->{-cc}; } *_add_color = \&_add_colour; sub _add_colour { # Object method my $self = shift; # my $class = ref($self) || $self; my $colour = shift; my $lccolour = lc $colour; return $self->{-cindex}{$lccolour} if exists $self->{-cindex}{$lccolour}; $self->{-cc} = $self->_nextcc if exists $self->{-palette}{$self->{-cc}}; $self->{-palette}{$self->{-cc}} = { c => $colour }; $self->{-cindex}{$lccolour} = $self->{-cc}; $self->{-ncolours}++; $self->{-cc}; } sub DESTROY { ; # Save's time } ### Public methods sub new { # Class and object method my $self = shift; my $class = ref($self) || $self; my $obj = ref $self ? $self : undef; my %arg = @_; # Defaults $self = { '-hotx' => $UNSET, '-hoty' => $UNSET, '-cpp' => 1, '-palette' => {}, '-cindex' => {}, '-pixels' => '', '-comments' => [], '-commentpixel' => '', # Typically /* pixels */ '-commentcolour' => '', # Typically /* colors */ '-extlines' => [], }; bless $self, $class; # If $obj->new copy original object's data if (defined $obj) { foreach my $field (@FIELD) { $self->_set($field, $obj->_get($field)); } } # Any options specified override foreach my $field (@FIELD) { $self->_set($field, $arg{$field}) if defined $arg{$field}; } $self->{-cc} = ' ' x $self->{-cpp}; my $file = $self->get('-file'); if (defined $file and not $self->{-pixels}) { $self->load if ref $file or -r $file; } croak "new() `$file' not found or unreadable" if defined $file and not defined $self->get('-width'); foreach my $field (qw(-width -height -cpp)) { croak "new() $field must be set" unless defined $self->get($field); } if (not $self->{-pixels}) { $self->{-pixels} = ' ' x ($self->{-width} * $self->{-height} * $self->{-cpp}); $self->_add_colour('white'); } $self; } # get() is inherited sub set { # Object method my $self = shift; # my $class = ref($self) || $self; while (@_) { my $field = shift; my $val = shift; carp "set() -field has no value" unless defined $val; carp "set() $field is read-only" if $field =~ /^-(?:cpp|comments|cindex|ncolours|palette|pixels| width|height|ext(?:name|lines))/ox; carp "set() -hotx `$val' is out of range" if $field eq '-hotx' and ($val < $UNSET or $val >= $self->get('-width')); carp "set() -hoty `$val' is out of range" if $field eq '-hoty' and ($val < $UNSET or $val >= $self->get('-height')); $self->_set($field, $val); } } sub xy { # Object method my $self = shift; my ($x, $y, $colour) = @_; # xy() is common so we can't afford the expense of method calls if (defined $colour) { substr($self->{-pixels}, ($y * $self->{-width} * $self->{-cpp}) + ($x * $self->{-cpp}), $self->{-cpp}) = $self->{-cindex}{lc $colour} || $self->_add_colour($colour); } else { my $cc = substr($self->{-pixels}, ($y * $self->{-width} * $self->{-cpp}) + ($x * $self->{-cpp}), $self->{-cpp}); return $self->{-palette}{$cc}{c} || $self->{-palette}{$cc}{m} || $self->{-palette}{$cc}{s} || $self->{-palette}{$cc}{g} || $self->{-palette}{$cc}{g4}; } } sub vec { # Object method my $self = shift; my ($offset, $colour) = @_; if (defined $colour) { substr($self->{-pixels}, $offset, $self->{-cpp}) = $self->{-cindex}{lc $colour} || $self->_add_colour($colour); } else { my $cc = substr($self->{-pixels}, $offset, $self->{-cpp}); return $self->{-palette}{$cc}{c} || $self->{-palette}{$cc}{m} || $self->{-palette}{$cc}{s} || $self->{-palette}{$cc}{g} || $self->{-palette}{$cc}{g4}; } } *rgb2color = \&rgb2colour; sub rgb2colour { # Class or object method my $self = shift; # my $class = ref($self) || $self; sprintf "#%02x%02x%02x", @_; } *add_colors = \&add_colours; sub add_colours { # Object method my $self = shift; # my $class = ref($self) || $self; $self->_add_colour(shift @_) while @_; } *del_color = \&del_colour; sub del_colour { # Object method my $self = shift; # my $class = ref($self) || $self; my $colour = lc shift; my $cc = $self->{-cindex}{$colour}; return undef unless defined $cc; # Colour isn't there to delete my $cpp = $self->get(-cpp); for (my $i = 0; $i < length($self->{-pixels}) / $cpp; $i += $cpp) { return 0 if substr($self->{-pixels}, $i, $cpp) eq $cc; } delete $self->{-palette}{$cc}; delete $self->{-cindex}{$colour}; $self->{-ncolours}--; 1; } sub load { # Object method my $self = shift; # my $class = ref($self) || $self; my $file = shift() || $self->get('-file'); croak "load() no file specified" unless $file; $self->set('-file', $file); my ($width, $height, $ncolours, $cpp, $hotx, $hoty, $extname); my $next_state = $STATE_START; my $state = $STATE_START; my $err = "load() file `$file' "; my %palette; my $i; local $_; my $fh = Symbol::gensym; if( not ref $file ) { open $fh, $file or croak "load() failed to open `$file': $!" ; } elsif( ref($file) eq 'SCALAR' ) { if( $] >= 5.008001 ) { # 5.8.0 dumps core when using "scalar open" eval q{ open $fh, "<", $file } # avoid syntax error with pre-5.6 perls or croak "cannot handle scalar value: $!"; } else { require IO::String; $fh = IO::String->new( $$file ); } } else { seek($file, 0, 0) or croak "load() can't rewind handle for `$file': $!"; $fh = $file; } $self->{-palette} = {}; $self->{-cindex} = {}; $self->{-comments} = []; $self->{-extlines} = []; $self->{-pixels} = ''; $self->{-commentpixel} = ''; $self->{-commentcolour} = ''; LINE: while (<$fh>) { # Blank lines next LINE if /^\s*$/o; # Starting comment if ($state == $STATE_START) { croak "$err does not begin with /* XPM */" unless m,/\*\s*XPM\s*\*/,o; $state = $STATE_ARRAY; next LINE; } # Comment only lines if (m,^(\s*/\*.*\*/\s*)$,o) { my $comment = $1; if ($comment =~ m,^\s*/\*\s*colou?rs?\s*\*/\s*$,o) { $self->set(-commentcolour, $comment); } elsif ($comment =~ m,^\s*/\*\s*pixels?\s*\*/\s*$,o) { $self->set(-commentpixel, $comment); } else { push @{$self->{-comments}}, $comment; } next LINE; } # Start of multi-line comment if ($state != $STATE_IN_COMMENT and m,^\s*/\*,o) { push @{$self->{-comments}}, $_; $next_state = $state; # Remember the state we're due for $state = $STATE_IN_COMMENT; next LINE; } # End of multi-line comment if ($state == $STATE_IN_COMMENT) { push @{$self->{-comments}}, $_; $state = $next_state if m,\*/,o; next LINE; } # Name of C string if ($state == $STATE_ARRAY) { ## While this line is specified in the xpm.ps document, the libXpm ## library itself seems to ignore the contents of this line ## completely. So Image::Xpm should also do. # croak "$err does not have a proper C array name" # unless /static\s+(?:const\s+)?char\s*\*\s*(?:const\s+)?[A-Za-z0-9_-]+\s*\[\s*\]\s*=\s*\{/o; #} $state = $STATE_VALUES; next LINE; } # Values line if ($state == $STATE_VALUES) { ($width, $height, $ncolours, $cpp, $hotx, $hoty, $extname) = /"\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+) (?:\s+(-?\d+)\s+(-?\d+))?(?:\s+(\w+))?\s*"/ox; croak "$err missing width" unless defined $width; croak "$err missing height" unless defined $height; croak "$err missing ncolours" unless defined $ncolours; croak "$err missing cpp" unless defined $cpp; croak "$err zero width is invalid" if $width == 0; croak "$err zero height is invalid" if $height == 0; croak "$err zero ncolours is invalid" if $ncolours == 0; croak "$err zero cpp is invalid" if $cpp == 0; if ((defined $hotx and not defined $hoty) or (defined $hotx and $hotx >= $width) or (defined $hoty and $hoty >= $height)) { carp "$err deleted invalid hotspot"; $hotx = $hoty = $UNSET; } $hotx = $hoty = $UNSET unless defined $hotx ; carp "$err unusually large cpp `$cpp'" if $cpp > 4; $self->{-cpp} = $cpp; # Have to do this early as possible. $i = 0; $state = $STATE_COLOURS; next LINE; } # Colour palette if ($state == $STATE_COLOURS) { /"(.{$cpp})/; #" No /o since this can vary between images! my $cc = $1; my %pair = /\s+(m|s|g4|g|c)\s+(#[A-Fa-f\d]{3,}|\w+)/go; $self->{-cindex}{lc $pair{'c'}} = $cc if exists $pair{'c'}; $self->{-palette}{$cc} = { %pair }; $i++; croak "$err palette larger than ncolors" if $i > $ncolours; if ($i == $ncolours) { $i = 0; $state = $STATE_PIXELS; } next LINE; } # Pixels if ($state == $STATE_PIXELS) { /^\s*"(.*)"/o; $self->{-pixels} .= $1; $i++; croak "$err more pixels than height indicates" if $i > $height; $state = defined $extname ? $STATE_EXTENSIONS : $STATE_FINISH if $i == $height; next LINE; } # Extensions if ($state == $STATE_EXTENSIONS) { if (/XPMENDEXT/o) { $state = $STATE_FINISH; } else { push @{$self->{-extlines}}, $_; } next LINE; } # Finish if ($state == $STATE_FINISH) { ## The ending brace could also happened in the line before. So don't ## do any checks anymore here. # croak "$err invalid ending" unless /\}\s*;/; last LINE; } } close $fh or croak "load() failed to close `$file': $!"; push @{$self->{-extlines}}, "XPMENDEXT\n" if scalar @{$self->{-extlines}}; $self->_set(-cpp => $cpp); $self->_set(-width => $width); $self->_set(-height => $height); $self->_set(-ncolours => $ncolours); $self->_set(-extname => $extname); $self->set(-hotx => $hotx, -hoty => $hoty); } sub save { # Object method my $self = shift; # my $class = ref($self) || $self; my $file = shift() || $self->get('-file'); croak "save() no file specified" unless $file; $self->set('-file', $file); my ($width, $height, $cpp) = $self->get('-width', '-height', '-cpp'); my $line; my $fh = Symbol::gensym; open $fh, ">$file" or croak "save() failed to open `$file': $!"; $file =~ s,^.*/,,o; $file =~ s/\.xpm$//o; $file =~ tr/[-_A-Za-z0-9]/_/c; print $fh "/* XPM */\nstatic char *", $file, "[] = {\n"; print $fh @{$self->get(-comments)}; $line = qq{"$width $height } . $self->get(-ncolours) . " $cpp "; #" $line .= $self->get(-hotx) . " " . $self->get(-hoty) . " " if $self->get(-hotx) > $UNSET; $line .= $self->get(-extname) if defined $self->get(-extname); $line =~ s/\s+$//o; print $fh qq{$line",\n}, $self->get(-commentcolour); #" while (my ($cc, $pairs) = each (%{$self->{-palette}})) { $line = qq{"$cc }; #" foreach my $key (sort keys %{$pairs}) { $line .= "$key $pairs->{$key} "; } $line =~ s/\s+$//o; print $fh qq{$line",\n}; #" } print $fh $self->get(-commentpixel); my $comma = ','; for (my $y = 0; $y < $height; $y++) { $comma = '' if $y == $height - 1; print $fh '"', substr($self->{-pixels}, $y * $width * $cpp, $width * $cpp), qq{"$comma\n}; #" } print $fh @{$self->get(-extlines)}, "};\n"; close $fh or croak "save() failed to close `$file': $!"; } 1; __END__ =head1 NAME Image::Xpm - Load, create, manipulate and save xpm image files. =head1 SYNOPSIS use Image::Xpm; my $j = Image::Xpm->new(-file, 'Camel.xpm'); my $i = Image::Xpm->new(-width => 10, -height => 16); my $h = $i->new; # Copy of $i $i->xy(5, 8, 'red'); # Set a colour (& add to palette if necessary) print $i->xy(9, 3); # Get a colour $i->xy(120, 130, '#1256DD'); $i->xy(120, 130, $i->rgb2colour(66, 0x4D, 31)); $i->vec(24, '#808080'); # Set a colour using a vector offset print $i->vec(24); # Get a colour using a vector offset print $i->get(-width); # Get and set object attributes $i->set(-height, 15); $i->load('test.xpm'); $i->save; # Changing just the palette $i->add_colours(qw(red green blue #123456 #C0C0C0)); $i->del_colour('blue'); =head1 DESCRIPTION This class module provides basic load, manipulate and save functionality for the xpm file format. It inherits from C<Image::Base> which provides additional manipulation functionality, e.g. C<new_from_image()>. See the C<Image::Base> pod for information on adding your own functionality to all the Image::Base derived classes. =head2 new() my $i = Image::Xpm->new(-file => 'test.xpm'); my $j = Image::Xpm->new(-width => 12, -height => 18); my $k = $i->new; We can create a new xpm image by reading in a file, or by creating an image from scratch (all the pixels are white by default), or by copying an image object that we created earlier. If we set C<-file> then all the other arguments are ignored (since they're taken from the file). If we don't specify a file, C<-width> and C<-height> are mandatory and C<-cpp> will default to 1 unless specified otherwise. =over =item C<-file> The name of the file to read when creating the image. May contain a full path. This is also the default name used for C<load>ing and C<save>ing, though it can be overridden when you load or save. =item C<-width> The width of the image; taken from the file or set when the object is created; read-only. =item C<-height> The height of the image; taken from the file or set when the object is created; read-only. =item C<-cpp> Characters per pixel. Commonly 1 or 2, default is 1 for images created by the module; read-only. See the example for how to change an image's cpp. =item C<-hotx> The x-coord of the image's hotspot; taken from the file or set when the object is created. Set to -1 if there is no hotspot. =item C<-hoty> The y-coord of the image's hotspot; taken from the file or set when the object is created. Set to -1 if there is no hotspot. =item C<-ncolours> The number of unique colours in the palette. The image may not be using all of them; read-only. =item C<-cindex> An hash whose keys are colour names, e.g. '#123456' or 'blue' and whose values are the palette names, e.g. ' ', '#', etc; read-only. If you want to add more colours to the image itself simply write pixels with the new colours using C<xy>; if you want to add more colours to the palette without necessarily using them in the image use C<add_colours>. =item C<-palette> A hash whose keys are the palette names, e.g. ' ', '#', etc. and whose values are hashes of colour type x colour name pairs, e.g. C<c =E<gt> red>, etc; read-only. If you want to add more colours to the image itself simply write pixels with the new colours using C<xy>; if you want to add more colours to the palette without necessarily using them in the image use C<add_colours>. =item C<-pixels> A string of palette names which constitutes the data for the image itself; read-only. =item C<-extname> The name of the extension text if any; commonly XPMEXT; read-only. =item C<-extlines> The lines of text of any extensions; read-only. =item C<-comments> An array (possibly empty) of comment lines that were in a file that was read in; they will be written out although we make no guarantee regarding their placement; read-only. =back =head2 get() my $width = $i->get(-width); my ($hotx, $hoty) = $i->get(-hotx, -hoty); Get any of the object's attributes. Multiple attributes may be requested in a single call. See C<xy> and C<vec> to get/set colours of the image itself. =head2 set() $i->set(-hotx => 120, -hoty => 32); Set any of the object's attributes. Multiple attributes may be set in a single call; some attributes are read-only. See C<xy> and C<vec> to get/set colours of the image itself. =head2 xy() $i->xy(4, 11, '#123454'); # Set the colour at point 4,11 my $v = $i->xy(9, 17); # Get the colour at point 9,17 Get/set colours using x, y coordinates; coordinates start at 0. If the colour does not exist in the palette it will be added automatically. When called to set the colour the value returned is characters used for that colour in the palette; when called to get the colour the value returned is the colour name, e.g. 'blue' or '#f0f0f0', etc, e.g. $colour = xy($x, $y); # e.g. #123456 $cc = xy($x, $y, $colour); # e.g. ! We don't normally pick up the return value when setting the colour. =head2 vec() $i->vec(43, 0); # Unset the bit at offset 43 my $v = $i->vec(87); # Get the bit at offset 87 Get/set bits using vector offsets; offsets start at 0. The offset of a pixel is ((y * width * cpp) + (x * cpp)). The sort of return value depends on whether we are reading (getting) or writing (setting) the colour - see C<xy> for an explanation. =head2 rgb2colour() and rgb2color() $i->rgb2colour(0xff, 0x40, 0x80); # Returns #ff4080 Image::Xpm->rgb2colour(10, 20, 30); # Returns #0a141e Convenience class or object methods which accept three integers and return a colour name string. =head2 load() $i->load; $i->load('test.xpm'); Load the image whose name is given, or if none is given load the image whose name is in the C<-file> attribute. =head2 save() $i->save; $i->save('test.xpm'); Save the image using the name given, or if none is given save the image using the name in the C<-file> attribute. The image is saved in xpm format. =head2 add_colours() and add_colors() $i->add_colours(qw(#C0C0DD red blue #123456)); These are for adding colours to the palette; you don't need to use them to set a pixel's colour - use C<xy> for that. Add one or more colour names either as hex strings or as literal colour names. These are always added as type 'c' colours; duplicates are ignored. NB If you just want to set some pixels in colours that may not be in the palette, simply do so using C<xy> since new colours are added automatically. =head2 del_colour() and del_color() $i->del_colour('green'); Delete a colour from the palette; returns undef if the colour isn't in the palette, false (0) if the colour is in the palette but also in the image, or true (1) if the colour has been deleted (i.e. it was in the palette but not in use in the image). =head1 EXAMPLE =head2 Changing the -cpp of an image: my $i = Image::Xpm(-file => 'test1.xpm'); # test1.xpm has cpp == 1 my $j = $i->new_from_image('Image::xpm', -cpp => 2); $j->save('test2.xpm'); # Could have written 2nd line above as: my $j = $i->new_from_image(ref $i, -cpp => 2); =head1 AUTHOR Mark Summerfield. I can be contacted as <summer@perlpress.com> - please include the word 'xpm' in the subject line. =head1 COPYRIGHT Copyright (c) Mark Summerfield 2000. All Rights Reserved. This module may be used/distributed/modified under the GPL. =cut