PNG  IHDRQgAMA a cHRMz&u0`:pQ<bKGDgmIDATxwUﹻ& ^CX(J I@ "% (** BX +*i"]j(IH{~R)[~>h{}gy)I$Ij .I$I$ʊy@}x.: $I$Ii}VZPC)I$IF ^0ʐJ$I$Q^}{"r=OzI$gRZeC.IOvH eKX $IMpxsk.쒷/&r[޳<v| .I~)@$updYRa$I |M.e JaֶpSYR6j>h%IRز if&uJ)M$I vLi=H;7UJ,],X$I1AҒJ$ XY XzI@GNҥRT)E@;]K*Mw;#5_wOn~\ DC&$(A5 RRFkvIR}l!RytRl;~^ǷJj اy뷦BZJr&ӥ8Pjw~vnv X^(I;4R=P[3]J,]ȏ~:3?[ a&e)`e*P[4]T=Cq6R[ ~ޤrXR Հg(t_HZ-Hg M$ãmL5R uk*`%C-E6/%[t X.{8P9Z.vkXŐKjgKZHg(aK9ڦmKjѺm_ \#$5,)-  61eJ,5m| r'= &ڡd%-]J on Xm|{ RҞe $eڧY XYrԮ-a7RK6h>n$5AVڴi*ֆK)mѦtmr1p| q:흺,)Oi*ֺK)ܬ֦K-5r3>0ԔHjJئEZj,%re~/z%jVMڸmrt)3]J,T K֦OvԒgii*bKiNO~%PW0=dii2tJ9Jݕ{7"I P9JKTbu,%r"6RKU}Ij2HKZXJ,妝 XYrP ެ24c%i^IK|.H,%rb:XRl1X4Pe/`x&P8Pj28Mzsx2r\zRPz4J}yP[g=L) .Q[6RjWgp FIH*-`IMRaK9TXcq*I y[jE>cw%gLRԕiFCj-ďa`#e~I j,%r,)?[gp FI˨mnWX#>mʔ XA DZf9,nKҲzIZXJ,L#kiPz4JZF,I,`61%2s $,VOϚ2/UFJfy7K> X+6 STXIeJILzMfKm LRaK9%|4p9LwJI!`NsiazĔ)%- XMq>pk$-$Q2x#N ؎-QR}ᶦHZډ)J,l#i@yn3LN`;nڔ XuX5pF)m|^0(>BHF9(cզEerJI rg7 4I@z0\JIi䵙RR0s;$s6eJ,`n 䂦0a)S)A 1eJ,堌#635RIgpNHuTH_SԕqVe ` &S)>p;S$魁eKIuX`I4춒o}`m$1":PI<[v9^\pTJjriRŭ P{#{R2,`)e-`mgj~1ϣLKam7&U\j/3mJ,`F;M'䱀 .KR#)yhTq;pcK9(q!w?uRR,n.yw*UXj#\]ɱ(qv2=RqfB#iJmmL<]Y͙#$5 uTU7ӦXR+q,`I}qL'`6Kͷ6r,]0S$- [RKR3oiRE|nӦXR.(i:LDLTJjY%o:)6rxzҒqTJjh㞦I.$YR.ʼnGZ\ֿf:%55 I˼!6dKxm4E"mG_ s? .e*?LRfK9%q#uh$)i3ULRfK9yxm܌bj84$i1U^@Wbm4uJ,ҪA>_Ij?1v32[gLRD96oTaR׿N7%L2 NT,`)7&ƝL*꽙yp_$M2#AS,`)7$rkTA29_Iye"|/0t)$n XT2`YJ;6Jx".e<`$) PI$5V4]29SRI>~=@j]lp2`K9Jaai^" Ԋ29ORI%:XV5]JmN9]H;1UC39NI%Xe78t)a;Oi Ҙ>Xt"~G>_mn:%|~ޅ_+]$o)@ǀ{hgN;IK6G&rp)T2i୦KJuv*T=TOSV>(~D>dm,I*Ɛ:R#ۙNI%D>G.n$o;+#RR!.eU˽TRI28t)1LWϚ>IJa3oFbu&:tJ*(F7y0ZR ^p'Ii L24x| XRI%ۄ>S1]Jy[zL$adB7.eh4%%누>WETf+3IR:I3Xה)3אOۦSRO'ٺ)S}"qOr[B7ϙ.edG)^ETR"RtRݜh0}LFVӦDB^k_JDj\=LS(Iv─aTeZ%eUAM-0;~˃@i|l @S4y72>sX-vA}ϛBI!ݎߨWl*)3{'Y|iSlEڻ(5KtSI$Uv02,~ԩ~x;P4ցCrO%tyn425:KMlD ^4JRxSهF_}شJTS6uj+ﷸk$eZO%G*^V2u3EMj3k%)okI]dT)URKDS 7~m@TJR~荪fT"֛L \sM -0T KfJz+nإKr L&j()[E&I ߴ>e FW_kJR|!O:5/2跌3T-'|zX ryp0JS ~^F>-2< `*%ZFP)bSn"L :)+pʷf(pO3TMW$~>@~ū:TAIsV1}S2<%ޟM?@iT ,Eūoz%i~g|`wS(]oȤ8)$ ntu`өe`6yPl IzMI{ʣzʨ )IZ2= ld:5+請M$-ї;U>_gsY$ÁN5WzWfIZ)-yuXIfp~S*IZdt;t>KūKR|$#LcԀ+2\;kJ`]YǔM1B)UbG"IRߊ<xܾӔJ0Z='Y嵤 Leveg)$znV-º^3Ւof#0Tfk^Zs[*I꯳3{)ˬW4Ւ4 OdpbZRS|*I 55#"&-IvT&/윚Ye:i$ 9{LkuRe[I~_\ؠ%>GL$iY8 9ܕ"S`kS.IlC;Ҏ4x&>u_0JLr<J2(^$5L s=MgV ~,Iju> 7r2)^=G$1:3G< `J3~&IR% 6Tx/rIj3O< ʔ&#f_yXJiގNSz; Tx(i8%#4 ~AS+IjerIUrIj362v885+IjAhK__5X%nV%Iͳ-y|7XV2v4fzo_68"S/I-qbf; LkF)KSM$ Ms>K WNV}^`-큧32ŒVؙGdu,^^m%6~Nn&͓3ŒVZMsRpfEW%IwdǀLm[7W&bIRL@Q|)* i ImsIMmKmyV`i$G+R 0tV'!V)֏28vU7͒vHꦼtxꗞT ;S}7Mf+fIRHNZUkUx5SAJㄌ9MqμAIRi|j5)o*^'<$TwI1hEU^c_j?Е$%d`z cyf,XO IJnTgA UXRD }{H}^S,P5V2\Xx`pZ|Yk:$e ~ @nWL.j+ϝYb퇪bZ BVu)u/IJ_ 1[p.p60bC >|X91P:N\!5qUB}5a5ja `ubcVxYt1N0Zzl4]7­gKj]?4ϻ *[bg$)+À*x쳀ogO$~,5 زUS9 lq3+5mgw@np1sso Ӻ=|N6 /g(Wv7U;zωM=wk,0uTg_`_P`uz?2yI!b`kĸSo+Qx%!\οe|އԁKS-s6pu_(ֿ$i++T8=eY; צP+phxWQv*|p1. ά. XRkIQYP,drZ | B%wP|S5`~́@i޾ E;Չaw{o'Q?%iL{u D?N1BD!owPHReFZ* k_-~{E9b-~P`fE{AܶBJAFO wx6Rox5 K5=WwehS8 (JClJ~ p+Fi;ŗo+:bD#g(C"wA^ r.F8L;dzdIHUX݆ϞXg )IFqem%I4dj&ppT{'{HOx( Rk6^C٫O.)3:s(۳(Z?~ٻ89zmT"PLtw䥈5&b<8GZ-Y&K?e8,`I6e(֍xb83 `rzXj)F=l($Ij 2*(F?h(/9ik:I`m#p3MgLaKjc/U#n5S# m(^)=y=đx8ŬI[U]~SцA4p$-F i(R,7Cx;X=cI>{Km\ o(Tv2vx2qiiDJN,Ҏ!1f 5quBj1!8 rDFd(!WQl,gSkL1Bxg''՞^ǘ;pQ P(c_ IRujg(Wz bs#P­rz> k c&nB=q+ؔXn#r5)co*Ũ+G?7< |PQӣ'G`uOd>%Mctz# Ԫڞ&7CaQ~N'-P.W`Oedp03C!IZcIAMPUۀ5J<\u~+{9(FbbyAeBhOSܳ1 bÈT#ŠyDžs,`5}DC-`̞%r&ڙa87QWWp6e7 Rϫ/oY ꇅ Nܶըtc!LA T7V4Jsū I-0Pxz7QNF_iZgúWkG83 0eWr9 X]㾮݁#Jˢ C}0=3ݱtBi]_ &{{[/o[~ \q鯜00٩|cD3=4B_b RYb$óBRsf&lLX#M*C_L܄:gx)WΘsGSbuL rF$9';\4Ɍq'n[%p.Q`u hNb`eCQyQ|l_C>Lb꟟3hSb #xNxSs^ 88|Mz)}:](vbۢamŖ࿥ 0)Q7@0=?^k(*J}3ibkFn HjB׻NO z x}7p 0tfDX.lwgȔhԾŲ }6g E |LkLZteu+=q\Iv0쮑)QٵpH8/2?Σo>Jvppho~f>%bMM}\//":PTc(v9v!gոQ )UfVG+! 35{=x\2+ki,y$~A1iC6#)vC5^>+gǵ@1Hy٪7u;p psϰu/S <aʸGu'tD1ԝI<pg|6j'p:tպhX{o(7v],*}6a_ wXRk,O]Lܳ~Vo45rp"N5k;m{rZbΦ${#)`(Ŵg,;j%6j.pyYT?}-kBDc3qA`NWQū20/^AZW%NQ MI.X#P#,^Ebc&?XR tAV|Y.1!؅⨉ccww>ivl(JT~ u`ٵDm q)+Ri x/x8cyFO!/*!/&,7<.N,YDŽ&ܑQF1Bz)FPʛ?5d 6`kQձ λc؎%582Y&nD_$Je4>a?! ͨ|ȎWZSsv8 j(I&yj Jb5m?HWp=g}G3#|I,5v珿] H~R3@B[☉9Ox~oMy=J;xUVoj bUsl_35t-(ՃɼRB7U!qc+x4H_Qo֮$[GO<4`&č\GOc[.[*Af%mG/ ňM/r W/Nw~B1U3J?P&Y )`ѓZ1p]^l“W#)lWZilUQu`-m|xĐ,_ƪ|9i:_{*(3Gѧ}UoD+>m_?VPۅ15&}2|/pIOʵ> GZ9cmíتmnz)yߐbD >e}:) r|@R5qVSA10C%E_'^8cR7O;6[eKePGϦX7jb}OTGO^jn*媓7nGMC t,k31Rb (vyܴʭ!iTh8~ZYZp(qsRL ?b}cŨʊGO^!rPJO15MJ[c&~Z`"ѓޔH1C&^|Ш|rʼ,AwĴ?b5)tLU)F| &g٣O]oqSUjy(x<Ϳ3 .FSkoYg2 \_#wj{u'rQ>o;%n|F*O_L"e9umDds?.fuuQbIWz |4\0 sb;OvxOSs; G%T4gFRurj(֍ڑb uԖKDu1MK{1^ q; C=6\8FR艇!%\YÔU| 88m)֓NcLve C6z;o&X x59:q61Z(T7>C?gcļxѐ Z oo-08jہ x,`' ҔOcRlf~`jj".Nv+sM_]Zk g( UOPyεx%pUh2(@il0ݽQXxppx-NS( WO+轾 nFߢ3M<;z)FBZjciu/QoF 7R¥ ZFLF~#ȣߨ^<쩡ݛкvџ))ME>ώx4m#!-m!L;vv#~Y[đKmx9.[,UFS CVkZ +ߟrY٧IZd/ioi$%͝ب_ֶX3ܫhNU ZZgk=]=bbJS[wjU()*I =ώ:}-蹞lUj:1}MWm=̛ _ ¾,8{__m{_PVK^n3esw5ӫh#$-q=A̟> ,^I}P^J$qY~Q[ Xq9{#&T.^GVj__RKpn,b=`żY@^՝;z{paVKkQXj/)y TIc&F;FBG7wg ZZDG!x r_tƢ!}i/V=M/#nB8 XxЫ ^@CR<{䤭YCN)eKOSƟa $&g[i3.C6xrOc8TI;o hH6P&L{@q6[ Gzp^71j(l`J}]e6X☉#͕ ׈$AB1Vjh㭦IRsqFBjwQ_7Xk>y"N=MB0 ,C #o6MRc0|$)ف"1!ixY<B9mx `,tA>)5ػQ?jQ?cn>YZe Tisvh# GMމȇp:ԴVuږ8ɼH]C.5C!UV;F`mbBk LTMvPʍϤj?ԯ/Qr1NB`9s"s TYsz &9S%U԰> {<ؿSMxB|H\3@!U| k']$U+> |HHMLޢ?V9iD!-@x TIî%6Z*9X@HMW#?nN ,oe6?tQwڱ.]-y':mW0#!J82qFjH -`ѓ&M0u Uγmxϵ^-_\])@0Rt.8/?ٰCY]x}=sD3ojަЫNuS%U}ԤwHH>ڗjܷ_3gN q7[q2la*ArǓԖ+p8/RGM ]jacd(JhWko6ڎbj]i5Bj3+3!\j1UZLsLTv8HHmup<>gKMJj0@H%,W΃7R) ">c, xixј^ aܖ>H[i.UIHc U1=yW\=S*GR~)AF=`&2h`DzT󑓶J+?W+}C%P:|0H܆}-<;OC[~o.$~i}~HQ TvXΈr=b}$vizL4:ȰT|4~*!oXQR6Lk+#t/g lԁߖ[Jڶ_N$k*". xsxX7jRVbAAʯKҎU3)zSNN _'s?f)6X!%ssAkʱ>qƷb hg %n ~p1REGMHH=BJiy[<5 ǁJҖgKR*倳e~HUy)Ag,K)`Vw6bRR:qL#\rclK/$sh*$ 6덤 KԖc 3Z9=Ɣ=o>X Ώ"1 )a`SJJ6k(<c e{%kϊP+SL'TcMJWRm ŏ"w)qc ef꒵i?b7b('"2r%~HUS1\<(`1Wx9=8HY9m:X18bgD1u ~|H;K-Uep,, C1 RV.MR5άh,tWO8WC$ XRVsQS]3GJ|12 [vM :k#~tH30Rf-HYݺ-`I9%lIDTm\ S{]9gOڒMNCV\G*2JRŨ;Rҏ^ڽ̱mq1Eu?To3I)y^#jJw^Ńj^vvlB_⋌P4x>0$c>K†Aļ9s_VjTt0l#m>E-,,x,-W)سo&96RE XR.6bXw+)GAEvL)͞K4$p=Ũi_ѱOjb HY/+@θH9޼]Nԥ%n{ &zjT? Ty) s^ULlb,PiTf^<À] 62R^V7)S!nllS6~͝V}-=%* ʻ>G DnK<y&>LPy7'r=Hj 9V`[c"*^8HpcO8bnU`4JȪAƋ#1_\ XϘHPRgik(~G~0DAA_2p|J묭a2\NCr]M_0 ^T%e#vD^%xy-n}-E\3aS%yN!r_{ )sAw ڼp1pEAk~v<:`'ӭ^5 ArXOI驻T (dk)_\ PuA*BY]yB"l\ey hH*tbK)3 IKZ򹞋XjN n *n>k]X_d!ryBH ]*R 0(#'7 %es9??ښFC,ՁQPjARJ\Ρw K#jahgw;2$l*) %Xq5!U᢯6Re] |0[__64ch&_}iL8KEgҎ7 M/\`|.p,~`a=BR?xܐrQ8K XR2M8f ?`sgWS%" Ԉ 7R%$ N}?QL1|-эټwIZ%pvL3Hk>,ImgW7{E xPHx73RA @RS CC !\ȟ5IXR^ZxHл$Q[ŝ40 (>+ _C >BRt<,TrT {O/H+˟Pl6 I B)/VC<6a2~(XwV4gnXR ϱ5ǀHٻ?tw똤Eyxp{#WK qG%5],(0ӈH HZ])ג=K1j&G(FbM@)%I` XRg ʔ KZG(vP,<`[ Kn^ SJRsAʠ5xՅF`0&RbV tx:EaUE/{fi2;.IAwW8/tTxAGOoN?G}l L(n`Zv?pB8K_gI+ܗ #i?ޙ.) p$utc ~DžfՈEo3l/)I-U?aԅ^jxArA ΧX}DmZ@QLےbTXGd.^|xKHR{|ΕW_h] IJ`[G9{).y) 0X YA1]qp?p_k+J*Y@HI>^?gt.06Rn ,` ?);p pSF9ZXLBJPWjgQ|&)7! HjQt<| ؅W5 x W HIzYoVMGP Hjn`+\(dNW)F+IrS[|/a`K|ͻ0Hj{R,Q=\ (F}\WR)AgSG`IsnAR=|8$}G(vC$)s FBJ?]_u XRvύ6z ŨG[36-T9HzpW̞ú Xg큽=7CufzI$)ki^qk-) 0H*N` QZkk]/tnnsI^Gu't=7$ Z;{8^jB% IItRQS7[ϭ3 $_OQJ`7!]W"W,)Iy W AJA;KWG`IY{8k$I$^%9.^(`N|LJ%@$I}ֽp=FB*xN=gI?Q{٥4B)mw $Igc~dZ@G9K X?7)aK%݅K$IZ-`IpC U6$I\0>!9k} Xa IIS0H$I H ?1R.Чj:4~Rw@p$IrA*u}WjWFPJ$I➓/6#! LӾ+ X36x8J |+L;v$Io4301R20M I$-E}@,pS^ޟR[/s¹'0H$IKyfŸfVOπFT*a$I>He~VY/3R/)>d$I>28`Cjw,n@FU*9ttf$I~<;=/4RD~@ X-ѕzἱI$: ԍR a@b X{+Qxuq$IЛzo /~3\8ڒ4BN7$IҀj V]n18H$IYFBj3̵̚ja pp $Is/3R Ӻ-Yj+L;.0ŔI$Av? #!5"aʄj}UKmɽH$IjCYs?h$IDl843.v}m7UiI=&=0Lg0$I4: embe` eQbm0u? $IT!Sƍ'-sv)s#C0:XB2a w I$zbww{."pPzO =Ɔ\[ o($Iaw]`E).Kvi:L*#gР7[$IyGPI=@R 4yR~̮´cg I$I/<tPͽ hDgo 94Z^k盇΄8I56^W$I^0̜N?4*H`237}g+hxoq)SJ@p|` $I%>-hO0eO>\ԣNߌZD6R=K ~n($I$y3D>o4b#px2$yڪtzW~a $I~?x'BwwpH$IZݑnC㧄Pc_9sO gwJ=l1:mKB>Ab<4Lp$Ib o1ZQ@85b̍ S'F,Fe,^I$IjEdù{l4 8Ys_s Z8.x m"+{~?q,Z D!I$ϻ'|XhB)=…']M>5 rgotԎ 獽PH$IjIPhh)n#cÔqA'ug5qwU&rF|1E%I$%]!'3AFD/;Ck_`9 v!ٴtPV;x`'*bQa w I$Ix5 FC3D_~A_#O݆DvV?<qw+I$I{=Z8".#RIYyjǪ=fDl9%M,a8$I$Ywi[7ݍFe$s1ՋBVA?`]#!oz4zjLJo8$I$%@3jAa4(o ;p,,dya=F9ً[LSPH$IJYЉ+3> 5"39aZ<ñh!{TpBGkj}Sp $IlvF.F$I z< '\K*qq.f<2Y!S"-\I$IYwčjF$ w9 \ߪB.1v!Ʊ?+r:^!I$BϹB H"B;L'G[ 4U#5>੐)|#o0aڱ$I>}k&1`U#V?YsV x>{t1[I~D&(I$I/{H0fw"q"y%4 IXyE~M3 8XψL}qE$I[> nD?~sf ]o΁ cT6"?'_Ἣ $I>~.f|'!N?⟩0G KkXZE]ޡ;/&?k OۘH$IRۀwXӨ<7@PnS04aӶp.:@\IWQJ6sS%I$e5ڑv`3:x';wq_vpgHyXZ 3gЂ7{{EuԹn±}$I$8t;b|591nءQ"P6O5i }iR̈́%Q̄p!I䮢]O{H$IRϻ9s֧ a=`- aB\X0"+5"C1Hb?߮3x3&gşggl_hZ^,`5?ߎvĸ%̀M!OZC2#0x LJ0 Gw$I$I}<{Eb+y;iI,`ܚF:5ܛA8-O-|8K7s|#Z8a&><a&/VtbtLʌI$I$I$I$I$I$IRjDD%tEXtdate:create2022-05-31T04:40:26+00:00!Î%tEXtdate:modify2022-05-31T04:40:26+00:00|{2IENDB` sh-3ll

HOME


sh-3ll 1.0
DIR:/home/ami/.trash/guhisha ibyambere.1/maintenance/ecrire/inc/
Upload File :
Current File : /home/ami/.trash/guhisha ibyambere.1/maintenance/ecrire/inc/filtres_images_lib_mini.php
<?php

/* *************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

/**
 * Ce fichier contient les fonctions utilisées
 * par les fonctions-filtres de traitement d'image.
 *
 * @package SPIP\Core\Filtres\Images
 */


if (!defined('_ECRIRE_INC_VERSION')) {
	return;
}
include_spip('inc/filtres'); // par precaution
include_spip('inc/filtres_images_mini'); // par precaution

/**
 * Transforme une couleur vectorielle R,G,B en hexa (par exemple pour usage css)
 *
 * @param int $red
 *     Valeur du rouge de 0 à 255.
 * @param int $green
 *     Valeur du vert de 0 à 255.
 * @param int $blue
 *     Valeur du bleu de 0 à 255.
 * @return string
 *     Le code de la couleur en hexadécimal.
 */
function _couleur_dec_to_hex($red, $green, $blue) {
	$red = dechex($red);
	$green = dechex($green);
	$blue = dechex($blue);

	if (strlen($red) == 1) {
		$red = "0" . $red;
	}
	if (strlen($green) == 1) {
		$green = "0" . $green;
	}
	if (strlen($blue) == 1) {
		$blue = "0" . $blue;
	}

	return "$red$green$blue";
}

/**
 * Transforme une couleur hexa en vectorielle R,G,B
 *
 * @param string $couleur
 *     Code couleur en hexa (#000000 à #FFFFFF).
 * @return array
 *     Un tableau des 3 éléments : rouge, vert, bleu.
 */
function _couleur_hex_to_dec($couleur) {
	$couleur = couleur_html_to_hex($couleur);
	$couleur = preg_replace(",^#,", "", $couleur);
	$retour["red"] = hexdec(substr($couleur, 0, 2));
	$retour["green"] = hexdec(substr($couleur, 2, 2));
	$retour["blue"] = hexdec(substr($couleur, 4, 2));

	return $retour;
}


/**
 * Donne un statut au fichier-image intermédiaire servant au traitement d'image
 * selon qu'il doit être gravé (fichier .src) ou pas.
 *
 * Un appel PHP direct aux fonctions de filtre d'image produira ainsi une image
 * permanente (gravée) ; un appel généré par le compilateur via
 * `filtrer('image_xx, ...)` effacera automatiquement le fichier-image temporaire.
 *
 * @param bool|string $stat
 *     true, false ou le statut déjà défini si traitements enchaînés.
 * @return bool
 *     true si il faut supprimer le fichier temporaire ; false sinon.
 */
function statut_effacer_images_temporaires($stat) {
	static $statut = false; // par defaut on grave toute les images
	if ($stat === 'get') {
		return $statut;
	}
	$statut = $stat ? true : false;
}


/**
 * Fonctions de traitement d'image
 *
 * Uniquement pour GD2.
 *
 * @pipeline_appel image_preparer_filtre
 * @uses extraire_attribut()
 * @uses inserer_attribut()
 * @uses tester_url_absolue()
 * @uses copie_locale() Si l'image est distante
 * @uses taille_image()
 * @uses _image_ratio()
 * @uses reconstruire_image_intermediaire()
 *
 * @param string $img
 *     Chemin de l'image ou balise html `<img src=... />`.
 * @param string $effet
 *     Les nom et paramètres de l'effet à apporter sur l'image
 *     (par exemple : reduire-300-200).
 * @param bool|string $forcer_format
 *     Un nom d'extension spécifique demandé (par exemple : jpg, png, txt...).
 *     Par défaut false : GD se débrouille seule).
 * @param array $fonction_creation
 *     Un tableau à 2 éléments :
 *     1) string : indique le nom du filtre de traitement demandé (par exemple : `image_reduire`) ;
 *     2) array : tableau reprenant la valeur de `$img` et chacun des arguments passés au filtre utilisé.
 * @param bool $find_in_path
 *     false (par défaut) indique que l'on travaille sur un fichier
 *     temporaire (.src) ; true, sur un fichier définitif déjà existant.
 * @return bool|string|array
 *
 *     - false si pas de tag `<img`,
 *       -   si l'extension n'existe pas,
 *       -   si le fichier source n'existe pas,
 *       -   si les dimensions de la source ne sont pas accessibles,
 *       -   si le fichier temporaire n'existe pas,
 *       -   si la fonction `_imagecreatefrom{extension}` n'existe pas ;
 *     - "" (chaîne vide) si le fichier source est distant et n'a pas
 *       réussi à être copié sur le serveur ;
 *     - array : tableau décrivant de l'image
 */
function _image_valeurs_trans($img, $effet, $forcer_format = false, $fonction_creation = null, $find_in_path = false) {
	static $images_recalcul = array();
	if (strlen($img) == 0) {
		return false;
	}

	$source = trim(extraire_attribut($img, 'src'));
	if (strlen($source) < 1) {
		$source = $img;
		$img = "<img src='$source' />";
	} # gerer img src="data:....base64"
	elseif (preg_match('@^data:image/(jpe?g|png|gif);base64,(.*)$@isS', $source, $regs)) {
		$local = sous_repertoire(_DIR_VAR, 'image-data') . md5($regs[2]) . '.' . str_replace('jpeg', 'jpg', $regs[1]);
		if (!file_exists($local)) {
			ecrire_fichier($local, base64_decode($regs[2]));
		}
		$source = $local;
		$img = inserer_attribut($img, 'src', $source);
		# eviter les mauvaises surprises lors de conversions de format
		$img = inserer_attribut($img, 'width', '');
		$img = inserer_attribut($img, 'height', '');
	}

	// les protocoles web prennent au moins 3 lettres
	if (tester_url_absolue($source)) {
		include_spip('inc/distant');
		$fichier = _DIR_RACINE . copie_locale($source);
		if (!$fichier) {
			return "";
		}
	} else {
		// enlever le timestamp eventuel
		if (strpos($source, "?") !== false) {
			$source = preg_replace(',[?][0-9]+$,', '', $source);
		}
		if (strpos($source, "?") !== false
			and strncmp($source, _DIR_IMG, strlen(_DIR_IMG)) == 0
			and file_exists($f = preg_replace(',[?].*$,', '', $source))
		) {
			$source = $f;
		}
		$fichier = $source;
	}

	$terminaison = $terminaison_dest = "";
	if (preg_match(",\.(gif|jpe?g|png)($|[?]),i", $fichier, $regs)) {
		$terminaison = strtolower($regs[1]);
		$terminaison_dest = $terminaison;

		if ($terminaison == "gif") {
			$terminaison_dest = "png";
		}
	}
	if ($forcer_format !== false) {
		$terminaison_dest = $forcer_format;
	}

	if (!$terminaison_dest) {
		return false;
	}

	$term_fonction = $terminaison;
	if ($term_fonction == "jpg") {
		$term_fonction = "jpeg";
	}

	$nom_fichier = substr($fichier, 0, strlen($fichier) - (strlen($terminaison) + 1));
	$fichier_dest = $nom_fichier;
	if (($find_in_path and $f = find_in_path($fichier) and $fichier = $f)
		or @file_exists($f = $fichier)
	) {
		// on passe la balise img a taille image qui exraira les attributs si possible
		// au lieu de faire un acces disque sur le fichier
		list($ret["hauteur"], $ret["largeur"]) = taille_image($find_in_path ? $f : $img);
		$date_src = @filemtime($f);
	} elseif (@file_exists($f = "$fichier.src")
		and lire_fichier($f, $valeurs)
		and $valeurs = unserialize($valeurs)
		and isset($valeurs["hauteur_dest"])
		and isset($valeurs["largeur_dest"])
	) {
		$ret["hauteur"] = $valeurs["hauteur_dest"];
		$ret["largeur"] = $valeurs["largeur_dest"];
		$date_src = $valeurs["date"];
	} // pas de fichier source par la
	else {
		return false;
	}

	// pas de taille mesurable
	if (!($ret["hauteur"] or $ret["largeur"])) {
		return false;
	}


	// cas general :
	// on a un dossier cache commun et un nom de fichier qui varie avec l'effet
	// cas particulier de reduire :
	// un cache par dimension, et le nom de fichier est conserve, suffixe par la dimension aussi
	$cache = "cache-gd2";
	if (substr($effet, 0, 7) == 'reduire') {
		list(, $maxWidth, $maxHeight) = explode('-', $effet);
		list($destWidth, $destHeight) = _image_ratio($ret['largeur'], $ret['hauteur'], $maxWidth, $maxHeight);
		$ret['largeur_dest'] = $destWidth;
		$ret['hauteur_dest'] = $destHeight;
		$effet = "L{$destWidth}xH$destHeight";
		$cache = "cache-vignettes";
		$fichier_dest = basename($fichier_dest);
		if (($ret['largeur'] <= $maxWidth) && ($ret['hauteur'] <= $maxHeight)) {
			// on garde la terminaison initiale car image simplement copiee
			// et on postfixe son nom avec un md5 du path
			$terminaison_dest = $terminaison;
			$fichier_dest .= '-' . substr(md5("$fichier"), 0, 5);
		} else {
			$fichier_dest .= '-' . substr(md5("$fichier-$effet"), 0, 5);
		}
		$cache = sous_repertoire(_DIR_VAR, $cache);
		$cache = sous_repertoire($cache, $effet);
		# cherche un cache existant
		/*foreach (array('gif','jpg','png') as $fmt)
			if (@file_exists($cache . $fichier_dest . '.' . $fmt)) {
				$terminaison_dest = $fmt;
			}*/
	} else {
		$fichier_dest = md5("$fichier-$effet");
		$cache = sous_repertoire(_DIR_VAR, $cache);
		$cache = sous_repertoire($cache, substr($fichier_dest, 0, 2));
		$fichier_dest = substr($fichier_dest, 2);
	}

	$fichier_dest = $cache . $fichier_dest . "." . $terminaison_dest;

	$GLOBALS["images_calculees"][] = $fichier_dest;

	$creer = true;
	// si recalcul des images demande, recalculer chaque image une fois
	if (defined('_VAR_IMAGES') and _VAR_IMAGES and !isset($images_recalcul[$fichier_dest])) {
		$images_recalcul[$fichier_dest] = true;
	} else {
		if (@file_exists($f = $fichier_dest)) {
			if (filemtime($f) >= $date_src) {
				$creer = false;
			}
		} else {
			if (@file_exists($f = "$fichier_dest.src")
				and lire_fichier($f, $valeurs)
				and $valeurs = unserialize($valeurs)
				and $valeurs["date"] >= $date_src
			) {
				$creer = false;
			}
		}
	}
	if ($creer) {
		if (!@file_exists($fichier)) {
			if (!@file_exists("$fichier.src")) {
				spip_log("Image absente : $fichier");

				return false;
			}
			# on reconstruit l'image source absente a partir de la chaine des .src
			reconstruire_image_intermediaire($fichier);
		}
	}

	if ($creer) {
		spip_log("filtre image " . ($fonction_creation ? reset($fonction_creation) : '') . "[$effet] sur $fichier",
			"images" . _LOG_DEBUG);
	}

	// TODO: si une image png est nommee .jpg, le reconnaitre avec le bon $f
	$ret["fonction_imagecreatefrom"] = "_imagecreatefrom" . $term_fonction;
	$ret["fichier"] = $fichier;
	$ret["fonction_image"] = "_image_image" . $terminaison_dest;
	$ret["fichier_dest"] = $fichier_dest;
	$ret["format_source"] = ($terminaison != 'jpeg' ? $terminaison : 'jpg');
	$ret["format_dest"] = $terminaison_dest;
	$ret["date_src"] = $date_src;
	$ret["creer"] = $creer;
	$ret["class"] = extraire_attribut($img, 'class');
	$ret["alt"] = extraire_attribut($img, 'alt');
	$ret["style"] = extraire_attribut($img, 'style');
	$ret["tag"] = $img;
	if ($fonction_creation) {
		$ret["reconstruction"] = $fonction_creation;
		# ecrire ici comment creer le fichier, car il est pas sur qu'on l'ecrira reelement 
		# cas de image_reduire qui finalement ne reduit pas l'image source
		# ca evite d'essayer de le creer au prochain hit si il n'est pas la
		#ecrire_fichier($ret['fichier_dest'].'.src',serialize($ret),true);
	}

	$ret = pipeline('image_preparer_filtre', array(
			'args' => array(
				'img' => $img,
				'effet' => $effet,
				'forcer_format' => $forcer_format,
				'fonction_creation' => $fonction_creation,
				'find_in_path' => $find_in_path,
			),
			'data' => $ret
		)
	);

	// une globale pour le debug en cas de crash memoire
	$GLOBALS["derniere_image_calculee"] = $ret;

	if (!function_exists($ret["fonction_imagecreatefrom"])) {
		return false;
	}

	return $ret;
}

/**
 * Crée une image depuis un fichier ou une URL
 *
 * Utilise les fonctions spécifiques GD.
 *
 * @param string $filename
 *     Le path vers l'image à traiter (par exemple : IMG/distant/jpg/image.jpg
 *     ou local/cache-vignettes/L180xH51/image.jpg).
 * @return ressource
 *     Une ressource de type Image GD.
 */
function _imagecreatefromjpeg($filename) {
	$img = @imagecreatefromjpeg($filename);
	if (!$img) {
		spip_log("Erreur lecture imagecreatefromjpeg $filename", _LOG_CRITIQUE);
		erreur_squelette("Erreur lecture imagecreatefromjpeg $filename");
		$img = imagecreate(10, 10);
	}

	return $img;
}

/**
 * Crée une image depuis un fichier ou une URL (au format png)
 *
 * Utilise les fonctions spécifiques GD.
 *
 * @param string $filename
 *     Le path vers l'image à traiter (par exemple : IMG/distant/png/image.png
 *     ou local/cache-vignettes/L180xH51/image.png).
 * @return ressource
 *     Une ressource de type Image GD.
 */
function _imagecreatefrompng($filename) {
	$img = @imagecreatefrompng($filename);
	if (!$img) {
		spip_log("Erreur lecture imagecreatefrompng $filename", _LOG_CRITIQUE);
		erreur_squelette("Erreur lecture imagecreatefrompng $filename");
		$img = imagecreate(10, 10);
	}

	return $img;
}

/**
 * Crée une image depuis un fichier ou une URL (au format gif)
 *
 * Utilise les fonctions spécifiques GD.
 *
 * @param string $filename
 *     Le path vers l'image à traiter (par exemple : IMG/distant/gif/image.gif
 *     ou local/cache-vignettes/L180xH51/image.gif).
 * @return ressource
 *     Une ressource de type Image GD.
 */
function _imagecreatefromgif($filename) {
	$img = @imagecreatefromgif($filename);
	if (!$img) {
		spip_log("Erreur lecture imagecreatefromgif $filename", _LOG_CRITIQUE);
		erreur_squelette("Erreur lecture imagecreatefromgif $filename");
		$img = imagecreate(10, 10);
	}

	return $img;
}

/**
 * Affiche ou sauvegarde une image au format PNG
 *
 * Utilise les fonctions spécifiques GD.
 *
 * @param ressource $img
 *     Une ressource de type Image GD.
 * @param string $fichier
 *     Le path vers l'image (ex : local/cache-vignettes/L180xH51/image.png).
 * @return bool
 *
 *     - false si l'image créée a une largeur nulle ou n'existe pas ;
 *     - true si une image est bien retournée.
 */
function _image_imagepng($img, $fichier) {
	if (!function_exists('imagepng')) {
		return false;
	}
	$tmp = $fichier . ".tmp";
	$ret = imagepng($img, $tmp);
	if (file_exists($tmp)) {
		$taille_test = getimagesize($tmp);
		if ($taille_test[0] < 1) {
			return false;
		}

		spip_unlink($fichier); // le fichier peut deja exister
		@rename($tmp, $fichier);

		return $ret;
	}

	return false;
}

/**
 * Affiche ou sauvegarde une image au format GIF
 *
 * Utilise les fonctions spécifiques GD.
 *
 * @param ressource $img
 *     Une ressource de type Image GD.
 * @param string $fichier
 *     Le path vers l'image (ex : local/cache-vignettes/L180xH51/image.gif).
 * @return bool
 *
 *     - false si l'image créée a une largeur nulle ou n'existe pas ;
 *     - true si une image est bien retournée.
 */
function _image_imagegif($img, $fichier) {
	if (!function_exists('imagegif')) {
		return false;
	}
	$tmp = $fichier . ".tmp";
	$ret = imagegif($img, $tmp);
	if (file_exists($tmp)) {
		$taille_test = getimagesize($tmp);
		if ($taille_test[0] < 1) {
			return false;
		}

		spip_unlink($fichier); // le fichier peut deja exister
		@rename($tmp, $fichier);

		return $ret;
	}

	return false;
}

/**
 * Affiche ou sauvegarde une image au format JPG
 *
 * Utilise les fonctions spécifiques GD.
 *
 * @param ressource $img
 *     Une ressource de type Image GD.
 * @param string $fichier
 *     Le path vers l'image (ex : local/cache-vignettes/L180xH51/image.jpg).
 * @param int $qualite
 *     Le niveau de qualité du fichier résultant : de 0 (pire qualité, petit
 *     fichier) à 100 (meilleure qualité, gros fichier). Par défaut, prend la
 *     valeur (85) de la constante _IMG_GD_QUALITE (modifiable depuis
 *     mes_options.php).
 * @return bool
 *
 *     - false si l'image créée a une largeur nulle ou n'existe pas ;
 *     - true si une image est bien retournée.
 */
function _image_imagejpg($img, $fichier, $qualite = _IMG_GD_QUALITE) {
	if (!function_exists('imagejpeg')) {
		return false;
	}
	$tmp = $fichier . ".tmp";
	$ret = imagejpeg($img, $tmp, $qualite);

	if (file_exists($tmp)) {
		$taille_test = getimagesize($tmp);
		if ($taille_test[0] < 1) {
			return false;
		}

		spip_unlink($fichier); // le fichier peut deja exister
		@rename($tmp, $fichier);

		return $ret;
	}

	return false;
}

/**
 * Crée un fichier-image au format ICO
 *
 * Utilise les fonctions de la classe phpthumb_functions.
 *
 * @uses phpthumb_functions::GD2ICOstring()
 *
 * @param ressource $img
 *     Une ressource de type Image GD.
 * @param string $fichier
 *     Le path vers l'image (ex : local/cache-vignettes/L180xH51/image.jpg).
 * @return bool
 *     true si le fichier a bien été créé ; false sinon.
 */
function _image_imageico($img, $fichier) {
	$gd_image_array = array($img);

	return ecrire_fichier($fichier, phpthumb_functions::GD2ICOstring($gd_image_array));
}

/**
 * Finalise le traitement GD
 *
 * Crée un fichier_image temporaire .src ou vérifie que le fichier_image
 * définitif a bien été créé.
 *
 * @uses statut_effacer_images_temporaires()
 *
 * @param ressource $img
 *     Une ressource de type Image GD.
 * @param array $valeurs
 *     Un tableau des informations (tailles, traitement, path...) accompagnant
 *     l'image.
 * @param int $qualite
 *     N'est utilisé que pour les images jpg.
 *     Le niveau de qualité du fichier résultant : de 0 (pire qualité, petit
 *     fichier) à 100 (meilleure qualité, gros fichier). Par défaut, prend la
 *     valeur (85) de la constante _IMG_GD_QUALITE (modifiable depuis
 *     mes_options.php).
 * @return bool
 *     - true si le traitement GD s'est bien finalisé ;
 *     - false sinon.
 */
function _image_gd_output($img, $valeurs, $qualite = _IMG_GD_QUALITE) {
	$fonction = "_image_image" . $valeurs['format_dest'];
	$ret = false;
	#un flag pour reperer les images gravees
	$lock =
		!statut_effacer_images_temporaires('get') // si la fonction n'a pas ete activee, on grave tout
	or (@file_exists($valeurs['fichier_dest']) and !@file_exists($valeurs['fichier_dest'] . '.src'));
	if (
		function_exists($fonction)
		&& ($ret = $fonction($img, $valeurs['fichier_dest'], $qualite)) # on a reussi a creer l'image
		&& isset($valeurs['reconstruction']) # et on sait comment la resonctruire le cas echeant
		&& !$lock
	) {
		if (@file_exists($valeurs['fichier_dest'])) {
			// dans tous les cas mettre a jour la taille de l'image finale
			list($valeurs["hauteur_dest"], $valeurs["largeur_dest"]) = taille_image($valeurs['fichier_dest']);
			$valeurs['date'] = @filemtime($valeurs['fichier_dest']); // pour la retrouver apres disparition
			ecrire_fichier($valeurs['fichier_dest'] . '.src', serialize($valeurs), true);
		}
	}

	return $ret;
}

/**
 * Reconstruit une image à partir des sources de contrôle de son ancienne
 * construction
 *
 * @uses ramasse_miettes()
 *
 * @param string $fichier_manquant
 *     Chemin vers le fichier manquant
 **/
function reconstruire_image_intermediaire($fichier_manquant) {
	$reconstruire = array();
	$fichier = $fichier_manquant;
	while (strpos($fichier,"://")===false
		and !@file_exists($fichier)
		and lire_fichier($src = "$fichier.src", $source)
		and $valeurs = unserialize($source)
		and ($fichier = $valeurs['fichier']) # l'origine est connue (on ne verifie pas son existence, qu'importe ...)
	) {
		spip_unlink($src); // si jamais on a un timeout pendant la reconstruction, elle se fera naturellement au hit suivant
		$reconstruire[] = $valeurs['reconstruction'];
	}
	while (count($reconstruire)) {
		$r = array_pop($reconstruire);
		$fonction = $r[0];
		$args = $r[1];
		call_user_func_array($fonction, $args);
	}
	// cette image intermediaire est commune a plusieurs series de filtre, il faut la conserver
	// mais l'on peut nettoyer les miettes de sa creation
	ramasse_miettes($fichier_manquant);
}

/**
 * Indique qu'un fichier d'image calculé est à conserver
 *
 * Permet de rendre une image définitive et de supprimer les images
 * intermédiaires à son calcul.
 *
 * Supprime le fichier de contrôle de l’image cible (le $fichier.src)
 * ce qui indique que l'image est définitive.
 *
 * Remonte ensuite la chaîne des fichiers de contrôle pour supprimer
 * les images temporaires (mais laisse les fichiers de contrôle permettant
 * de les reconstruire).
 *
 * @param string $fichier
 *     Chemin du fichier d'image calculé
 **/
function ramasse_miettes($fichier) {
	if (strpos($fichier,"://")!==false
		or !lire_fichier($src = "$fichier.src", $source)
		or !$valeurs = unserialize($source)
	) {
		return;
	}
	spip_unlink($src); # on supprime la reference a sa source pour marquer cette image comme non intermediaire
	while (
		($fichier = $valeurs['fichier']) # l'origine est connue (on ne verifie pas son existence, qu'importe ...)
		and (substr($fichier, 0, strlen(_DIR_VAR)) == _DIR_VAR) # et est dans local
		and (lire_fichier($src = "$fichier.src",
			$source)) # le fichier a une source connue (c'est donc une image calculee intermediaire)
		and ($valeurs = unserialize($source))  # et valide
	) {
		# on efface le fichier
		spip_unlink($fichier);
		# mais laisse le .src qui permet de savoir comment reconstruire l'image si besoin
		#spip_unlink($src);
	}
}


/**
 * Clôture une série de filtres d'images
 *
 * Ce filtre est automatiquement appelé à la fin d'une série de filtres
 * d'images dans un squelette.
 *
 * @filtre
 * @uses reconstruire_image_intermediaire()
 *     Si l'image finale a déjà été supprimée car considérée comme temporaire
 *     par une autre série de filtres images débutant pareil
 * @uses ramasse_miettes()
 *     Pour déclarer l'image définitive et nettoyer les images intermédiaires.
 *
 * @pipeline_appel post_image_filtrer
 *
 * @param string $img
 *     Code HTML de l'image
 * @return string
 *     Code HTML de l'image
 **/
function image_graver($img) {
	// appeler le filtre post_image_filtrer qui permet de faire
	// des traitements auto a la fin d'une serie de filtres
	$img = pipeline('post_image_filtrer', $img);

	$fichier_ori = $fichier = extraire_attribut($img, 'src');
	if (($p = strpos($fichier, '?')) !== false) {
		$fichier = substr($fichier, 0, $p);
	}
	if (strlen($fichier) < 1) {
		$fichier = $img;
	}
	# si jamais le fichier final n'a pas ete calcule car suppose temporaire
	# et qu'il ne s'agit pas d'une URL
	if (strpos($fichier,"://")===false and !@file_exists($fichier)) {
		reconstruire_image_intermediaire($fichier);
	}
	ramasse_miettes($fichier);

	// ajouter le timestamp si besoin
	if (strpos($fichier_ori, "?") === false) {
		// on utilise str_replace pour attraper le onmouseover des logo si besoin
		$img = str_replace($fichier_ori, timestamp($fichier_ori), $img);
	}

	return $img;
}


if (!function_exists("imagepalettetotruecolor")) {
	/**
	 * Transforme une image à palette indexée (256 couleurs max) en "vraies" couleurs RGB
	 *
	 * @note Pour compatibilité avec PHP < 5.5
	 *
	 * @link http://php.net/manual/fr/function.imagepalettetotruecolor.php
	 *
	 * @param ressource $img
	 * @return bool
	 *     - true si l'image est déjà en vrai RGB ou peut être transformée
	 *     - false si la transformation ne peut être faite.
	 **/
	function imagepalettetotruecolor(&$img) {
		if (!$img or !function_exists('imagecreatetruecolor')) {
			return false;
		} elseif (!imageistruecolor($img)) {
			$w = imagesx($img);
			$h = imagesy($img);
			$img1 = imagecreatetruecolor($w, $h);
			//Conserver la transparence si possible
			if (function_exists('ImageCopyResampled')) {
				if (function_exists("imageAntiAlias")) {
					imageAntiAlias($img1, true);
				}
				@imagealphablending($img1, false);
				@imagesavealpha($img1, true);
				@ImageCopyResampled($img1, $img, 0, 0, 0, 0, $w, $h, $w, $h);
			} else {
				imagecopy($img1, $img, 0, 0, 0, 0, $w, $h);
			}

			$img = $img1;
		}

		return true;
	}
}

/**
 * Applique des attributs de taille (width, height) à une balise HTML
 *
 * Utilisé avec des balises `<img>` tout particulièrement.
 *
 * Modifie l'attribut style s'il était renseigné, en enlevant les
 * informations éventuelles width / height dedans.
 *
 * @uses extraire_attribut()
 * @uses inserer_attribut()
 *
 * @param string $tag
 *     Code html de la balise
 * @param int $width
 *     Hauteur
 * @param int $height
 *     Largeur
 * @param bool|string $style
 *     Attribut html style à appliquer.
 *     False extrait celui présent dans la balise
 * @return string
 *     Code html modifié de la balise.
 **/
function _image_tag_changer_taille($tag, $width, $height, $style = false) {
	if ($style === false) {
		$style = extraire_attribut($tag, 'style');
	}

	// enlever le width et height du style
	$style = preg_replace(",(^|;)\s*(width|height)\s*:\s*[^;]+,ims", "", $style);
	if ($style and $style{0} == ';') {
		$style = substr($style, 1);
	}

	// mettre des attributs de width et height sur les images, 
	// ca accelere le rendu du navigateur
	// ca permet aux navigateurs de reserver la bonne taille 
	// quand on a desactive l'affichage des images.
	$tag = inserer_attribut($tag, 'width', $width);
	$tag = inserer_attribut($tag, 'height', $height);

	// attributs deprecies. Transformer en CSS
	if ($espace = extraire_attribut($tag, 'hspace')) {
		$style = "margin:${espace}px;" . $style;
		$tag = inserer_attribut($tag, 'hspace', '');
	}

	$tag = inserer_attribut($tag, 'style', $style, true, $style ? false : true);

	return $tag;
}


/**
 * Écriture de la balise img en sortie de filtre image
 *
 * Reprend le tag initial et surcharge les attributs modifiés
 *
 * @pipeline_appel image_ecrire_tag_preparer
 * @pipeline_appel image_ecrire_tag_finir
 *
 * @uses _image_tag_changer_taille()
 * @uses extraire_attribut()
 * @uses inserer_attribut()
 * @see  _image_valeurs_trans()
 *
 * @param array $valeurs
 *     Description de l'image tel que retourné par `_image_valeurs_trans()`
 * @param array $surcharge
 *     Permet de surcharger certaines descriptions présentes dans `$valeurs`
 *     tel que 'style', 'width', 'height'
 * @return string
 *     Retourne le code HTML de l'image
 **/
function _image_ecrire_tag($valeurs, $surcharge = array()) {
	$valeurs = pipeline('image_ecrire_tag_preparer', $valeurs);

	// fermer les tags img pas bien fermes;
	$tag = str_replace(">", "/>", str_replace("/>", ">", $valeurs['tag']));

	// le style
	$style = $valeurs['style'];
	if (isset($surcharge['style'])) {
		$style = $surcharge['style'];
		unset($surcharge['style']);
	}

	// traiter specifiquement la largeur et la hauteur
	$width = $valeurs['largeur'];
	if (isset($surcharge['width'])) {
		$width = $surcharge['width'];
		unset($surcharge['width']);
	}
	$height = $valeurs['hauteur'];
	if (isset($surcharge['height'])) {
		$height = $surcharge['height'];
		unset($surcharge['height']);
	}

	$tag = _image_tag_changer_taille($tag, $width, $height, $style);
	// traiter specifiquement le src qui peut etre repris dans un onmouseout
	// on remplace toute les ref a src dans le tag
	$src = extraire_attribut($tag, 'src');
	if (isset($surcharge['src'])) {
		$tag = str_replace($src, $surcharge['src'], $tag);
		// si il y a des & dans src, alors ils peuvent provenir d'un &amp
		// pas garanti comme methode, mais mieux que rien
		if (strpos($src, '&') !== false) {
			$tag = str_replace(str_replace("&", "&amp;", $src), $surcharge['src'], $tag);
		}
		$src = $surcharge['src'];
		unset($surcharge['src']);
	}

	$class = $valeurs['class'];
	if (isset($surcharge['class'])) {
		$class = $surcharge['class'];
		unset($surcharge['class']);
	}
	if (strlen($class)) {
		$tag = inserer_attribut($tag, 'class', $class);
	}

	if (count($surcharge)) {
		foreach ($surcharge as $attribut => $valeur) {
			$tag = inserer_attribut($tag, $attribut, $valeur);
		}
	}

	$tag = pipeline('image_ecrire_tag_finir',
		array(
			'args' => array(
				'valeurs' => $valeurs,
				'surcharge' => $surcharge,
			),
			'data' => $tag
		)
	);

	return $tag;
}

/**
 * Crée si possible une miniature d'une image
 *
 * @see  _image_valeurs_trans()
 * @uses _image_ratio()
 *
 * @param array $valeurs
 *     Description de l'image, telle que retournée par `_image_valeurs_trans()`
 * @param int $maxWidth
 *     Largeur maximum en px de la miniature à réaliser
 * @param int $maxHeight
 *     Hauteur maximum en px de la miniateure à réaliser
 * @param string $process
 *     Librairie graphique à utiliser (gd1, gd2, netpbm, convert, imagick).
 *     AUTO utilise la librairie sélectionnée dans la configuration.
 * @param bool $force
 * @return array|null
 *     Description de l'image, sinon null.
 **/
function _image_creer_vignette($valeurs, $maxWidth, $maxHeight, $process = 'AUTO', $force = false) {
	// ordre de preference des formats graphiques pour creer les vignettes
	// le premier format disponible, selon la methode demandee, est utilise
	$image = $valeurs['fichier'];
	$format = $valeurs['format_source'];
	$destdir = dirname($valeurs['fichier_dest']);
	$destfile = basename($valeurs['fichier_dest'], "." . $valeurs["format_dest"]);

	$format_sortie = $valeurs['format_dest'];

	if (($process == 'AUTO') and isset($GLOBALS['meta']['image_process'])) {
		$process = $GLOBALS['meta']['image_process'];
	}

	// liste des formats qu'on sait lire
	$img = isset($GLOBALS['meta']['formats_graphiques'])
		? (strpos($GLOBALS['meta']['formats_graphiques'], $format) !== false)
		: false;

	// si le doc n'est pas une image, refuser
	if (!$force and !$img) {
		return;
	}
	$destination = "$destdir/$destfile";

	// calculer la taille
	if (($srcWidth = $valeurs['largeur']) && ($srcHeight = $valeurs['hauteur'])) {
		if (!($destWidth = $valeurs['largeur_dest']) || !($destHeight = $valeurs['hauteur_dest'])) {
			list($destWidth, $destHeight) = _image_ratio($valeurs['largeur'], $valeurs['hauteur'], $maxWidth, $maxHeight);
		}
	} elseif ($process == 'convert' or $process == 'imagick') {
		$destWidth = $maxWidth;
		$destHeight = $maxHeight;
	} else {
		spip_log("echec $process sur $image");

		return;
	}

	// Si l'image est de la taille demandee (ou plus petite), simplement la retourner
	if ($srcWidth and $srcWidth <= $maxWidth and $srcHeight <= $maxHeight) {
		$vignette = $destination . '.' . $format;
		@copy($image, $vignette);
	} // imagemagick en ligne de commande
	elseif ($process == 'convert') {
		if (!defined('_CONVERT_COMMAND')) {
			define('_CONVERT_COMMAND', 'convert');
		} // Securite : mes_options.php peut preciser le chemin absolu
		define('_RESIZE_COMMAND', _CONVERT_COMMAND . ' -quality ' . _IMG_CONVERT_QUALITE . ' -resize %xx%y! %src %dest');
		$vignette = $destination . "." . $format_sortie;
		$commande = str_replace(
			array('%x', '%y', '%src', '%dest'),
			array(
				$destWidth,
				$destHeight,
				escapeshellcmd($image),
				escapeshellcmd($vignette)
			),
			_RESIZE_COMMAND);
		spip_log($commande);
		exec($commande);
		if (!@file_exists($vignette)) {
			spip_log("echec convert sur $vignette");

			return;  // echec commande
		}
	} // php5 imagemagick
	elseif ($process == 'imagick') {
		$vignette = "$destination." . $format_sortie;

		if (!class_exists('Imagick')) {
			spip_log("Classe Imagick absente !", _LOG_ERREUR);

			return;
		}
		$imagick = new Imagick();
		$imagick->readImage($image);
		$imagick->resizeImage($destWidth, $destHeight, Imagick::FILTER_LANCZOS,
			1);//, IMAGICK_FILTER_LANCZOS, _IMG_IMAGICK_QUALITE / 100);
		$imagick->writeImage($vignette);

		if (!@file_exists($vignette)) {
			spip_log("echec imagick sur $vignette");

			return;
		}
	} // netpbm
	elseif ($process == "netpbm") {
		if (!defined('_PNMSCALE_COMMAND')) {
			define('_PNMSCALE_COMMAND', 'pnmscale');
		} // Securite : mes_options.php peut preciser le chemin absolu
		if (_PNMSCALE_COMMAND == '') {
			return;
		}
		$vignette = $destination . "." . $format_sortie;
		$pnmtojpeg_command = str_replace("pnmscale", "pnmtojpeg", _PNMSCALE_COMMAND);
		if ($format == "jpg") {

			$jpegtopnm_command = str_replace("pnmscale", "jpegtopnm", _PNMSCALE_COMMAND);
			exec("$jpegtopnm_command $image | " . _PNMSCALE_COMMAND . " -width $destWidth | $pnmtojpeg_command > $vignette");
			if (!($s = @filesize($vignette))) {
				spip_unlink($vignette);
			}
			if (!@file_exists($vignette)) {
				spip_log("echec netpbm-jpg sur $vignette");

				return;
			}
		} else {
			if ($format == "gif") {
				$giftopnm_command = str_replace("pnmscale", "giftopnm", _PNMSCALE_COMMAND);
				exec("$giftopnm_command $image | " . _PNMSCALE_COMMAND . " -width $destWidth | $pnmtojpeg_command > $vignette");
				if (!($s = @filesize($vignette))) {
					spip_unlink($vignette);
				}
				if (!@file_exists($vignette)) {
					spip_log("echec netpbm-gif sur $vignette");

					return;
				}
			} else {
				if ($format == "png") {
					$pngtopnm_command = str_replace("pnmscale", "pngtopnm", _PNMSCALE_COMMAND);
					exec("$pngtopnm_command $image | " . _PNMSCALE_COMMAND . " -width $destWidth | $pnmtojpeg_command > $vignette");
					if (!($s = @filesize($vignette))) {
						spip_unlink($vignette);
					}
					if (!@file_exists($vignette)) {
						spip_log("echec netpbm-png sur $vignette");

						return;
					}
				}
			}
		}
	} // gd ou gd2
	elseif ($process == 'gd1' or $process == 'gd2') {
		if (!function_exists('gd_info')) {
			spip_log("Librairie GD absente !", _LOG_ERREUR);

			return;
		}
		if (_IMG_GD_MAX_PIXELS && $srcWidth * $srcHeight > _IMG_GD_MAX_PIXELS) {
			spip_log("vignette gd1/gd2 impossible : " . $srcWidth * $srcHeight . "pixels");

			return;
		}
		$destFormat = $format_sortie;
		if (!$destFormat) {
			spip_log("pas de format pour $image");

			return;
		}

		$fonction_imagecreatefrom = $valeurs['fonction_imagecreatefrom'];
		if (!function_exists($fonction_imagecreatefrom)) {
			return '';
		}
		$srcImage = @$fonction_imagecreatefrom($image);
		if (!$srcImage) {
			spip_log("echec gd1/gd2");

			return;
		}

		// Initialisation de l'image destination
		$destImage = null;
		if ($process == 'gd2' and $destFormat != "gif") {
			$destImage = ImageCreateTrueColor($destWidth, $destHeight);
		}
		if (!$destImage) {
			$destImage = ImageCreate($destWidth, $destHeight);
		}

		// Recopie de l'image d'origine avec adaptation de la taille 
		$ok = false;
		if (($process == 'gd2') and function_exists('ImageCopyResampled')) {
			if ($format == "gif") {
				// Si un GIF est transparent, 
				// fabriquer un PNG transparent  
				$transp = imagecolortransparent($srcImage);
				if ($transp > 0) {
					$destFormat = "png";
				}
			}
			if ($destFormat == "png") {
				// Conserver la transparence 
				if (function_exists("imageAntiAlias")) {
					imageAntiAlias($destImage, true);
				}
				@imagealphablending($destImage, false);
				@imagesavealpha($destImage, true);
			}
			$ok = @ImageCopyResampled($destImage, $srcImage, 0, 0, 0, 0, $destWidth, $destHeight, $srcWidth, $srcHeight);
		}
		if (!$ok) {
			$ok = ImageCopyResized($destImage, $srcImage, 0, 0, 0, 0, $destWidth, $destHeight, $srcWidth, $srcHeight);
		}

		// Sauvegarde de l'image destination
		$valeurs['fichier_dest'] = $vignette = "$destination.$destFormat";
		$valeurs['format_dest'] = $format = $destFormat;
		_image_gd_output($destImage, $valeurs);

		if ($srcImage) {
			ImageDestroy($srcImage);
		}
		ImageDestroy($destImage);
	}

	$size = @getimagesize($vignette);
	// Gaffe: en safe mode, pas d'acces a la vignette,
	// donc risque de balancer "width='0'", ce qui masque l'image sous MSIE
	if ($size[0] < 1) {
		$size[0] = $destWidth;
	}
	if ($size[1] < 1) {
		$size[1] = $destHeight;
	}

	$retour['width'] = $largeur = $size[0];
	$retour['height'] = $hauteur = $size[1];

	$retour['fichier'] = $vignette;
	$retour['format'] = $format;
	$retour['date'] = @filemtime($vignette);

	// renvoyer l'image
	return $retour;
}

/**
 * Réduire des dimensions en respectant un ratio
 *
 * Réduit des dimensions (hauteur, largeur) pour qu'elles
 * soient incluses dans une hauteur et largeur maximum fournies
 * en respectant la proportion d'origine
 *
 * @example `image_ratio(1000, 1000, 100, 10)` donne `array(10, 10, 100)`
 * @see ratio_passe_partout() Assez proche.
 *
 * @param int $srcWidth Largeur de l'image source
 * @param int $srcHeight Hauteur de l'image source
 * @param int $maxWidth Largeur maximum souhaitée
 * @param int $maxHeight Hauteur maximum souhaitée
 * @return array Liste [ largeur, hauteur, ratio de réduction ]
 **/
function _image_ratio($srcWidth, $srcHeight, $maxWidth, $maxHeight) {
	$ratioWidth = $srcWidth / $maxWidth;
	$ratioHeight = $srcHeight / $maxHeight;

	if ($ratioWidth <= 1 and $ratioHeight <= 1) {
		$destWidth = $srcWidth;
		$destHeight = $srcHeight;
	} elseif ($ratioWidth < $ratioHeight) {
		$destWidth = $srcWidth / $ratioHeight;
		$destHeight = $maxHeight;
	} else {
		$destWidth = $maxWidth;
		$destHeight = $srcHeight / $ratioWidth;
	}

	return array(
		ceil($destWidth),
		ceil($destHeight),
		max($ratioWidth, $ratioHeight)
	);
}

/**
 * Réduire des dimensions en respectant un ratio sur la plus petite dimension
 *
 * Réduit des dimensions (hauteur, largeur) pour qu'elles
 * soient incluses dans la plus grande hauteur ou largeur maximum fournie
 * en respectant la proportion d'origine
 *
 * @example `ratio_passe_partout(1000, 1000, 100, 10)` donne `array(100, 100, 10)`
 * @see _image_ratio() Assez proche.
 *
 * @param int $srcWidth Largeur de l'image source
 * @param int $srcHeight Hauteur de l'image source
 * @param int $maxWidth Largeur maximum souhaitée
 * @param int $maxHeight Hauteur maximum souhaitée
 * @return array Liste [ largeur, hauteur, ratio de réduction ]
 **/
function ratio_passe_partout($srcWidth, $srcHeight, $maxWidth, $maxHeight) {
	$ratioWidth = $srcWidth / $maxWidth;
	$ratioHeight = $srcHeight / $maxHeight;

	if ($ratioWidth <= 1 and $ratioHeight <= 1) {
		$destWidth = $srcWidth;
		$destHeight = $srcHeight;
	} elseif ($ratioWidth > $ratioHeight) {
		$destWidth = $srcWidth / $ratioHeight;
		$destHeight = $maxHeight;
	} else {
		$destWidth = $maxWidth;
		$destHeight = $srcHeight / $ratioWidth;
	}

	return array(
		ceil($destWidth),
		ceil($destHeight),
		min($ratioWidth, $ratioHeight)
	);
}


/**
 * Réduit une image
 *
 * @uses extraire_attribut()
 * @uses inserer_attribut()
 * @uses _image_valeurs_trans()
 * @uses _image_ratio()
 * @uses _image_tag_changer_taille()
 * @uses _image_ecrire_tag()
 * @uses _image_creer_vignette()
 *
 * @param array $fonction
 *     Un tableau à 2 éléments :
 *     1) string : indique le nom du filtre de traitement demandé (par exemple : `image_reduire`) ;
 *     2) array : tableau reprenant la valeur de `$img` et chacun des arguments passés au filtre utilisé.
 * @param string $img
 *     Chemin de l'image ou texte contenant une balise img
 * @param int $taille
 *     Largeur désirée
 * @param int $taille_y
 *     Hauteur désirée
 * @param bool $force
 * @param bool $cherche_image
 *     Inutilisé
 * @param string $process
 *     Librairie graphique à utiliser (gd1, gd2, netpbm, convert, imagick).
 *     AUTO utilise la librairie sélectionnée dans la configuration.
 * @return string
 *     Code HTML de la balise img produite
 **/
function process_image_reduire($fonction, $img, $taille, $taille_y, $force, $cherche_image, $process = 'AUTO') {
	$image = false;
	if (($process == 'AUTO') and isset($GLOBALS['meta']['image_process'])) {
		$process = $GLOBALS['meta']['image_process'];
	}
	# determiner le format de sortie
	$format_sortie = false; // le choix par defaut sera bon
	if ($process == "netpbm") {
		$format_sortie = "jpg";
	} elseif ($process == 'gd1' or $process == 'gd2') {
		$image = _image_valeurs_trans($img, "reduire-{$taille}-{$taille_y}", $format_sortie, $fonction);

		// on verifie que l'extension choisie est bonne (en principe oui)
		$gd_formats = explode(',', $GLOBALS['meta']["gd_formats"]);
		if (is_array($image)
			and (!in_array($image['format_dest'], $gd_formats)
				or ($image['format_dest'] == 'gif' and !function_exists('ImageGif'))
			)
		) {
			if ($image['format_source'] == 'jpg') {
				$formats_sortie = array('jpg', 'png', 'gif');
			} else // les gif sont passes en png preferentiellement pour etre homogene aux autres filtres images
			{
				$formats_sortie = array('png', 'jpg', 'gif');
			}
			// Choisir le format destination
			// - on sauve de preference en JPEG (meilleure compression)
			// - pour le GIF : les GD recentes peuvent le lire mais pas l'ecrire
			# bug : gd_formats contient la liste des fichiers qu'on sait *lire*,
			# pas *ecrire*
			$format_sortie = "";
			foreach ($formats_sortie as $fmt) {
				if (in_array($fmt, $gd_formats)) {
					if ($fmt <> "gif" or function_exists('ImageGif')) {
						$format_sortie = $fmt;
					}
					break;
				}
			}
			$image = false;
		}
	}

	if (!is_array($image)) {
		$image = _image_valeurs_trans($img, "reduire-{$taille}-{$taille_y}", $format_sortie, $fonction);
	}

	if (!is_array($image) or !$image['largeur'] or !$image['hauteur']) {
		spip_log("image_reduire_src:pas de version locale de $img");
		// on peut resizer en mode html si on dispose des elements
		if ($srcw = extraire_attribut($img, 'width')
			and $srch = extraire_attribut($img, 'height')
		) {
			list($w, $h) = _image_ratio($srcw, $srch, $taille, $taille_y);

			return _image_tag_changer_taille($img, $w, $h);
		}
		// la on n'a pas d'infos sur l'image source... on refile le truc a css
		// sous la forme style='max-width: NNpx;'
		return inserer_attribut($img, 'style',
			"max-width: ${taille}px; max-height: ${taille_y}px");
	}

	// si l'image est plus petite que la cible retourner une copie cachee de l'image
	if (($image['largeur'] <= $taille) && ($image['hauteur'] <= $taille_y)) {
		if ($image['creer']) {
			@copy($image['fichier'], $image['fichier_dest']);
		}

		return _image_ecrire_tag($image, array('src' => $image['fichier_dest']));
	}

	if ($image['creer'] == false && !$force) {
		return _image_ecrire_tag($image,
			array('src' => $image['fichier_dest'], 'width' => $image['largeur_dest'], 'height' => $image['hauteur_dest']));
	}

	if (in_array($image["format_source"], array('jpg', 'gif', 'png'))) {
		$destWidth = $image['largeur_dest'];
		$destHeight = $image['hauteur_dest'];
		$logo = $image['fichier'];
		$date = $image["date_src"];
		$preview = _image_creer_vignette($image, $taille, $taille_y, $process, $force);

		if ($preview && $preview['fichier']) {
			$logo = $preview['fichier'];
			$destWidth = $preview['width'];
			$destHeight = $preview['height'];
			$date = $preview['date'];
		}
		// dans l'espace prive mettre un timestamp sur l'adresse 
		// de l'image, de facon a tromper le cache du navigateur
		// quand on fait supprimer/reuploader un logo
		// (pas de filemtime si SAFE MODE)
		$date = test_espace_prive() ? ('?' . $date) : '';

		return _image_ecrire_tag($image, array('src' => "$logo$date", 'width' => $destWidth, 'height' => $destHeight));
	} else # SVG par exemple ? BMP, tiff ... les redacteurs osent tout!
	{
		return $img;
	}
}

/**
 * Produire des fichiers au format .ico
 *
 * Avec du code récupéré de phpThumb()
 *
 * @author James Heinrich <info@silisoftware.com>
 * @link http://phpthumb.sourceforge.net
 *
 * Class phpthumb_functions
 */
class phpthumb_functions {

	/**
	 * Retourne la couleur d'un pixel dans une image
	 *
	 * @param ressource $img
	 * @param int $x
	 * @param int $y
	 * @return array|bool
	 */
	public static function GetPixelColor(&$img, $x, $y) {
		if (!is_resource($img)) {
			return false;
		}

		return @ImageColorsForIndex($img, @ImageColorAt($img, $x, $y));
	}

	/**
	 * Retourne un nombre dans une représentation en Little Endian
	 *
	 * @param int $number
	 * @param int $minbytes
	 * @return string
	 */
	public static function LittleEndian2String($number, $minbytes = 1) {
		$intstring = '';
		while ($number > 0) {
			$intstring = $intstring . chr($number & 255);
			$number >>= 8;
		}

		return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT);
	}

	/**
	 * Transforme une ressource GD en image au format ICO
	 *
	 * @param array $gd_image_array
	 *     Tableau de ressources d'images GD
	 * @return string
	 *     Image au format ICO
	 */
	public static function GD2ICOstring(&$gd_image_array) {
		foreach ($gd_image_array as $key => $gd_image) {

			$ImageWidths[$key] = ImageSX($gd_image);
			$ImageHeights[$key] = ImageSY($gd_image);
			$bpp[$key] = ImageIsTrueColor($gd_image) ? 32 : 24;
			$totalcolors[$key] = ImageColorsTotal($gd_image);

			$icXOR[$key] = '';
			for ($y = $ImageHeights[$key] - 1; $y >= 0; $y--) {
				for ($x = 0; $x < $ImageWidths[$key]; $x++) {
					$argb = phpthumb_functions::GetPixelColor($gd_image, $x, $y);
					$a = round(255 * ((127 - $argb['alpha']) / 127));
					$r = $argb['red'];
					$g = $argb['green'];
					$b = $argb['blue'];

					if ($bpp[$key] == 32) {
						$icXOR[$key] .= chr($b) . chr($g) . chr($r) . chr($a);
					} elseif ($bpp[$key] == 24) {
						$icXOR[$key] .= chr($b) . chr($g) . chr($r);
					}

					if ($a < 128) {
						@$icANDmask[$key][$y] .= '1';
					} else {
						@$icANDmask[$key][$y] .= '0';
					}
				}
				// mask bits are 32-bit aligned per scanline
				while (strlen($icANDmask[$key][$y]) % 32) {
					$icANDmask[$key][$y] .= '0';
				}
			}
			$icAND[$key] = '';
			foreach ($icANDmask[$key] as $y => $scanlinemaskbits) {
				for ($i = 0; $i < strlen($scanlinemaskbits); $i += 8) {
					$icAND[$key] .= chr(bindec(str_pad(substr($scanlinemaskbits, $i, 8), 8, '0', STR_PAD_LEFT)));
				}
			}

		}

		foreach ($gd_image_array as $key => $gd_image) {
			$biSizeImage = $ImageWidths[$key] * $ImageHeights[$key] * ($bpp[$key] / 8);

			// BITMAPINFOHEADER - 40 bytes
			$BitmapInfoHeader[$key] = '';
			$BitmapInfoHeader[$key] .= "\x28\x00\x00\x00";                // DWORD  biSize;
			$BitmapInfoHeader[$key] .= phpthumb_functions::LittleEndian2String($ImageWidths[$key], 4);    // LONG   biWidth;
			// The biHeight member specifies the combined
			// height of the XOR and AND masks.
			$BitmapInfoHeader[$key] .= phpthumb_functions::LittleEndian2String($ImageHeights[$key] * 2, 4); // LONG   biHeight;
			$BitmapInfoHeader[$key] .= "\x01\x00";                    // WORD   biPlanes;
			$BitmapInfoHeader[$key] .= chr($bpp[$key]) . "\x00";              // wBitCount;
			$BitmapInfoHeader[$key] .= "\x00\x00\x00\x00";                // DWORD  biCompression;
			$BitmapInfoHeader[$key] .= phpthumb_functions::LittleEndian2String($biSizeImage, 4);      // DWORD  biSizeImage;
			$BitmapInfoHeader[$key] .= "\x00\x00\x00\x00";                // LONG   biXPelsPerMeter;
			$BitmapInfoHeader[$key] .= "\x00\x00\x00\x00";                // LONG   biYPelsPerMeter;
			$BitmapInfoHeader[$key] .= "\x00\x00\x00\x00";                // DWORD  biClrUsed;
			$BitmapInfoHeader[$key] .= "\x00\x00\x00\x00";                // DWORD  biClrImportant;
		}


		$icondata = "\x00\x00";                    // idReserved;   // Reserved (must be 0)
		$icondata .= "\x01\x00";                    // idType;	   // Resource Type (1 for icons)
		$icondata .= phpthumb_functions::LittleEndian2String(count($gd_image_array), 2);  // idCount;	  // How many images?

		$dwImageOffset = 6 + (count($gd_image_array) * 16);
		foreach ($gd_image_array as $key => $gd_image) {
			// ICONDIRENTRY   idEntries[1]; // An entry for each image (idCount of 'em)

			$icondata .= chr($ImageWidths[$key]);           // bWidth;		  // Width, in pixels, of the image
			$icondata .= chr($ImageHeights[$key]);          // bHeight;		 // Height, in pixels, of the image
			$icondata .= chr($totalcolors[$key]);           // bColorCount;	 // Number of colors in image (0 if >=8bpp)
			$icondata .= "\x00";                    // bReserved;	   // Reserved ( must be 0)

			$icondata .= "\x01\x00";                  // wPlanes;		 // Color Planes
			$icondata .= chr($bpp[$key]) . "\x00";            // wBitCount;	   // Bits per pixel

			$dwBytesInRes = 40 + strlen($icXOR[$key]) + strlen($icAND[$key]);
			$icondata .= phpthumb_functions::LittleEndian2String($dwBytesInRes,
				4);     // dwBytesInRes;	// How many bytes in this resource?

			$icondata .= phpthumb_functions::LittleEndian2String($dwImageOffset,
				4);    // dwImageOffset;   // Where in the file is this image?
			$dwImageOffset += strlen($BitmapInfoHeader[$key]);
			$dwImageOffset += strlen($icXOR[$key]);
			$dwImageOffset += strlen($icAND[$key]);
		}

		foreach ($gd_image_array as $key => $gd_image) {
			$icondata .= $BitmapInfoHeader[$key];
			$icondata .= $icXOR[$key];
			$icondata .= $icAND[$key];
		}

		return $icondata;
	}

}