From 504ba77654ec5d8ff914858a19420184e5f07a55 Mon Sep 17 00:00:00 2001 From: Luca Cuzzocrea Date: Tue, 26 Sep 2023 19:40:16 +0200 Subject: [PATCH] System info & network --- .gitignore | 2 + assets/fonts/Archivo-VariableFont.ttf | Bin 0 -> 649980 bytes assets/fonts/DejaVuSans.ttf | Bin 0 -> 757076 bytes assets/fonts/DejaVuSerif-Bold.ttf | Bin 0 -> 356088 bytes assets/fonts/DejaVuSerif.ttf | Bin 0 -> 380132 bytes build.sh | 93 + code/assets.cpp | 705 +++ code/assets.h | 49 + code/audio.cpp | 156 + code/audio.h | 47 + code/camera.cpp | 107 + code/camera.h | 38 + code/debug/log_viewer.cpp | 73 + code/debug/log_viewer.h | 20 + code/debug/logger.cpp | 94 + code/debug/logger.h | 59 + code/enginestate.cpp | 39 + code/enginestate.h | 56 + code/file_formats/wavefront.cpp | 1323 ++++ code/file_formats/wavefront.h | 121 + code/gui/gui.cpp | 807 +++ code/gui/gui.h | 195 + code/gui/layout.cpp | 159 + code/gui/layout.h | 57 + code/gui/text_draw.cpp | 548 ++ code/gui/text_draw.h | 18 + code/lib/all.h | 12 + code/lib/bits.h | 26 + code/lib/color.h | 25 + code/lib/ds.cpp | 87 + code/lib/ds.h | 29 + code/lib/event.h | 220 + code/lib/geometry.h | 319 + code/lib/hashing.h | 97 + code/lib/math.h | 1432 +++++ code/lib/memory.h | 10 + code/lib/queue.h | 93 + code/lib/text.cpp | 196 + code/lib/text.h | 22 + code/lib/types.h | 42 + code/linux_platform.cpp | 1092 ++++ code/linux_platform.h | 12 + code/main.cpp | 315 + code/physics/base.h | 17 + code/physics/body.cpp | 28 + code/physics/body.h | 61 + code/physics/collision.cpp | 336 ++ code/physics/collision.h | 35 + code/physics/physics.cpp | 25 + code/physics/physics.h | 16 + code/physics/simulation.cpp | 40 + code/physics/simulation.h | 9 + code/physics/world.cpp | 72 + code/physics/world.h | 23 + code/platform.h | 92 + code/render/2d.cpp | 382 ++ code/render/2d.h | 27 + code/render/gl_helpers.h | 52 + code/render/lights.cpp | 0 code/render/lights.h | 44 + code/render/object.cpp | 79 + code/render/object.h | 64 + code/render/primitives.cpp | 424 ++ code/render/primitives.h | 149 + code/render/render.cpp | 513 ++ code/render/render.h | 90 + code/render/shader.cpp | 193 + code/render/shader.h | 85 + code/render/state.h | 36 + external/cgltf.cpp | 6 + external/cgltf.h | 7050 ++++++++++++++++++++++ external/cgltf_write.h | 1506 +++++ external/stb_image.cpp | 2 + external/stb_image.h | 7987 +++++++++++++++++++++++++ external/stb_include.h | 295 + external/stb_truetype.cpp | 2 + external/stb_truetype.h | 5077 ++++++++++++++++ external/stb_vorbis.cpp | 2 + external/stb_vorbis.h | 5470 +++++++++++++++++ shaders/2d.frag | 38 + shaders/2d.vert | 30 + shaders/environment_map.frag | 13 + shaders/environment_map.vert | 18 + shaders/pbr.frag | 320 + shaders/pbr.vert | 34 + shaders/postprocessing.frag | 23 + shaders/postprocessing.vert | 11 + shaders/shadow_map.frag | 5 + shaders/shadow_map.vert | 1 + 89 files changed, 39577 insertions(+) create mode 100644 .gitignore create mode 100644 assets/fonts/Archivo-VariableFont.ttf create mode 100644 assets/fonts/DejaVuSans.ttf create mode 100644 assets/fonts/DejaVuSerif-Bold.ttf create mode 100644 assets/fonts/DejaVuSerif.ttf create mode 100755 build.sh create mode 100644 code/assets.cpp create mode 100644 code/assets.h create mode 100644 code/audio.cpp create mode 100644 code/audio.h create mode 100644 code/camera.cpp create mode 100644 code/camera.h create mode 100644 code/debug/log_viewer.cpp create mode 100644 code/debug/log_viewer.h create mode 100644 code/debug/logger.cpp create mode 100644 code/debug/logger.h create mode 100644 code/enginestate.cpp create mode 100644 code/enginestate.h create mode 100644 code/file_formats/wavefront.cpp create mode 100644 code/file_formats/wavefront.h create mode 100644 code/gui/gui.cpp create mode 100644 code/gui/gui.h create mode 100644 code/gui/layout.cpp create mode 100644 code/gui/layout.h create mode 100644 code/gui/text_draw.cpp create mode 100644 code/gui/text_draw.h create mode 100644 code/lib/all.h create mode 100644 code/lib/bits.h create mode 100644 code/lib/color.h create mode 100644 code/lib/ds.cpp create mode 100644 code/lib/ds.h create mode 100644 code/lib/event.h create mode 100644 code/lib/geometry.h create mode 100644 code/lib/hashing.h create mode 100644 code/lib/math.h create mode 100644 code/lib/memory.h create mode 100644 code/lib/queue.h create mode 100644 code/lib/text.cpp create mode 100644 code/lib/text.h create mode 100644 code/lib/types.h create mode 100644 code/linux_platform.cpp create mode 100644 code/linux_platform.h create mode 100644 code/main.cpp create mode 100644 code/physics/base.h create mode 100644 code/physics/body.cpp create mode 100644 code/physics/body.h create mode 100644 code/physics/collision.cpp create mode 100644 code/physics/collision.h create mode 100644 code/physics/physics.cpp create mode 100644 code/physics/physics.h create mode 100644 code/physics/simulation.cpp create mode 100644 code/physics/simulation.h create mode 100644 code/physics/world.cpp create mode 100644 code/physics/world.h create mode 100644 code/platform.h create mode 100644 code/render/2d.cpp create mode 100644 code/render/2d.h create mode 100644 code/render/gl_helpers.h create mode 100644 code/render/lights.cpp create mode 100644 code/render/lights.h create mode 100644 code/render/object.cpp create mode 100644 code/render/object.h create mode 100644 code/render/primitives.cpp create mode 100644 code/render/primitives.h create mode 100644 code/render/render.cpp create mode 100644 code/render/render.h create mode 100644 code/render/shader.cpp create mode 100644 code/render/shader.h create mode 100644 code/render/state.h create mode 100644 external/cgltf.cpp create mode 100644 external/cgltf.h create mode 100644 external/cgltf_write.h create mode 100644 external/stb_image.cpp create mode 100644 external/stb_image.h create mode 100644 external/stb_include.h create mode 100644 external/stb_truetype.cpp create mode 100644 external/stb_truetype.h create mode 100644 external/stb_vorbis.cpp create mode 100644 external/stb_vorbis.h create mode 100644 shaders/2d.frag create mode 100644 shaders/2d.vert create mode 100644 shaders/environment_map.frag create mode 100644 shaders/environment_map.vert create mode 100644 shaders/pbr.frag create mode 100644 shaders/pbr.vert create mode 100644 shaders/postprocessing.frag create mode 100644 shaders/postprocessing.vert create mode 100644 shaders/shadow_map.frag create mode 100644 shaders/shadow_map.vert diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..54dc766 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +server_monitor +cache_build diff --git a/assets/fonts/Archivo-VariableFont.ttf b/assets/fonts/Archivo-VariableFont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..620c0d6eec552a5eb956fb1df8dec95f3bd88857 GIT binary patch literal 649980 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 literal 0 HcmV?d00001 diff --git a/assets/fonts/DejaVuSans.ttf b/assets/fonts/DejaVuSans.ttf new file mode 100644 index 0000000000000000000000000000000000000000..e5f7eecce43be41ff0703ed99e1553029b849f14 GIT binary patch literal 757076 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

fI);V1&`p^P@z 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%0Dm&v@4taQGLT-zh3S`AeK1 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,GZiO*k^1+ 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


*Toc{%?n7Na{@W#kyM+}-t@ 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�$)HrBlgX{^OQ>l&R#_}Voc4WraFOKl-NY6s!EJe6+ z5rp+lWeZu?_Nn^T?{4_CjT_y_A_qTh6KM@{A+5Euy)s3jy~DU+>Pg%rsl93iM`VJc z9X==j|7@>BDA>xDXsrw%-`$|T6%CeQgU)ll8mU-a=pA=kCb1S96evjH@=b&aKrhg%%C^u+fi62T(1D` z-muZ8QEPQt9pJ3#mKw{xN+AH33~>pZNyvE$KB<*FXGmfQ5|LzRN0kgUT}V2jZAZc4 zJ4HKlS=)s;T!RruP8@xz<)5~Zbqp@wBNRs?2IFFpc13h{qdxR!R?q)l zn1Bqx{{+?f?>5ZfTx}CjCL@RmZMge~{0Vws`Aec;91^W4QhzdI#&;W3=UEQ4V#4j+Bp6|<&Kx|*Wie2RbWX-~+|TkR`o;2MQo$4x z?-=nPJg7R804Ei&q!r; z^(g|r0E~61*)Np^jFkL4wE=xg{61g8LmODmfmq^y51pOR7!W@HyMNy@ zK|l;8fQWzyh=34+(iOq4O0z}ftLRtB?d1QN+1p$p#Nhu&xVtSgZ(f@>Z`zyfI?nvs zphG-!!_W1Oe&u?nmsjySHPjs^R~qSUSAOYjj&H^9jQo|QLHPp5%F-BcDo!iU;PmM7 z;n#+ITsR`Xz|sAkC%^BRPvBH}V^JyT5ajvj=VNX^i~dG`XkohEs7`oqjzn zm&4t(dcvtZjb29OX*}g|cUmv~01qlpW55G1|1qr_5B@Q&0k`rz@(hlzUSIjOQD5)Y zANO}|@-uV#+~0Zfo76@5ccnpjz@OAL^ry1?6{T@2zshjj_~3?*PQSPQ+#4U4EGtT* zmr;2dPq|c{)>|hkD${^hm9%a=0N*Mv+kjhn9-V)>Ja}EaTh^ZXP;nYhc~+dp6OPa; zK|@7l8t@FBRFuXWUS(+wc;39I3f#)`+@*i_q{$OsA-jJ}tCwB1w4U;=S{{REdfuwZ z?SArj^X`7iscK#}iuNp9#q}2FS7Y#n`$7Df8^7;*=cW&Ly>rV4`55-k6Sk-Q^L*!> zAN;zj47c({85O1RhU3N$FL;KnH|&8sKE^xl54yKc6{m62w|kUfz;V@Iz`bW$H(j}B z8XYgumZZ9DT4nfEEv*69jc@maugk;G|BA~t_y>4*(c&r3s=#sauQGTB|0+u3tsj-8 z^~BG`zpB7R8t|oW0Z40 zd3Afl+oHR5)8M61c13AC^`k0qTzswwo|}CXb*`c`o_M+VT2We0{3=iD4cD8G6~Wi@ zxb*DaX$<>UwY1*)`_FJ)e637g291Dc*tGlRb5-Db^R6m+^fU$?E?HHUSC_A_n^mNv z^0c1%Qh8c~m!5c6p2vW1@ZXSgRpeWhJO+Gk{@+hI_n+6L(^W6${_+@b4P7wqtHL)U z?fv96czr*43_bWad37A`)_DW>s?%9j81f7@O4+Xazx>+pe?9y@Gxw|9-?{16UGFNW z4|k<;v+sAk^Ukl+e9!Vz-NzB!arB1c##^^Cb^7(RLKofMasR`E4=AhhG=`j@1OEWW zuy2*abJL-JgjbccZanx$S`0m|Jde&lT_1$a`v*KLPUEIe0xy*L4{%)kt0=7j&)`W# zX}tBLva|*~Z(dXdZsmFI(!VOw?_oFZ$t%6=s-^Xmch&M3Jk#@5EZckWxhi?Qc~_M@ zo^(`b2i^Fk^WW3|<-X;JMkydzN`GzE%X!6F(PE zDoX1O*INdjc`DXl&ouY6qyI>oxBmV!To+#rU8yV&-U%G4lGf0x`^n?YyZb4pYI$}3 z>+<#XIV;n@%F}x4OXX=R)|blj81M~V8gi~oR^I%tERO--n}?OTFfudfWfX z^LXlasXVQxTwVM(@UKX}4fqCMDog7P zzbbhQ_y*jnl>2XB zxcUE8YzKsof6ug@`d4|{iuJDYJf7`Civy!**(l;zYB+{qy1eVCieTO)V-}53SlAV~q9C^8A8=($Y%T zLyK4n_g)Vz&o|aXJH`|gDC#}eLlZ2pR(D~a5_aul^OI|*lGvn#W2NdnN$ZK-MA)Jv zZ=>}!{5nP`zUN94m9Nf~{wXiv5k(1+%KiqN3mlGXTat4}QX-D8;v1d#E+_5WlQb2e zmX!W^PWh@d@tor87=g!U!J|i{scNd}J?wkOPNZb)C*prrzS#(eDw>^JS`ciF|H$v2}+>&JpRd#j()XiS*%=QN#N6Xi&d?gC0-k@ip%Kdm8ushK*{EJwmvX#rpUp@uZAD>0PhBQtSJ1q-{c5V}Fz9UMH}_gBI)%_M{~n z+t&3>sRk{A4zr0YbQ?l%zv3mU`agS#wfseCu*JNWxD3ERvAnbs$P2HZ>x` zqKzNSBDdPQwf*+=jO`t@4~f}D82ei%mIRZ)*3C)qt>weaE!UQ&zwwZH_;U5XZfB(o z=s90Gz2I>k$L12tP%o>0Lo4}yP3-1SZ9X)MU_+H`BVlR-M}Z>UVJa=%B=>L>6sWYA z4q%^Z#pK(K2uB zm&y*CQ`MH(BZFv15eWBUL)0bPB{@}2bzFL#Ty3sS)wa_vcc#+LquDxojMu>m@}#=* zusHWTq_5T*rpqmlvlM3$ZfSv#D;=;&MkEd(6b9qzxa1J&wddD;Lpy6bb)>#xvEF$? zvP7ON;so!wMCU0_f1~ZBok2nuZTnZ`y2#_&Yl}EN}wXg zX~jlBM5)0RK>o?1{+mdrO(c_@+QfEkVyEcmw4Qd3e+u*;TCYII%?T$)hl99&`X4sg zxcz03O`CS(@3YVJ9~U?ekQ_7z3z;`z!_3FjQe5fL`Vhk9-f_i~;uUTH^)01JJoYUc zJ2>URB%Pfmjo6`iujDusl{^+|lpZ68U)3 zCV696$y;k!$(Xz*P5B)OUGo+R#og^i^A~I>IB|4K!GigVa9yzF=!t?Y3+8*^A0=ht ztYGQi;I8sG=|UO^JM`xLQ!tfIq4Tvdo3t@>zS{Es37Oha8&kkD7tsGRny^0<|II!G z0Z#rEO?}8I^65C1NluMrnJi-*ImI&n6>;Bl7~^mPS>{*{V;q7Wox)j`FR+JwlTCrv zRR7&3gn_cA`0vIy?kIMCS}G#2Kr;p{JB^=H>fQjlIKM7{$nNO%MQIXO(#3&{~ABbL98hFpV#k0!0yd(~aVMY98>gMP34`EsH5 z-R?K{X~UgG(J0)}rA|VzSI^1M+Ndu$x008}S(z$l=OAo|d#Og<){7<~aDw>hi8>B} zE835)*(E$ata)~8H_C(hv>!3@u^03wjD$Z>w_V3%jug9CEC0cE39OFEHY&-79dKet z8br1q`TD9cJwiyc_4yMPI@*wnw)!2O9o#NSmx)pjvd=Iuh1c}}tawglggI3hA#Z#$ zxr@dT#*dC@o!qn@ecFAPy}w=W5#5K|EN{LB&9F6X?PKlLeC(hunRV;pQTyyLj-+Fp zHLZJI9Fl+7u20;m=X%)KS_9FCqneLhnb$efPhPQMcH4%~8W%5RDFGNF_`J*`V0mZ^ z0x=2gg0woc(U9%Uj*ATJYgaW4_(nR~6SQgisxjM)uSc{g5T_nU*6) z^cWLlJZb9_UNd{->{o#UnM_sk@!K;e&F2_gjO;jxogLXdgdK1qMRr@4elf+#17#Fs zB#Vetz=sHx1sc3isFyo(*$C8b<&g+<^ytEK=hy_U(lPZKjXJh$&gct|MVi*>5^7@! zYTEjpPp`cGcza!HbQ{y&Lmkd+9-F=R%)+&_57!uu-^q|4*xAbo>@A_?*sJf zfzG9HAj1QU%K|%Rx5XVb55AGD=gyscb`ZOi-CGYbm3SHx&xb&Q;FW>#1NcqkN{6^IcaJ&r1g*`2tm1y1X|u% zI7O(ACE#e}7It*=($^^??G5cYtx@)$ty3m003O7LKF`9*j|{;nz?QND>|td%WLFP2 z!VXpj`O3hjO><+6u~23|Q4X4H6horV!e z%@H--sOX_@jB_}~?Qq(DE~JPx6PRP%PTd9zBh>Sa@kp~>yKU6!t8elL?~*TcvADGZ zwT*Uwn>{!Oh)Cq6F5;;ZDZTNj_h5X(@vn~$beZOWC!6;eKdGM43dkF+8C_Pfz(L+vHw*{?Pi z3fA5&dW@f3M?4TlZhi^^_BCSfvw5j(oxzAs5pAEkNZPaGe>9M1>desX+ac56I6sPe zojjzO(dvrO^QqjZVLyLD7P)3w*RhkZXb)K)w5;pY(Roed9KR}8ow|5uyYs^Q?3-_L z+c8XgTD0wz!_P_xyC-~hLblxvUpo!>*uNAdwNO_nN3A9t0fc+$5;><{R))gY_=Niy zh^-JxlK69Jjob6ofG*N^>~a2H&Lv5rpQ7MB?_K#qE5b;Y55*TzFN;puUwV(K?QU{r zHyy|pv!%G&NY3y&#``#Kc_YvXv1Q>4{R)-x+;^B_X^V_3O)0_GMwYgl3>UZ}-&e{; zE3i@0%094~Y_oJ4ba>X3fG&(boz08kH1OwYDo>9vqThO^*PqMP#B=0urvD0YaRhu? z?3vyaz@Ou+)=!)zpkK-Ut6ot%OR+eW;z8c0a)lk^p}fUmJQ(JQfJ$VCRQM7WR)5R8 zV&LADp*x=*vBcM8vaamgbIa3%cl96fs;__Gs=hro4X)^wViE72b>zy2IP1E++qq3s zMW&_)I(6fhs$zPeBYAyim)1=bg_@dm>bkQtE~#Q_)&a3ce`3GvB%=xloK4Z!{Xe4z zwEX|jQ#z1qfqdxx$l1;o7qCS;34%9p9kW0`H|g<5F%kzK74{L%tFEW=mSsXlJ_1;2>GoWmCal_{g2z*i#8SC#hZl(Tx@qxPfy+kK@Ps3EjyS5sY-@;Acx` z=ZNbx?$W`=8CvALhO@`xzrpqkopRHf){ixu_qeVl!X>f*t)Jbqxlz%3HR|kUvLr4CBfYwb6?A`f~@g;4BdfC)Xj0o983=^)fVXBCt zU5{TPN@rGrZqFQZ7Na2YcL9LI_LNbfKhYR@hOb1NT7%}V zTxqIPt|UA8T#&1BbHd*jZ0uSw=JcE1Wly85^$wxVu8Wv_!u`BCf{Zq*mDKy zDz_?ezc|R{CZSR!h_l|@3j>!Fx%~}>4E+%UG65&PR>A{>9u~6I*{@z6!cnl9f?nrF zmftK53V*$Ck9VHsLgxH8Btj4De56fNGq4X~&`@?@jL(!S36!?J<@FE0F@Wgw!rkIq8jIo!LB%Jy>jJxJPic1GK1 zl)+Bp)pjm7PK#GL<$NMWJw=fi_VlRyhEb61U1|0AT{7b7okIyP8u%A=>(tz&D9ze--dTR- zXyZ-@FE_nv6|M)Iy5sWrY5aB@9Ng6nTaY2E*-7i`SEXYa;rQltqdjb z$f=|MfR!h&_>FtXQ(@m0|(Tc9FBpcDu{TWoRwtsv)DA2M6Qr2Y?|XGoGLtJe1Woq z-RRSY-T0i0o3U{cnJR3L0Vjy}a1yd?S(1tn(BC{L&jl`iJOZe@FYbnum;X$6d1FULI~{Yy?1iLWC+}GX@O7f= zRfe|H6L+@~JSouqEg#U(QRqK97&s$dXul0yRXw?SO8bH)kS6RcM=yB;>67Wy=wa+e zy+{A|IeEV4#vbHF;LLrv3Sok^0^_9Qq;eZa`p6p`z1Ul%2~E(xD2Mquo6}=sPd4xK z|2>KUD`A`1Xn|)4{6a~EzVSW}`U}tiy${5B?!=g;uy@Iq{k3EC_$za2kuI!8Kl*@{ zJ!g*mALd2Hj|F22){kj`?+wqZ3%yAZGXAG>T~yt(Tz18iPR~-kh~@s@c+ff*^B_(5 z2}`Qp0})lq=>+rclF`M!`>hcUwmAuLF~#YP`65iW?jG)Zy8l`~1hf_oqVODD@3`ye zi{RJ=tG7Sv&9a&veeuTrrUz9DN3tVX$$d(_lP;V}h03N@{%zzEPqzMF(&wg9|CYYe zfu6FtfAYNK2fLvCj5cL1PQ}CMiQa=rz3)D-o83+!Zk`LFvwP?q-37f}T5wvqy_~0YFnsUn|ycaR}^f4cN8HL$?{zEr= z^%Xr~CF`CMS`z@pz?T>{G`z5n;e73+JNdUf#MR*w_U*m06Y3h}UGxb8cw55%Y$2&W z6od1J`)G*==~ul6exxO`UG%HN^fRS%Z}F@T8K*5ftR<38^sA3J91>r4!sJ^H2Pd@< z9Lr%#F#AxdvHafjtAhYCm40@Ze#LfaiAraduY=cqqn|ko;P&=CsiC??jdS8-#xSxu zLCx`@IX1OP1!YrP9|0Oj(yvG-E%C61*%*$G)X?N>z)4Oe__N6oJ~SeMYIkQ@p(EIb z<-9kSHN{5`F15^AKC|5CIy0cmxf*$T1)%I8>(aa$Pc)Fn148 zt7}{$CfD8MO5Eg%wIy7fB)(jTYt4lu6IxZFx?@A)e zZbH!cq|3lf0-k*L; zah|SprhEp=1@l!ugg^h0H7OvmKaL}Fc)oThE7@6=C<@C&5TBf!pO*(XWao+#K z%BBRw-M1y|GsWC_T+O+2!eNU4`YXWf9)8UMb5c;Av$ML%yAO|{r8$id=aaz7? zb=-2?68L(g55Zb2c{P2(NKQkweC1Xp>7f@s6dCA?cV5y<;92N6qW~{_LK1wG)yhvU zd}yc#I+A}m|8Tl6EGr)ojlo*MM5z{LLL2ZiOVuz0bI%IFH~(OvV^*T^E3WiAOm>&2 zK|0)-E?gU(hktBC@sCBy!+E2B#4lT)cbz}PSNP56^T_VvVzR5K$dU9s{;6AT=JDU> z?;Oi}{-*JcoPIvIDBEkg zq`rc(B&=;jg(s(3SF%m+MYgf759xWdNS*FcOtQ={)#O-(TS-#9Dp`pVpF9fd5@f32 zqiUs0(;Q68RW^~@%a*Z=r9IijWy?^`!)0%p-ZnkVdFF%p!6y8)MMtQqlcT^~kj`m?sAwVs;G+lQRSPfj|NsF+z^jAUHS$ z{{M+0N^Ais!NK$;I;dA@2*=H&x}HRZs~5Dog!m*Ub)EI9!qN)BX<5HRvqc5n^FgICXgJaUdQMh7owIpsj=UH|zvCUOIK!w5g@6=-|O3Hh<;H`Hb{ZFO1H+ z{MG2u+9vt4MT^cXnLTUCX+aB>POw30H^7d;D8eVX(YC-K^h4w*A1Ns=T=h+eQleVK z`o@MT$yP^6hT{m2d5<-{jjdS9jCIQ?J{Gg=YqIgcT5hi1N9u#K zt|CGb<@y9H63e79Y8FKj#L4zJT17vtU7r8o#O9|i9qcf7+?5ftT0DI16giG#MyG49 zuqAf;61qQp(PW&_JZ0I_+J6r(E4WPp)!`Svn7@xcqWv;^`t+I5@^)qGlvT=pSg@K> zx_;(zVwTVXKCy`cs(CxMUySoiM@Z@*nUhTK3@dsIr3fcbV&#c(19lh~PW)C_=n^rLBsb(*Ap4nsK zhldXO`?slIzaTX=@7+gEj;ZrtaO6HVuKuN6wQ#n0n15X6MAST}dfE47E#+X-Qp_d8 zZ^;bFl%ec0mX~88xj4#7P7L7+g{4vr4iy}F^Grd=5A3rTZ~^`2k{O^SNKYEodwQCIh|8)_`-7|!y+3L3s5Ni65c zv|((D_ld($*AkThdnQe?Z`r%IIdm(S_4$7SLj#`$I>|Q zQC0yTKE{h|IQhnavTrX<{z=a@- zhh4;28ybGTikKvfQS6Q`%Aymc56Igc&#D(zmX74*uT&NzPfvD3S*rB+&NEsbs&$f| z+v9kN?$QP-{Wm!-!yLzKl9N%_$CO=D$EyEDU5}OO&A3)cJ14RI-`$#^2_t+BkNNeL&h^yFoS5hJ7ID zhn+B02mNUH(9(l`D-4jnE+41g*b+}*vg9R<&(%A1?cvt5MzAR-*_0#d1&2lc>&{a4 z+wFp7hn4pos5E!J1w6{n6;uZUp0IAo5Mv_A3`q#dr29#*cA3n)p?z{TpRUcn@Cp6S z(G_MKgMU1q!Ji-r4OX%^A_+z|9`LXiLK3v%RArJQ$i)eO-PjiP+lCT3L6A#Ueg`3f zSk_%@$GT5N!`g(Chyz$SnUEQF*r?wq6UB*`=^QB^!iU+b;iyflWHXzTB=-It*85}h zB0{_UXZqfqkLJ}1lZQJXXGb@B^V@^7^75K=tIZFrkXoaKGpV<@)fQ(c31}RkUIlcR z7}z(N2eA{39c70Zn^R1hkjB^WWe#rE38E{Wvu-;Ii5_qH(VoI_i<88<+n3 z+a-2+&&?Z`Zp!&R9_a6+AS%?cR9@y73c8lbLmkV&`@v}Url3C?!b=FTCWA3yp+N-a zNRdR@oQ2a4v$Igq!$OafZlS@O_DtDK`hLuM-#N3XL9-2Cka~sk!z81D{MU*;xhzfI zQu-#%=zb(Lw#$fy4c|-#{wDx`G;{)}nrWiZ;$R6wi|X2V{nEWUjR_Q!_Rnu%uYrLysj zRAJ6Vwpi&UKk#6`e%*Vt&7M2;l~<;neE;~&{QQA$w`|g*$HSxKlZ&;kgZq4adQ`7B zX3S?;o3nN~S+NQnh+8r|_N|D{V-6IJd6pKE`T|d>>;_!~n-c*Wm}N?+ktVFZzL5o0 zhZ{lic^>;axe;mBbJg1q5BZM0|47$uCwxClJb8dVx8kW){hvNx`Z8e2(&(~l$_tRK z83)Y7RnUQTFT=nBEB!wUF9^urii0mvmyzlHjeK4NT3x46ZSdIM)tImFvAKbz& zZNWkCy=Ue>kr@$8p6b_kcJJPFFczjA*DkS7NMrKl58s{W|L{YN`t-|a*}fMTCp`@W zGQn@yKJ<6diPJxFrj|w{_{nD4pGyDIHM zXrWkvj5SZ9*{V-*ZXuuG(5Jjr@tsbi;3EXmutfA-3WSBpr*A49%W*B{Nvp8Dc5ne`K+(uO>@`_iZ< zg2#?%Utmprq|vAKd=t!(X{Xz?&7VACQuWAshc+L(o&sEZGpovNBPn_of^?z z@M~h(WThAQULA08CSPI=93=?1Au~hfNAx9fPXqPY3`)&PqeCH4Ll?1&ZDPhuz4GMy zb%GBDhctZRk7b8yKYuWiOjvR@if)~@WeZcbtz=hUet8C&P8!H*J9g{<{uwMz$y54q zy&|S?*to>_q?}AscqX4?OvzCaYvjOLFlB}(gIGr&B=yFfSv`C0=4GuXk#;ZkKlkYNLV2>LZll|2si3V3%4~-+ z`C3yG{8CUfHt zM_#_Xg{^AbIG)T@o}5{KJ@q}}>)XC%(V3^#zCg#XHxDLB4dq97MaCDgn)^TZe{;Yy z*oQOa+b7Au(6Gt*T^?+h_0YHnl0#D2t3EMoI*^fufFB*f59|noG3CUoF5;DjXSxoK z8Zw)mX?x^_gHdGi!n2VtusAx1ZUTwZWl*@?aly&gYNi{2!+kt&I9yQxRKQk(Pj-JU zYVZ*9||So<`+ef=aycGSpCr}Q6m?#l3}bGeU`&_oLBns0=7FImFuFoo`RQB z*{{k<(`?v;B+g5rn>l>1X#@`Hgu=w|&Jg4Y=Q1Z#7A2pE8nTF8dL*ph`H90q52mtH z?63TDkwXXX-flbQGxYJG?GwnvMdzaAIw<7RS)@_R$@2eNK3GUElGZnW(1vBRC!nod zj?bZ;YX};B#N4=EHk6aN)0gN}TV6ZlcVY)FWFK^m&YKUTkxGUvI3M}?f!UFR7qOC; z_r$S@o4-t2bTEdjSa=Z>?qt4POJI z=0^=)%C2M`=zA!FOnc>vc+N1kn6BHt{Sa?IA>%RNnGaSI89d9(NalSNQOD%W5K)=s zwbe&1VxM%599>Al3x=QS!{%%`7rChYxrWuqlKJN%g&6+WcJK{2xQh=J%Gan#`}Gs9 z~>3Xf{o4L6rQz`AUaTp@C#U-ayYeIYk#*4U&jVtb$8uG31mFJz7apCQTQasvrIb2}-11jFUJY{{y#ksA*z zjfPz`&ptFKtoIuvddQ)rA4RdLa}E?1LbDv_wr!K^>EESm3kwDQ+}GkZna_xO!cTMp zU3usg4%9a0(5l03a7x#T{0mr|DG~Hzh8A`}uM93YsFDIrXq;@KXFCP44;K~1wqzw! z-U;vhY3I1-GcF!@G@#`a5;0^#@!%;iHIbcXd_?x;zeYZ1`xcJTuJs*Huh4B;ssj~* z^PBU-59Ptv#SoaG$VLruwILaD*6?npa{Wo({0ot*^h$Vh%%KRj3Vd4r@f=Q4hAh$U z+S>818feg0UY61aWx-bRqC7aB^Yg=3az;fD2DkebS9^@lw5W^mHU&~d|0g8(nRsawLna}3dDaJpn^BFd^EY(R+EW0Gx7 z5uQUb>@=#sDZ*(FQq8bDIZ8sg7r||%{6^r&)$F^6qDK`HUpXx5r6*WnyU6)=BHQ1O z#z5cmKHWdfjbFxV{X4ezt)O;Tyv^$swv4R!L44@=S($CWff;@Mk}LG@CtH?a0JW z9w8fBc0b%Ele`&wa$<28)_bH@NV}356MvRRYJbflAJE6NiEQ(@Lyk2xcCxen_txu= zXS)!RNtTg`3ob;yeqwfHUo=RgKU#fIE_CS{GU@U{{p-*Bsc!X;`$51qkQKguHuBX2 z)1a-t=buN|3^$y^RX=E`7z3-^G*su2~!%;Snb`pPFe+yJpk`P(8eDZI9h*@0 zi*gkBn)#@$-X_H7z#>r<9#?QOs&A&eOmjrGs(y+ziXsb#45+JJX7AK)o=Oi7J=<}V zd@J=mmT_?>eP8R%Zq_3`$vzU(iq_Jw!JG3K{4k;S!E11WjxJ}4?lijl1N8Qon0|}d zH~o8m**YKo&I^l9gp(o5&V;{y_?4K^i`Y-k^H$aIho+IMN`J1WJ9K}?abA?u41D7H z6puoke94Z8w^@@-d^=9OEd|G}i*_|4TUKfusulL^B6g+EkFSw;wOv2_6!+B)@}`-6 z@$S*1Zyc*e>VAE?Hccd!Z9BBr#t=52^`+#|0~1K-*CUjgJ9ZLoGC*5@l=&3O4CI`H zDU##4{!x`C(BoR8{;wXGOVxyKhx#UIjdRF%ERN7ta+xFf7X8U_j+Iqoa{;3*VDwjB z5$!;DL{bQRdz|Wx4I%M4nH14B%t@)Hki_6*Y~<~lG?r{d(Z8|pt)?$5rnTg$#cRW~ zKQ3->%F6bhtZ|r<(AVqI>6^c}Le38Ox$E#pU)A#1fo;uRnXvr;4z~XM;K{#qnV>C@ zfVXISx#@>*?cvYrO{8hu;YhY@O;O$U5yPglZw7t5;9xk3zkWLE$;fAikXC&U78a5W z@;9V%QRxeLXfwF|22HRXqHTxm&;es)BP2M1cfHJVQcgIWTo~*jli0C7l>W&Yyjz=$ z+Vb?sC~nEJ^}4+XO)V+98G|Z@|F^O z&sK8a&6;?xz$};5)wZxRwG&UKL?0!MVjcshGEqeV_-8l#5Ma&*dYLDrBuIUVI zB~5-#m`}&qt+Y^1pjn!*q1>N53qJE%_{1D;%;8&c718;twE56-e)!1_M{^HGE;>9b zbm0_|H}d1Q{n}^;XjmpUF1r@4kXr&ZQBQw0ePGIm;F7tviJno83Qaewvfx8@vW+Gv z!h15;h_EHc%d%Z#(Jdo4lZRsEh!4la4rFTv)zbE|i?!Rw(j%12Cy$dN^3EZOv9iqC z@})W-vhU{@h0&o#P+0nHX0zy^ZmP5cIa|NH_qvo;4vf%6D;m zSd?U~%O^i>mt^pw1H$gj+CAUk`LmCEW!{8|4!C|2kUFaG~ zDKr`9P4f;doZ#$ati$Bv7#Qs{jhTF&q^w&vKWQ(`D+s%g+<3vQg&tOP^Jxxvw)_8N&>Vy;ztk6>85+3gZcGv$T_*f88=vZ(>8zfHEt7MqW+PfIcTL@@j0`s9ZC`S=H@ zzCQkv$t;;Hn9;HXs6qJmL=C<`-8q1pLlRz8?5rlGx7ds9gD?=AAFz_S(bY~+cjS)E2Dv#gfmc|GI*4o|*x7V{b+UKL4D151aIVvXG*bIF`r z8XLzp(|1-P62oY^4_HQMZQo+p;Sb#hKa~r>lR|%jq@lSs{~%%ynC5Gi?LM|3v#-C^ zdZpRbm;VuA0YK(MliYxhWxDQh20g#$Pm!oGg>!N$Slz~0ICc+Qu7(+^hc-MfN+g#G(l zw}llv({kiD9sQLtjT<&>)Tm*@#_JbMe|f=zm!~gq?78ca+`9m@66vxenO=enLZl|S z0jiI`9BU4sv63}7)Qb34B^g&rGK#rvv6_SJQh=Rqm6XLci`_4X+nnn#sxU6;JbZ(g zI0v@;iVrbgrha5D^B_%?Ip$q=e3HG3vcvl0;TbPPl7d`Zw&4kS_Xk)PHMayNo*n%UlzM(OQ#0m*e z8E5uUtB2R;S(NIb!M+jIYlZpNvIkcO-NEzf>yASjvNh%lBY0kZX|N%JuN5#{iB!Ix zfaZio@Q?(7RBf2bXOfyzoekHHy!rAQBc=*Z;`)&{r>z<><-%h62%nX_06 zTO=->ym;}*ki$dSe{?@$?9pK#4J8SyZ*G&9_wB#o+etSH9nbO$m^2CYmAqQ6$3as4 z+z7K>fgYp&`rD<#pa370_*)c-w-4ODiV5lKC09_XufQXM3WvFUmT^ty|DeZTpZXSm zupbyHRK7(SX4nrOb0LDSnTcw#Ub%9FH#g0$r{{*sHp#3|8$oIr)m5>f!kUToXY0=y z8D{Ix7VFQ(>?++!>wnMsuzfe>Dc_@&YfIy_s{oFiu-R%;T>4>FJs?1lV@)*H&m8NE z>f+#Sgu?-m`AUmbvz_{F^-*Cenw40uqdvkQq5AU=eWQq+FT#W*InND|V<9&}@@VJ` zk(vX3Gr$j&rscv2Dhej*%vk0YWIcc6$`LN*w2}t9rb<%+XdemVmaB?fy!kMY8+JDp8~WAAbuMILsG5yIQDD0|r`zmzWHZ zi)B>KU-*3Zr~SsiPjMSodQ@04-ELb@7R88oPOg>O)oB*H0u?Yxv8v+Bn?2oZbN@Qm zN3vN6=Zs;t6p&g2OA#}x-b$( zWSP*l1hht|FNKQ5ZpH$-SQz27;tU6G5z|DE0!<%p^56jBd!(Gg&XM|#FG)RiR=vQR zIlYNPaU;vFnj65UNR#U3h6g3sLt_{Fq}uB$^U@o{)lQ1?4>qM+A(4_R86dz4uTmEV zpW8~nL=5`-VpT{DOw(t^W4j7^Bv?ommXhdPErKcV#Q4+{`E+u_!)(jcyy4?f->mFb znKXWQ-c<6)Ck>PFhIHd^$|N|IRwCZ8oyQxJ+AXtBCluDkqJ>malQgPT82?%JgDtZ^ zi!ZFjjpVCD#0CfYTcuEI8s~$-j6kbury-Il$d1lRp$KpV;#;38fpoE|Kq zt+WsMr-+AwAA4?79Wezvp{F8VK6#jd}2M2 zhej2h{?tr9gPahbwC2DE)aqETY|+NOfAsQ+s~1il{FZ&d_OegeUh?r8qKqY-A<%)( zUmv?{eSZGCpMJCU)0=;DyY~!y_N{I{d*j@|25p4HuKVlh!e8g^I*`k?I{ox?r=Ong z^3(6zMW^}YgA{yt!f?-xfeY6&CAv%d_FedA{f6bQZQS_U@(r4;{1Lz9k0D3b7(l92 zGuMyz`gK2y_xa(6@s=*i9+c?n;#(^ozsL!VqukHPYFHnO$O4`IhS#{oh2E<$W&u~j z_`QeE_3eiK@GD-Lr2le#8&kGejJbqMbzuu4g93f4$0x)ESWOX$f%UD4Kso~cRXtDt z3dn~C1;=RP8_UNx|^yg z&#jyG_K_Q6JVwy<_9C`;IvKjiMcZl;cY4Bv)9mW%)$E%gpA0J+JK;2mTRrLO`u=^% zfuXGZwj2Dq|N5(xT}Kr{AHH#lC(PyS&vGOJy@)zSBd{g2MwBjt3SH z|EGx6EusVD(OP5riDMz}1@Ss7i#q#>v(EZK+M(`!jQ}64rd4>;)(r7@^$xt=&EUQn zZ>0GLOV9W}_dW*tu&~qU13TNxAoTBA;$JGB-Dp@^;iCc7FWBWi_@CBWuyMQ;xl z>Pj*H-rd67LT=$B^Ii?=3olUECcd;3AEOV>i&h8olC?;DiGGf1Id!MbDLTXG?YNK2 zrD9yJc`nq!w^q%Xp@vH9T?<>|v|?+?&a_$th2{p<`LyQNas_t{(UFSA=)84Y1zHt% zFAN+vG?>dR!5(Pt3)&if3* z*f&u5t6ce=`VJkRbYmN9@fc+;_<)1~y1^t0MOV*4@eWdBM#fzu4%Vr1IRs0>uZpZJNK9V|y zteMcPL4#&ZGcx4a;vy{#Z5^tndI)t#j7g~$-l34t|5s_Ouf@+F{>>3bGd@OnUHHq+ zHk+>}PM}+hXnXDbBJF+J9$k!~1==ubFW|ny1AvMC3&xz9ih9r2!E!$H=(`=ILAg;; zkumnLxVY+V8%6~t*Re!pH%YI~``(?vaN!Gbj-#Hyr-Fx=TRkE+G}0ay{eZn@d%ItB zE5Di;?YAb{8*l+MnAgcu0K6U-AGuo$7|jmLL`6oOfh|cyZ#}bICOlk4)rIoQYg0Zb z{^5t>52n2Ki!PW4o6cmvR#7tR=6nq%6<`GK>pANT@jMdIuwwbZCiC$-*tDj(v4MVe zh1#WQR=Y*n8mtD|{Vdp)5(Hme3FWLif8>a+t8m!Scv^qSNZ(mK6q^-v&%K;@;l-bj zw`h z^SsGU<6}7(UKh=R+O%{5ZxqxE=;73H!cfM8CX4hyu767mHYwAf;|fmpa5OuQ+-O$# zriEg9p!`@4C|zEXnq2zVXB~ zEhsG59_tqsia|h(E2F`~=q&$D=j34|Gt(+1+A<_tA}TSwc#L)?TBS@|4at@n8cq)v z&H3#dn|<*j8Gr7#IsArppfFpI?q&Wj#p2)#Hc@-`)fk|VsH!VTR;!7+u8I)o&kOU>82N7Io1uotk# zU8_f{VO6_}FLh8Kqr>aLpN)#O`Pa1)b4rvcH9j)bKQ^(B><6MbbI`xxW4}6kP)u%g zWSzZXgw&P;eC?sK)z37oT705Cs&;CPYF>QeqX~jtSqWJ>Q&0&F#)Uz8VRtbvixBdW zV3Ob%*hdbs2R&E_OVeEEkc*4W3m))hBPh4RX{Db4+h19-KsYf<(!^?F@mKL%R@)o5pM#%ZjBdnX{8$vm7}OiQ(xOh-?iJX)`DX3Hdc%JG;bAhFf{ z#*GfI>GVpE9%)%oVa*znKz8Q<*;X&BZ-Xatu#lG5KM{9Q&TxxI8Nwb1P21%rv?tbU z14UGBXx?_R>DC@o=tAv1LGvegKT>~>g`pS{BD;SjoA#$zvjLW-pjc>f9BkJR6z2LqAOk*w#hd)aRbX-AINQZQz_ddq4;yhnat!1kM7 zhRsinxGqVKZ1~0a^?3f1^ErM!o?{$Zr2oO|Iqt>tAHAL%@IBL~@bYjXrO4$0-+<#; zZVFF{^a3l7^alLz%gdb(x(xU?I96`yQ#b^`5xLx+r|OXJ;)4MPvx6RRTztbDH#i1- zaKR&eN(A`kg+IqLl|L7J1Ey|pQX@c*7eDp%f+FDQ%^#%qg0IWhfbWebFhqK9x>6$a z=Xdkjfa6)N3x9Cb4SxfUH=m8?0-7_uE-%mpd<4}VaCEtQ>q!d7+zWqqIQQbeH~dsi zui&#c{=g9Mz43SHi8nuWy}Fy9x?J3!>v-z;->siIz25kz>NN#&2VNe0)8%zH9KGCo z@!6X$L(kp$e=yG@G57%2mzV7Mu=ek_n>C&I;eDLO5 z3fCye(fzqjmk#G{zUj~Jg^xQNy<8o>JD%=*yBofq-d&&d^t#;L)9cUm^zP5y`EWNr zIvj8OU2?%2$mIn5)DrbVu!*n9ao*1t_nBx9+~h=H9^b^H-#@H z=ZYP`S19&No; z3(g^TL6sj$Z8Z2K4K7gY+_?Z4ygxEmy@for%V7=`FzWNRuMCqg%8S6&SHjXqY}@4H z_O?vr#Ukx#=AbtmHxVD?R1x-TzC!oN)wBlmGaWan?3(-thZ8YOz~$wnigHps?@2px z&*y@%7YBI2PjT`rQQX(^y!R_I?)kn(q)#{Q`4}(m^|^m81D%dE@qRYO3D=48v&++W z6X~;rOwc=1j)6|GM-Cg2Qn9d$Z&l(#-b# zt;b|Jo3`v}I`&Q-zUf%oL4#+rJ2Q2jSV1GkW`a!=5&@0n_pbE`ysrov>WKGmfoC6Z zoc%6wSqQy~RDa`Z%ZwiF9Uh?Rp_LGia8E1R^$cLy= zR%@*7+bCc+5%gIFQ@#`Ln<0ZNEyn#Wp`$jjcG8xhuhk;P1=N+ZC8s{7 zc9Sv_d zJhbDLb)oSmmmlXYmIp7AJnwC=j!u0*-w*iK6ufY*-9TSm*KKsdc}HAnq!!+ z%`UPlj^6UzTj$Q*a=d`DEnxUNylg+t42-X$>?fVPua5b%Sl{EIYahq=xzVTZ&!lX!>a-HCX;e%kukJOSS2 z0)OOJ!bx*$YVao%i?FSx;7!2+_gcn)1j zQ0g;^8Cnnk`xl`j>x@lCUt zxU}3E^!$&#Mm*Jj!ng?&50B{eos2?tQ~K#$sQt~CFw;)>~tVo zjveC1SUwqtdDf<6u-b|3VK>-KwuiKXWr#USm))sD?d*Fth-@HF;iR|?>{(InDA=S@ z>>c(M`&@MUn~`lzL8qG+j3xhx7)8J~;$WJ$bjy~d_RU+!;0t^9oc3XV{}*QjV({df zzvZug_~iKa-;pKXvBTsb_HO(9#m-{8a0ocmh{08*(mC)0OSI6g1VdH%rtkz*NxpOf zjUO6(W$g{x_-ww`FaLsrHNm!SGZ~4PzR4KHs>4HsQoA;ck9v~NG%PHl(nna6s{rpB zViybM4*2#=G!{8dklC3DSn!MHR6hnGynGq(1H`28h#Ff?rm?^F>>+1|+6jHz8B*i- zTWs;yTc(X%vDX2Ljs^9>95h~gft2sV!A?*v^wBXD!ikH9JbU^aF7AhWglnD!Rg z!zT9ZyQ11l8_DVSmpU@#QzKcI!=E8P?3sm+v%%L8aU(z%GCPORM92t5(P|x4*$h{} z2#!LqBfuFe0=!gp-(DJ#CL zB^WCt5H0}gCs3=)>o09^?i^Wv?%aR1PNzTn>@?klZ!CL=X5Wx_a+Ac%ZTTG6>u1l> zBj?VZJ*PE0tGwn}bx|g8QCmURd6~gtw}!J!S$K>3(ssiWfBcglaP|!=VLyxg{i4DOI|Vv@hoUV?2}uF{ z;EpNJ$p>%=d;?DA-LPYLql_$@A@~OlSxq6S(5)bxVfeV|Q^YFaIQ!u~1fzM?vEn=p z{e~9OL;T!YX$E>GPD%_e8ov@h7z(FV1v`I}?s0FVGv0@Y8J{|Y&-*|isc5jcXPr|+ z7zYI1;r^s^jSG+YW99`a9cInaew{Uo+Vo%c*Q{AejajoC6L$?6vTN|0;+wo9e%6X_ zZI<|9i`-v5G=KBv`AWmh>{|EmprEks?CLw%Mze79=7lul9TL|)EGQ_vJE^w0p;kC+ zR`{w_tJL=RVr}t7`Yib0f(8Gh*BAb|V8NdY#W!m#p5Zb6vmCb@xoh#3cZz$@+|L~ZOYxI4Hy00#u2wC2VDaXePqMSC(6_pS*eq7mF=#43m(M05L>U2E z5b}bD-{E#D!KD1E{X*968$G=EdZ*^I8s$uy#PUySzld3iv3b8>nFJ!WbnJMT)L63` z0XMkIfjZob^-$ZP{#EDoj~ga1m!HjZM$}T^u3R^j7UgH zsXLcB-eWD5{!3ZGLyz8Izg>8$|ElzyrNpu^sTPZp`Q#{QfbYl0lVK+W4Q_Fp1lGev zh$exm<8wV8lU$4AhJ3?;8|<6F4a3CFa7J0S1ddJH#Khq}N9^wM7;6I*v;&Llr}MBH zly_~BpZlEeqTC4h=YCO^SW}3<6dR=QttJ`8=lN=|TwiMU+ZtvMo@Nf@3y8V19ER{Z z;v6cSY|KJpytsymOaOELb3$te8p*F{&|!#%HSRJsVhR8xC8{Yxk`LHmO!=Hy_$+B3g2UF~^C_@R}g zQ1FDdY-pbdOS0NqQpU;XbOve`i!MRA(@ypK#iX6MU=-?F-eVj~+!?@W`Jei>6 zAJaK0`NwolO8zmOlajmAam$6#igNrohR_*<$Eq01K#d@M^XWtEL7*>m%6M-U@8SE1 z6#0C@MeWIok;nz3!Zh0Qe@^2V;E@KnB#XL<%~l7sy@KbwoI!LIU7%iv&xuwHt6*RT z5D~L)rfc2l=IO0O+T}?5jYyk_W8R=)2)p@OPL zv(_DHsq`^jMOq=P%v0;DNo?lC)Q_i5ofi*i?uHQ&`~j&O%9mNi;p^K# z%Z!C4-xXxi3KG4XeZTtQC-$>F00I7tAt&Vy!hgro$OtpdvL=MZlX@?#pG!YSppjFI zovlwAoTu@Zo#~|>atE~OocDL9r#;Klf2gPDvH18JWsK(W_=aG=E65hCt5^nv@LDZq zjlddYz5q{WO>`Ys8>_X!TsW(Jhvno)M+Y=Thlq9Pq^ z6sbxP=_)EJR*=|5MFqj$H7a&2vBzMqQKQD#E1GLBF={j>YSbjgBpO?qiGMZu8{sbB zZ}yx67lY>ie&72(&-*>k8w1?CXV2`+?Ck99?C#8YWg>YFDJBsur@?g`vZWY+SXJwD zlloCCN-6LZ&ZUuGaNh8Qgpni8q-^V**|uGasr8#DHGOmSk~u9?J9b*0*{)NIZXG+d z{S5)a*bbl0ohBUa-@i)W!7x|1hAoE-Y2Iu_n@(>bR#>%a@y7#PoFCtR=M?b493xML z%+m-Z-9SHnhov*tKiPF2a#+b&!oOHz{X@_)41S1nhE%A&F*2>aDTHe9t zodEkXNUTd^42gy|;F`WRZGf96S|~+?>(G{cFD>^x;LZ?YZSxi`H0!m@Ch54k%#-)g zT5xDPbwy|`uvrS`I3vbl$%4>2JBE!rC!Tk2EJbNcz!VW|F~}Yf7f)?Cc2Zv>PIgTS z3MyGBvi?IiC*YX}I7j>|x?3&`y21sgy?j7flnb4>)(m)vtja^1MMUfSmaySRv9Pbl za`ZyVG2kSMng9`l z8^PTds~=K^245eU7$+Si(c%;Cv!ksUJH6{7);tgRs4)TLSs)3McNL60`Nd;-UST@F z#c#0a{(LGR5-+^Li(s6ZG6a+@RqjsOp<*;BB+ti&A@bsu8}{wJXiNIj$IzoYEtaNx zN5dKOjmm@X-yUWwDTPIbH$q{`U|$3Jo@jn<@Bp` zcGX9#S6}&pyQ&?=TD-JnN-m9tat7KBH!O3k}PyA|oO0AGcmtvAXXBV%EP8@NzQG;#%0dGZWotdbdL|0N@^3I|!gUC2czzE8oN@dwQrGU;;>pIO9cbru#OY-fF+4}LNV&V1s88lqLM!v93T zR*KGiXdB`@BS>rET?rvu6}pMQ$j_r}f(@g>p;=4ZWn!=>0O#}}y0Rf+wn~E@BVucQ zzR7jRChH-J+G^cF5nGirn+VP)*#GE@zM}aL!x_1r0qTQvjss3bF9Wzg=?kz)Hlq~0l2LJOpI1oy+os!Rb!$GE^OIC zfVXThUcbGmux}EVIe7mgpQMuIAu1=eStK2N47iOTY!O1>Ak&Vr9%2N8j_9I=TFwqhi+B zTYmCXj}1o=nX%>6fv$4C%YZ3`&F#ldWtOR9jE}D#;CJ)*ad@K6u%>ye`hlyPHqE?o zVo#omzm{}vC(v;@73g zYu1eFG<|BP(L7*#zfo`YBR-3B#IzoU$JJj%>(l%>z9lm#(7`uHgCdGb|B{2il-hoN z;m2$z)MMfPe&K>id=?u=4AN&*Ij`h1cofs1K}^Z?8r>iv8V)|`410f5=?Rv#Ed3pL zYd$mh+TDa5STcqBmVBfDq#od5l$edhE{tF<&CSMqZT>6_)jlkKJjc!1*=^3h#1#Da zU+TBV|DrS+^7s1essCNG#>fR#}~&HfAok3Zf)!5;uwJ%SNu#KFJwv{U>6l&{jmwMgR`t?%MoEo9o>DP zq9FQYtceJ{fk8A7VA#p`Kj61M_<;Er9y@WU-rfnHLd$$QVQ)Q-+dA27*~5o?;6vUo zPx*CQb|yb+eaw$$X7f+;7A#QdfOa&l1RbiB`ihJ2YE*QxwlO?B%$gZqU%ek6!q{~X zg7`YX^Fmm9L_Jo;BO&RHX3eK$Zt&H@r5mr)*$C?vDb3TnK@5&?biJw? z5*_=+#OZvvaB|Bb7FmAVsNtVqeE9Gpzl#IjjiZA#bY^y~?;&k2X3GZa74+*UzJ&Io zq3`^FXYq(&$k|R2F15WS1cWww;C8WxUyve~h&|73xouFCb0ustVQ{F!=E~V-U$#8j z`6>Ck-A17an4`fJc?VBk~BVww8n=yjTqhQJ~Jp z2}%QG1N%@u@`ieQCtk$q?TZul*0Y>AVZlf1+mMSbTllRl*_kX!a%4%FxcKji&d-x) zLM|RcK5S&!OwopfEIS5LhzAldghp`0kzh^Zk|eKOz~e7GdW2)BstBAop#)j4EMgTF z;)rNo@*Upt2foYb2}v%NWicn%$TAitq`+fTn_vk zyv2+2_{YTp{c0L8Fa`dC7UEYFE2crGK#ax`8u)V}p=VxpcAh>KU&ddIMd1(le>E0O z>#ZA#aW5W>jOI6a2gV}&P_Vjif6na zCRF|FPZS`&duNk_(D&#?+}d!Xko_eN3^$=IosE@;&HUG;RV(*@dEr94zJ1o-{Yjq0 z-%{PdY|@w4i@qzcGf(-~tfoykCUk-ee~7UP+J%Lp!ws?tM=z9;yOP~{@P&=c#v_7D z%MTZ1t~qs`*-=3Hf^SXsyZU7R4v?^e9s|}#iXYB^K!2kD*0WYpZ^SDSXA0p+{jgTckRHRp5D@qdLaY#v>d~3=sU}5cI4fbI(;(%=f*fMTz+Orvksfqy^ zPZa{~+%b8NOULfGyeSFzfKEQ_#EOZ=w#E$OaATfvz0uTz#tHJQ0_Jr(YXCYm-?+0$ zJ}T&J!#m3zAx|b6({z`}>>3pZ!wO@qIL|^%gzN{aBr1|E@`!80(%V{Iyl9Y9r}>GM zQUd>xu|HyD=NZX$Ur2U^e7$w}#5dP`dO@0bOp}2V$Rn%TR&+XymS8w^L5J-frDbQ} zBYtchhy(Xx*>jz?QYk7rP!s)pU-`GtH>3qMix=zp2FUA$7^1(^o~iEkQZhsr zGlK#%woSD+%T{plrSYbe@ z$RXmtP_c0ym6%a@1$o9*Yyf4keg{PmYd+iUcb?ijb;n!vB8b_7mp*e5;FVD z7W@Y8&mo5d8+rpbJ^~<8m@*-ZMh$z}DZs@EatU{P{28FkAM?%p41HoJ3)#Y(7B29-Zvz)Fg>txl97@hdW@lCy` zJ;)Mhqq|SlVOL|6+onnp-Qt9McA`z%T@52QW{{tgTa-JFCD>+65xs>S#9G3Se0Hz;qLJY|l|qNX&Fnh$E2ojRpcMTfT!KiS_XxC!I#!7M30i^eW+ z5`J>h(HK`XOw}1gMP;;RJ0Z_;YwuQ}Yd&E6e0iAQK)u6nxrA4f^PQb89*@rJy+)-x zL7K6vZKZ8#>k)x|p*ae>m`WJzjjK^@kAFr%gnkQc>gkcsm`KGBzk4apP}C6bFiT$)7T|MtFt@ z7LPs2MoYxEnb3T~A1uxZ zh`0dCFxIb2#%Mdq$@+_3?x?1d8#bEWw9ksDuo-D9cI;S@_ExPri@GiNz^WV`GPq}t z!HPqt?98rSkpqWUub%1Y*`Ptggx#xF?`oKk&~RhQ)E}l!8$L|Ti}0(PRwx;m7rzz# zITrGJ1-zqu9!M?C7``#`6*bzza5~*2eX@;NU)Q|ZWNN$Y7 z=`Zdm24orYei0%+FSILumn}9Afi@t`jD4jG<{{5XtpaWmyjq<=OC`)RsvDAG$WR!W zDES#{NQp`6UgC2^wvxN>H=Mh~#`2Gij+B+;MF?dmq3k3809dD$%ZTYhd)7_|*;eGV zVv6G#Tt;-f2J?eVq@_HJeW6^Qjk<5kYoueS3tiw61pEC)E6FZcUNefdf=b5op;CY8 z9-h;{_QOr)DCwRfM_JGt%4O?{F49WW$-ptIC#C8{y zQg!|+Z~bwiR6kp}To4gw^nD2zq((xr$cMq!=VI%YwSrxA8n`Z1{IN&b&S2o53YmBt zxEvE@?TgEX0-t-L3|=FxUIyBKBFbE0XGulqBO{cCAu7wZbNSpvUv zF|5;QbE))L#lyNn#RF{~6J@|dD;tV7?};+tL1i!TN^SE=59`G-k^}a_<=aVx*=Vx_ z|L(S?;kBKtiL1m?RkIX$p=U>^@k0(l=*jM+BD~KSQ zvxGnRr17i-f8e3hSqvX|Xl9p0r4Fn zg>?*P&d}(&p&w9iptuL-dd6F*S8}FF%~DA52Pl?b9J~2pZU2bixX|c$L_I)Ug@lBI z-hLrIQPqOPms<%3T`N>|v2*m)pUUP+iie@R_H<9(PxquHw@=#>@0h&DzIR`rJ%)~H zd!r%}61w{AN$i-GXde|(J2WmjBsj>QfUE!ES#HO){RrP1N(K0O5?|w@^{*D3^-DWI z@5DvxZ&(<>p^kSo^%V;Ph`sc!tB-}@{SQNeqat(|qK?JU(Ay&AW+nS&s(5+0ipCh> zRjEQ%PY+iY$MSYmSN#u&NNB{YxHwV)zRLLO;#k#F`>Ft~s-ETTDpe?c!H62Np}2+# zNXZ8At{FJFIC{8N_0+4QcuzT0pxRxl9;{S>NQ@V4B_w=kR%AnYJHk-|UdQmEQqj{V zx@Lvo+G^h<95A|8_Kk?SC2AZnxH(p<6RLjsuv`GMbE@tirhdJZ5J6g0CVh$%nBoVY z-RLuwz2=$eMs{|#bqvDH-^9$$+in3mi*o{;S ziT$T4|DnA8cj(p>A+0I@Yh^X%|0MN)NK*fQs9RGs-J0^hShuG9pKkr1vIhQpbSu_J zML#I9rgh@17OrpkIK?0jfchaCVN=`%>#p&g3}jIw>=O{nB7#yY~AAuYKA~Yv&!) zeypE;VWkuD0&%oMbclwKKLRmee0^}uMv;*oif=?Fkkn(=e_4N%%styk?D)Y=NH>tq zDkLEe=w!Bu_wL1eu#K5rtox;gIqXJidgiJ#ESsO2*J-4n4Qu$_XjAMi2gJr1&=592 zV@2aeem`DSyp_cyv(5o!FAu za*fT3=g%wRnBE`CTR0E&JFalK;BwYSslKc;J3E9u-@nybwA6altC|!sw%7bs{KG6B z#`k)5xY0f_F<A{x?WfR2A&L|SEo^>=Y{^s6D` z@}LhD#^NZUkBi)jKjn|mi52B8_Mh^9GdqtbU%DVkPXhbw_R>-RX455Q5u3&*TNm?m zwrMqQ&R^VT->|D}9DigDqqv*W5F~7QaMSt|^(WS{ij4N%sCC&7SIHdxs5uuuNyS8w zSD)SV2mfZ*`AzID+b0$9xqJd^igSOf*knG9_vIbi$j3wo; zge_BdFFka82>*9>7GJC3ZTAcCj)7k&7Woj>(Sq}WxE<=|3PKt%XFngh6XED5GCc=E zK;4{;u^tNZ4Gi!{z^qXVd4mY{;g7?!+1epJpR!%wUn*qQBEycm-|Un)e;&s6jn9Jv z!kJ$V<}3MX{>kQ?rcc@93GA1BM~>{{-u(_EXz||jjaZ9^yc^rh40sEFku`N3Z~T)- zc|Shp{q*kqBVBhzi+(2erc+=Ljb)>cix6(dfJl!Rj38uar|j8wMvR}b@TH9PwsJ|@#eQ>bedVtagV{u$MQu$3|67~ZLx0g8 zH13|7Nrs$+{z9kLWNyASsw0iCkGzb3vSljv0Ymb+8~0=FB$-P)w$5iiN!!^Q$xzTc zaqs;F_hdiH9oe+?56*SccyF2sT6_pP^D~g3)S%1Z;*VYe<%Pa5&E)59v!49P{SW!S ze_;*3~qQY@nb9H znbLJXaZl*sxG1yWolp#vcpxUq6`2Rj3e-;ow{tyL9d!BbxjBl-yvHpz;TJ;@`!aw> zuN{ z6QIO!PwsmhyGBvgFIk4w!Zt98_mIybF+?RvYK=|hHGMcd;k7b}ZXiSg@Y)ucDtl z6FOFvjgon}GWKs(K1xAgsjES0O`?!b=lBZcu=A~%on#KNd@61(!R-!I)* zI|S8UN8B?#@4TsUJwROgGzv>WUJ28+{Ssw65wj7M`jCH`U(Yl0nx z(;rCdT-Pq1zdCuqRyKnDW_`+jX5m0&AB&Omw{}E$fj0A9)~#E|XKH$SKH5QKaI~cM z8#<4faJ&HLb5e`{R^}^{?@NzcnmS=B;=L`^Y+E{t>qA*RPbdYz%*A9tD54p1j7t`+92q+)Hy9 zTQ1G{p2g%2UosMc$fTO{B*$~7csG83(FD=YG+tt0f6*QmVXF?8%1pxbL>Ek;#z+^0 zJBh(e?HBiHGYE8}%lT(mll=*7$c}HfvI{ikzrBTtI#u$A)<>wcqQ6*AiEm>Yboj{6 zw4d%@U@K0wR$QDhLAjZP@A>EHwIW-=r0@6kuEDBFKFRFg{4ZwaKAFGpB`o7fM)^<613xHkWhm1{88ulH%!Xd0^L@QvAeVP?b?qM9jsbxr)w-3M(){GJZ~iK zQuN@mR3VT3B{g6f6Z7lU%U^f$B;OC(remINjW+3oTpZv#xnU-Q{i`zD7)ZO=lCO`8 zS$fCu{WH5(vrpN{TR-xfQxENDP_R>&tI=T*jsx83kGuR0dV=jAu+e*V!H{6R3nwa% z*+=}*BD6Cca`O@94x-E*e8b^<=y!?27C2R59vIt!+ANNg%&v;EP8T6n zccgt4oww^S&d4HBd5gZ(=1CYqh$1w9`~`lGRbxq8xEBnCdz+VR!PWhZZTax7d;}Y8 zeI_+*4FcP83D|T zKiu@e?#**?PKKA`5z>AfuV}?TpR?IGj@3JMY~A{#r1h)$+D?o+jkSIwRpoE75%XC4 z42_O-@UfcUBtnoQ3^{?nJuJxJhS3~K3tI^uYU1&ho|Fk8;8O5G= zct5tLXTbRyPYQo~ay@CmAI1Uv(AykykUloq$1IG+;#At_?~5Q1`rNPc&(YUZYVJo= zLoAM#@rxT~v$`xWpO#<;-kXE@{%tlz?!?oe;B$xevR;t_loi&asZ5OZ$f6(Q-@!j$ z^oTh?Ry<4!Bs(@HFd)b!AkG*Ui*r$Coci`~cdf+a-`nsH7P7X`>7QWA^7w@D7o&dS zg3k@;?9V6 zRUM|zo5VZOKhY$9k?MK?e|)hl5O%q{8}8qg%+Fz~9)7WE)x(F=`H(SVq;|`tt!(-- zwvxA!6ut&)%KbCOu>&hDYx%a-nW?juv=|E6&w-pYH0`5#+6e8XPK?67h>R0bkFzTv z9NAy|+&!>BadVW?d9?#rzy?jkgbyrGPd)i7ki+zDcH{sGw0jV z-szJ*WcgFq@~iK6=r=hxz4INOs>y?qtLw#19SrFA6De_X-wJ*NKl@{04tjeaHEXU(l_KutN#M%-3YH=G)O!?3v>O2ICfSPio ze)C6uKm{RG5!@vT3t2_&y%UiAF#hw&p{$?u!E|A5eK%=Ud48SM6f4LF3l@mBGj@8h zBf}&%_Uy-Zc<$ykM3cIvI|W%qx)X6`-PfPYn;5csouYkxRNK)pz)uw8APHzh#^)LQ z9J~dAEYi9K@rCN*-uX?r91TTQfx!g3$L`LbKVSMz!h>tiCoezFFIY4kW{w5T_7sx|&0>PUQcf8AHG)8Xj86=ZqGKg=VG~Oif3o^~6=yfo z#HpKcmjE0HI?O$DSMl1+mEWAVk_BL1VxAY7GG`2y@Ii;=15EyC6%ttt!W~gI!}{&4 zwS4gUXL1e;T7BBecfZg6v@-UBef1UNXHI}G8|Ge*^6DRtnb|ph9X`lQf9uWCk0yv4y8=H#Gh;)Y;rMufzxBS zEzjHyWVJiNks(LN!GmK0s}v8vzyOmu2rE0JG!2ZwnIjQ7J0{S8u2hp2(qc|Vs_o8R zY?vF2g5ItP%?Eb+mi3WBjNg>!6PLX?e$X1emeunQT<^I&b676pJEZr2_pTb7*3_P{ z`=&28@dw-cHgn~n+{e|G{nB@VeYx~FBxl&M^W=A1y2>jC_pALAOvMxNZAR~nAGaWH z&b;xg>a_v~*4`LhrN?`GsXb1v6Ftz6J4{~SGFA?Bv$5FXii&g!gmH-DJzqp`hzcW7mh;;mf$!kfW507WZFJDFi%7kzEw>#;uiLAkl$0svIdBF(;5%Hr8 z$WqG1i+tS0i_*it0gu0M;X+};-@#)WTHp-7nlo^&BCKZ`mNYw+;LL~=tzmh`%h0VA zi6k~hZ5<(cU!p=fxI_v70$QI79?e&IL^rU?q9o`S&UZ?!UT(=WpFb}Qd>KB$)%axg zEa8*vXD+)3q@Y9hRxT6W@cqjzm_7OPaZ3t6D60*iXrq57iD?`S;e$vD76m*1xhn zooCc9K4`6seks+Q+_t!Lj{m(my?g#@`Kkud9t7?r`pBu+TSg8##Psr$Xp*5`lTWVP zx#&;6aMyBpYqnYAL|-NGXZ$>t8h$JfvHfrJ@8R012>04?Tw4?RL%_{`C0vSJRSGWu z@DAr2C4&12^LZIAES%>yaK`~IX%Jc}l)_imIaV6@@M1Q-vVqE~D66`!$oK#}sV4fs znZdb%i@Z*_PeH7a=({kuqXc`54;X)Rh1F(PA8vm51!M77_yackPM6-d*jt6Jf?1v6 z9{w6j9E(eZe6Wu(SK_H zD^h8sKheH9SZhD{pD?h|q_4xG(3yCk>s0BVaEa43Ea@oXNA`cx4-lu<;b0vjgzF#L zR)tJ&cO2Tq;qce@f-Y{aYn;z3k-mSzpjZk?4ilhzeiZtX)_Au1(>GFhgWPaS0c!_@ z5y3)*Jd=-vxE1iByhY`G9?bZoG;4zWJN9eZ4{`0%_MJDHu4fUXQDbS_=I&ki13ct@ z2j}zmS@)da_acuR;pb@nQQKALX_A7M=jm6qF1@MA+v^da8T>{2*P#Ge6gZd@gx)Pr zHm9KfKgHyiQX=J4f?F*WHvj2m2 zX+Eflc6D+4$HwL6#WKe{UyFh^h5yy!HE0ye;uYLW2?8GQwwe80{t*i)?T>l+`Sbpe z{9lX06+ZiqKUmK5-(QCUa1gQNa37(Z(93ky6ASj}K;|d{+fsZeUu<^4j=_yp9|Of% z+|Gh`?63~{=#9pw*Rnz$e021pM@UQei2Ly0wQHT{&i};QvNo%0zg=hIlErgcMf3C4 zoBS*jXRR5XA2)78oBqfzjIdG2H)>bG*R!IRol^y0N7s}9r2aS)Nx5bmk@V#ZrLstL zDp%2ItFu&Pmcetm$=mrT<>7r08OalVi$rFV$Os&FrF9apg@22Fuxvz-2oX}tOb;kx zgmK%RtTwc-c!--1?b);I*seWUL4oYAUB^tWE8F(ZdZ)0qe1(4ANjP%u9(d$8`>v2Rc&tYuye2Es@U1d?)IjXDkb>x1f0!8t`d2x!Eu&@=W+MF4$I3K zjvA3y<>k3}MG>?R7LPcQ^YV ztz6v~=|k>C>e-f&Ygg@Otv}&TR{NK_SL^i>HbxNiWv)f4fh@o2V z%it$EUhU^j)m(yzojQAUF_6Gr3{H{(L0_ODHPJn|c||)%cTZP4M;tLQ+4)v2=k1pg zWJ8-EO^AYLHJ`1%PSUIjfy>>@zDFxn@luI;hCC=QQ-sk*bd+>~4LLd~yN=oRhek;; z729q5MkhhX-?WMEpaX z9@9SDY3YhDwtnByJhK06E4)&{6$-i~-w9ToV%UAcF)vE`~ zNS~cjirm!3A(5<3L}CM?dpqL7+y&p0@r6R~X>mYsBv*A*AVe{Rf+9kNvT}2c@-6k% zoB7IGRor>dUsv8JN1-6btut#jUxGTSYm*G&KeM^@^ z)i5uo&dUb_d<*js$>s4w)|bCWpIdi zSJ)TQ6RX3-i7PsE5piiwoj3Mjp+Pl{=CskUmNr{6RjeBY9X--wJWtui%dcD4tM_}X ze&KCFn81^F7CgtE|9^z1xP3!oLBF^AY80>0{k5~|ZOnhd&bk&iZ-2z?wzcY2T%jj- z72`%eE$VvGW5g_7AWAp|j3^zCdQnJ|81NENd@&_Q$?N$yOM7;ZK^&~k9U`+ z;mV*!Th4@g_f*c~e#UG=cZOJ=&Vfc;t8m0+ES&0EZ-7_OXpqmFyb%)t7N~kZE6RcS z1hUR!%4c@zoKbO%{aXIrT9bEja_f|L^781eM{7fOX@h{3Bw%@BhYqj|aMLRkw~WpO zxIpUTVjEy{z}3O%=ZkygEbG*Z^h7)9utQ*CpLxe~_(0}*O35f}w{gUigfai-H{LwB zXX>zKL3`!hG}bWRgaMWhMeFfn4ziHXhEg1SG7V+$)y>sGR`#>;kF3dIy}Q@9v>H3U z5uf!)$`9+EUf&X#+CPI0;LD}urud%A)A%!HpS^QzsyiEK9Uw|rg+Dv58rqFEsSRnc z$Bv;bGrqubqKjb0V;2q=dK>#_wgyB2cF76Q#dMw=6%-T`P(Azx%c}#sikF+_Q4&fW`}I zwH!Kn)##xuYiYC`fxOxo35D_-cT|U^1l9n9V^5UuAbBSH>g<&d{OyF zbl{uH6-niaWIS@XddK;17M_>mHheKHMsmdlw(HFIhSYoqaEPbOkWuv9h=_K=vnn_W z!V&He(_xG{fDmp1Xc-S5w)amQ-j@4MSP_%jIIuIGOY z-MH-dj>E`}Qq((5=jN;Mzm~#~^G*?ci>@Mu7{7VJrLZkhm=v~63bTG8?mdwVbTBS+ z3^dkw&){q_CerJWb;z_lkFL&hLmPSX~2tTG5}flUD94%CXj? za@6fGFd4~AsqHTI+GKP&OScec_ z@q{u6w5hOQJBl{CvJFzT;`-~Mg(3_$wP$IFQLspRwpUR2Eq62fFUM}TmaKI1XilJYOEA@klYrIxp#N$MLl#d`dnDa$_;?n}P zd?J~3w*Fd{ME=0Lc(Fjql$pxG-7qoJ~Kz)^;f-WKdKB836 zrH*Z7A@m8L3O%fl4HwIPb-@d~D2q<4|et&Uu`$b`OwNv)Qff44375He8oE3d$2NomM)6-^TBQO9t* z$b=j>D3>Oq9B`-v`Mw|>rQkh@d$`7{uQBsIM80(Woxr#ryMCCT{ zfoB=;0|_qZ6;D+-?5PcW;8`Yotz3h@5;B~EE!r(F;lC1i3i#?%4bKQ;rjEZcr6`0C z*YOwS0)M?6@Hd&)njFBN+QuAZGTd7(gK}sIQQlDgQSe8HA8E|N^H;&gQ)N#X@FQ8MF{cdpkr-S*0RFqYf-zX` zEBe=L*bVsI1^!gtP%W1>8YVfKT(P-NaCk!{U9RmI==!d}vyN1;C|)f`eJWpTTW$kC zT$*dBTn7AbDX{2a3H+k?Qt-oBo(jJxz7%||+y;Jxw9K%q4EPaJWkX~M{Fm^L(Bboj zrQmDjHt-_>zbyVlz7~GuE8uJ8hDrRITvcul{)e*=ehv8d5d25E(!#dfb(30NbW^UX z?4kOG7Z`uV^^0yQEo{qe>l?R`eEz+@ZF#Bs|EBR$bXw>u4IhGUv?*8X7j5Si^*xY>Et)*zDddFepE5#Ds2*nN6vI*8P%T#`Q@L7SFUR=$z+97J z;u}B0ti;6kuQ6v}^x$v;*>e6)bgYm9Dbs40qOKvQIj#BQn41w*@Py*=Xc+)%h;T{;0m^Pzvy?(x#m2*-O zS_SrI<>#jNoISZojkb;Bs)RRd!X`Fo=jS`N7K=Un!YjCZWuE|Rqbd%L7SE8HIf-Jd zU_ShuaCf~fcmf@$OcCF2(07$Ka|rsTos2Ddl}4@nYm^7NVqS$V5Kn205j;^2dkZs6 zyWwDoTCPkH<*>I($_>-3Tg){D{5sNgz5(z*QQJki3Li5s;ESgO$9hwP4^I}Avu6C7 zTCTwlw=LK3uSc>d&oFH&fsa1>+FTF#tMJ9sV*IIGg&%H6Hf<`#pUPGETDf5{KVWPr z{RX(#MBgvQTpc1c6KA;5qk5mBk6$GS%I(mq?vXJG$ zIBg0^4p1fCJt`t9M$I{cSmE#lxx>kabSH3l7{xHaCPR=(+S}-I$K=+5*6$rH4ijCy z=f265q;NMno4#dZIP9Y*^+ueMd;{Ay>v*D;%etzYqKCiTa&(o}u?>Tp1;(**QwC)< z9XYPIW$+J8vcCJ?JEr!4rf(0AUS7eh%3RSg`558sdSCs{E6Qi2?fCvc@FuV56XQA4 z4bnT(2+V^f<6ynqbQR@+uHYffbM&-WHU^^{V};5M$;Mm7vO(n-GdAUmplwLYh@U&u`-Bnf;ICE(}J z0w3M(EU_HzJ>{o;9%k{NU#zNYxP+zMPniV&6nf~&?I-wRC3)A>hL63>(5#JvRI!f@r z#)Mt#iWLm{G2D=)>K1-Y*v)~i?MuoHtZ=wVLDY?*w6=Dtp_pT0{i7c81`mE^AAfJU z)^EV%v7<3mFdrBYFfUK57ehhV0{y>dAOHG*H!xjWHG1si0sTZhYb_HvVGbg1gp+W8 zivw(a!ledPqU2S3Bhm$zxbO>nro)VAnP zz!{^rsp0`Uz-%0=mkC>aFxdub8O91)6L=f~&V)x!>AENjQp-x}@*mOGSExI#q%O(^ z8!V_R%JjNL&y7f(@Nj4=(qs>5u? zntTNHp)0_dNrp6Q66prue?@tSaxv!6uFYCTl#4N^ms7lJHjeBQ;tOO9-w6*9*BT4* z5-~ev5i+)_Z5K95d9Pp)DS`ApLPnivJAA@NTfd5@I((b; zYi&bT34ELNu~u%g{s@!JWvrh9XZ6Yb(s)6yt*RmJY>dyOSIBsnIUgddmC3B+`O9ZM*CVfqyZ+ehH@4juY+ z%5VQ(%j69^m~<;8ZFB2|$=`0w&AvOe|C>Ff>K-00$WUl8Gr#EGt30E7HIMw;H#K^y)vdN#`q*gX=e|Y13q%VtnKZXwtQ2 zYK^g$EwP#50eZ`R#W)E$7VB}bo(hv1L4HcD$AulC_cxxZ@}#6IDUf@0J&u0W{S3y2 zqFh~%YvqPXw0^|8Qmh-%-}p}JOoI;?t>$y3m6ST2sFND`%GGt1RHN>}yP56Vb)X4m zab}Ot!^8Ua8`aUb#tuI_snPhAsdHwIZq*^wGA-BF9B8RkuFmkPwV;R8HdUHfGeMKh zno`h&wJpgoJq1l<+x33+;S{lvVE&seUhh5 zi%cru_xN!Zaq*^~y%adyt6!T?OGEziTgxrEb1&CxN&XhmhI|luAxs+a^12n}`npv- z6}q=Vxhi5Ul1Vs`-_5>-^c%(Q3vkDxGfEiVbYbNs_6gBZ#}I4;1Pu_&RU$%VYY?JL zMv2j5NsS1wxFil;U=a*nI3{UGfKRK4xlVO@CC1MQ^6dE5skS+}L-?ZsmcfiI?$xe$ zuW>yS8FNgu_&ZjscR-rh>%t{u9P~sihi#eYhm7u(%T;bZVV#$&Bqi)0w5?$kR;>kV z`DgxTxAq5m21KkKykJs;pVzcbz-f2l^RGBZU=Nz=kPTQY?XW5I#kHU^E>=*fExH7G zq$XHyNPJ4xwB)SNwwBP4{zF1FTGEsI2KY6Ko8ugjk<_?%%Y?c0eZwuGZL?3VNzBd~ z-|zIL$ZZIi;7&N78vXRso?poY@t5>5bSldmP2_e_Dl^=u*K{nKKd-BBuy6zBiEuPX?CH{)?oocx> zSF90GZjKY>*qOvQsI8WhjFoedXkD0v{=vO6e7}RF6A6Y^w5JB9$6(=Crc}WR5@mu^ z!M;UW1_&F@f#~A~E)bbiH_0DqQ`2bvYhZL(>$KlWi&x+78$x`8wlfF&;87C>FK(GO zDf4hG2ge5Olc!DZSM5{hl*wBb9(gqN2$SXncvW#coV&VBX#q=`J9x_Cxs~FABNq+s z*}Mjm0$Tjkrg??(Fczwek>c77KHaE$^I0wk-zbreH${TB3L>+EiX0t2LGT5U&a2cK zMx%>vyV3a;`LaOd@VtPCl*9bj)~Sty`tDFA(K)P7ua$Ypokk5hY`MT5=q*}5spZ#T zE{WmDRL{1NGds>{((JT8ZKr9xP*}82|fqCrg>YomckpE&B{V$#E|1QT)bU^ zdYLFCx77Gmg1jzc3&O60?BTn42W-J=G&|Om#BQf^K&;jUHarN>#fr=WCDD3_{Z3_|5a1?BhBoEBoJ;e0foO6_8^Om#(5cK-wIyDyT9A}NQ0zFnZNa#(!Fla zq=TK-=kY(Euc|qS^*VLUT4(lWd|1}e@odrj$Tw>ZO^k?09NxWYh)-f0%e|?C1~*tc zMp_o15#_Jid-8Lo7TuVL!>v!%-o({QlbB=U0^E$q;1HAW-i74ov`;4$+&tW{L-V-S z%!jA*4g1n8Jv{4Y&u4a%+H9Ru6y4l+s^#NN7F~Yi$Dq%jP8=3{MMMMSE+!&Xg;K04 zCGxr<91Z!De>;6)Ova!l^@B5$yec~O@A_NU{*D#B+N60WH5i^T=E9a}uiE|k)jo8{ z66_z;sasH(cg?uyu(0U3n%-eSz1w()23w>~<_bR5d-baBQ$dVX!GHM1(7$Xf^uOo_ z^t;+GWYe9|#^hGrrjwteexU2eS~=cQ`(1u4Y`wEu`9`XbwF&l)j96cKgc<*Md2fp9 zYkO08S`5Dt>g#(`VQg#3-W1-~_ol+w5wSNlh_^JZ7VyL6_5wa66YUOypZ27}P`|Fg z2mW7qkcOuUzn)rdyZ0sFV`mt;DV)U%_^J<))}w=keBwKNhpxB-odo$L&{8tTC>7ZW}qDhcflpq6TGmA262%dG?3I#fvNo7O(HNqkaRLfwSa*3LdmO71jw z$imP2@NeKdmDZ1&GQaDJ-YM#;cjbt+%CPo|gO_hLXGi-=X+vuTl~1#;QpGFK$+24P z@OFVA1;b%*d8K`n98)heJ>0Kq?Vha|llQ9YX!3P_h>iUX{=wea6olJ;j_dgz%#8CO_cikI^WOv)P)?!s7Fn18w$p z6z5d63x=mUR~^R-W_HSIhFeK2(b9NxaI3aMcD0&(R~e|XIVwDOZ<8AC-I?>;^iiWG zH*u_ib-T)Uwi|5|CcbB%S$Y`izVWal-NhjZ^vQ&RZI*fO0eWP^xPDht@ zXtP3nQ@BMm->9-w#YCljK6h1mN>3rnLs1|Z@ZH!?dK34;fLDMJPE~Sg+H`ZJjVrRM z@tIC8;S{3~4VrH0pIDzsy~n47y2Y5>>Ia9{ubRY8R;u_`@`1M_lPjOkY1~cP&?dcQ z$8Eutf&&L6PusnvmV4cLo~~(56{;pB1#fk*YnxgvwaOcOF&nb8Z(Ob5B{LfNx7gn_ z6y99I54P$hd9ySG=klp{2p81^5(cF@>z81S`vI`RvNIQdHZ-yS`ZuGy7+q)eY`3R- zruA{db+uihgKMRQcdulQ?%O-U(xT3Rw(%XiTHZ-*<{Z;3Ik09zW`Jn#GbJDGRabR$ zaeJhfLA@>ACGC=VNCq&Po_9lSIy8jZ?$NTzDJNr;|Dfb)d$_B>!RTHuC@7|8q=tsS zAK_8G!v}R5hFZ=LDlX2%9A98VvMTm`RQpP5B`G9`mR)_`2(?6c*KbuRV%5$mNd_7HEcS75zAntr04;;rB!{XLdbpd_`4`Q31VX{W z=OQd_t$T1rnHC_$(#w|k*9fZQUav7;bo(nhHLQ|zSVWu9cHe)+42cfGt#?mfm9uD$ zwB6LZf>U&zNls?p>a1eb#Fh=h1ERx1{Ja9j%q~BmS!!a(4&BNHIUGvbf91RAy8RlA z7+llBk^+Jj4{&e^Z|dcnklGA91Mcdcli(T6=%nG*oiektDlnx$BD0j6I(X{bX5k1RDE7wY;%m9xtY2X3cLZ##O1CnPkj=#*HSjr#L+^U6~@3^<9wI$*|@hGW9!MGdrSRQZsgwS82gqHi zeH!7%5c@Q=pKe4n5kf=kor@u-?JDTPfL;a4U}WhncO|7et6kkIG`ej^uUezKihrfB#;s&|MDES5{g003-(a(SbCdnu+bs2s@(rnI7hJzVsVx!RMD4`V5Q`@>jFRa3 ztuDOx66_j@Z3j%<-PegFbZ`7p1BFSuI(?cB{N;vOVUBa~CzqGEz@ zL5#F`BUQUVxtPpqk}c$usU%X1C{^UbLZuLKaW0xnCOa38(6sa#{@+{ro$8sn|Bb=Z zqw03+7H>`wRSK=UkWywsbb?#uD&8TTyCpVDS$8_V?$M^rhjy*gGp1|jX?<4F_``kN zyGG=$!XBbMRuW*I8Hsdp3O5B^%Bc zJyKkaKVlDFL@Z)Ryhh3eT0@KsXcVDU;F~}UFa6QP=z_5g&hF;l&CY(j;j=GqjNZOff z7%?8pF^_Ug9#r_3Y@TAxk!Dwy?PR+t*0EM{i-SH$kzz7l#d_FHWc;8kJ?`*tE530J zv4b4Wg3fniGuE!FY#$VlkQ%4EeRi&~z4K9SyqYuquRgr@6wA(&c3Ich`1BnM?a?;i z$!m;Hglz|EDSqGPUAb1_ zjpEnP-ZBtD`NVO)7q?}_g$!f@^jjvSUA=1Ea`me5dVay;{CtJ7k`S%&sH^5+Kz-z0 z!fA3N{+^eI7v@Pvt|`{#2+Spg@P!SA*SJgo!^t}=EJs8lj3KK_A{ zqjRjkkK*pkVhwS=vXig70SDVnK;JjeVA?ePG9m?><+m@7AAgzoAYy>e*`|-Z>Bg@iHac9Y;UKwsNs+NHceY}x5f~jC>26%qv*)_+9)Gt^ z{loT_+23!qf3rRQXG^|+{QcTqz1FIKU@82qtG}dr*izMat2~7MaKaagj7b4N{Sf)f z2eBB8axg28WIR8_#N}_8%zCqT`BbTE-)jf|GQBusyyl9igun7Vi*z`qTHqPR2#!RT z!~TMw6Zm{SpIMnTKcCB7M)z!xdcFuzj#=N6dKHimNHn&TDj3VJ`TTWUusU`g?9N#ywWqyms{d2~B{_OK{^25awTeh6I7!Omf z)DLi=^lJ_Q377F`+~)wR>7XRzPmm!GgTW5h4JB9zf>IefGny$WeZ>Z!FB&rs)DC9l z-kLdYm_hDUEp#TeM!8;c#0ER*<6kV zN$q&EWo!{!yo@(vN0uS1$k1idBWYuA>W1Fd9@ezpQnqzXZ!`w^L|Z?gA6;-G2Nu4s zAK6;8jf!Ags@67Frs>fUJ1GcmCOjLmd;Yem@4c71UHg-fJY+~R|5g0?v`X%K?A(Uj zDi<%}6R(R;@+@nFN6KIpIXKzcQo8JsGIYhVp)^jx6O+4&KlBK4>6tV+mmAn!UY%6LG%=?BC2ueu0)IsAX+v}%TyXA@{y1w!m=gi9J~ZKwzf97e zO219mHDSUoR{K6bet%@`xVYLQ@3SV_r+oF957n4Z=RO{cs$I2O_(<0DKB{Y z*^+N#4B6AE9SWN#vibT}rXZhmozQ%K#?+^9`)%b0zzX<6Ri%*GVceJa;-)ds`jxIbV54hug0smD8l2kJ>==9#R!ljj zSuotljjHit4194@*p!&-NLKMK=+*i@NYl|YdqB$Z_Iasby!D%W?kI;MOBvyeLa^G*`+(o#xtm_3+GIea3p@8Qy30v}g6z zXH=ghs4y9x7q_RqSJb2QGsyfjV@nkmCD$>KV5;9(uG)euV7ydM;Eoz94LFlS?NVEM zJ;I;kJPE-AT)nVZ@pho zUzMNI{MPwQl0rSBV)FO4Rho6~mDCgO+0s(nc9{@M^SczMb(kjNcp3FdV^Ah7Wx~^X z`ehuMkJ`tt;dkj41?{LU;4z<<;ZZ*m?KW^VE|#K2P-)XAT3@Sgz6=wvO5-4?W~F38 zZan3#k8T+v2ScWt8MC|x-`E_(XDakwK&@F`!)4+ei!ox?W}(lEn-Zoa{0G0t@Dv+DOC^O%Yyee zev9|YtgDYL)S%I?wonF4RjvisU*9jqZI^;rcH6{{lF?6`d`&%VWT_awrd~0QDkf#n zBKn2-*ystgTt+>eX0=z$1do-{J5&OhE$(NvhCZ{F2~(F_O`p6TCUCT&y0qT0ZJTJJ z@1##uc`v(d4L?=Fg_QkA_0W_5K|R4A%p4F8tUYgGeTvklwED#26V4)E_`Ch!+VGWo zTw=dn;$5VAJbi^VcjZ{At|#J1#~Vkyc#5A8_8#wLyne3m-)Pii_yn+00jmPm%oI~J zfHF)ViU@hU-JP&}hU*nRZzKkgouXo+@Rwp=Ff68P)32aUq`nVdoaFp*fpsR!`?-Ep zRQ;cC`1|{w9tyOfXAh&C+b^Em`uGfKkN)r=(l= zla|F^Iunc7ia!FWDBxoUD#?QjT*!@vgA>d}#RKijEY`^}=toIL(AQ^)}WtuXw+lxHVDyE;(U8QT(jKq;}Tb zd?*XQaKU(8yjF=|sBMqJI)5JGcvTlS90UE~;UFgkTrDDhxJmI7Wc%WplkgEaIbtg@ zdkQan>&UEGM;1=$)0wxIoRMH+J=|iW**w;B5i<`fY+tR))~!{Pdzmc~3+g8ZddIMb z+14o?I65T<1g_6G*Iq#Bz_OSz=uj{pnFSp zw$6hWPw^SYDx{)iysy+%^p`8~(A6w9tpa?V98Cz`LS7dZ);X`y8wi`8cj+#F)M(~V z0NMK_+rD+rpN-|RUo7TNo@FmPt?|LjQp(5gZg6KoU2Pz`^9H?b8~M-o3st%_x^>Zx%+(MeCvGQcUy1AmtEY_B%6bo0f1+y6OijT2hI;8 z{6@h;4TyouU_y}>{$g&IeZr0J!%ad!%YUF7;oTk|aPOSDm~=`uuk~@Z@!}TQd$u6v zcZgQw85ZuiHH=_B(D*~3aU{e`ailr}%QvkC-b^FM`F1J@tf@6Rm|HppgX4feVgzr; zAaOw^9hDwA`oasaB=Pc*7hX8hXC_k4oXK67IXz8x_uV@BQ$xz#cc-+TmR)$~J?=j4 z{xk2s`wp6jCcXR4#mxl;><0w}tMr5&OtA4;?T%+q|`rH|P? zvR@%)odctSc9pn+oKjJwgp42v78_&IbrAm}J6WC40^KYN$q{9m4qex@?!cT!_NCu^ z&;Hp@?q(w2a==3z^=p$Z|$=r|#+&Z+ecqG++d3 z`l~6ybnoZS%w2Trx6{)S3jWQF_f*cq9c_fs(U&g5ynGU9g5{fJUaIu; z{FAn0vP1Uoxw-c9PF~zXmf+#+rvTqS!F6(&j}Qq*fM|HN1F)hjT@7;^8iPSkPFQrn z9XM6&D{GY{vU#>Uxvy4FLKB&4+c9qUgcT@-nc8Ht zTzL203(V`bG4P(W_faNF1AfD@_*pVmSh-Qdx}y!p-sYLK)1MS8RK^o8av+ zi_w}%TmvYMzVOny6;0@nW61V5=>q#2+r#krCcPXOh`wY4xDUWRgZTKT8Kx;L?vVLv zW@clEF94A)KqP-ZS8x)YKEBH?uNemb&kf6n!jztzMEh~|=NCr*apsOm|eOE>Ox+d<)sw2Y= zyl!uOYtii!4h^~Cvtr=sc<#x!in@=RR94DO?YV9lz4G0)v18ZSN74!QoA*zd_|gL} z9P7JsLvHRulgY+y7|PoJ!w%@%=VN4#n!4eJg#ar{?Et<~%cEf)_7ubpIU*d%U@sdR4Ml~8#NAI;htThiL^DY zMSsM_KUm*i6ES}5gYj|lX!`?A6S*URydx8v_R$OXtgg`6mgy=6E~sZdym&x1gll4^ zPg_{Gd(Z0C45lsgon^iT{KeyyPGb!2afj6&R)L>{ziH(f54Oc7G3C!Sl1wAs^8v!S3s3L-#OQct0&5ChDbSe-o2FX=XTD3kG(4}B+v~NK54u6?2)x6oC->oRXx=jJBkrfo=-s1V z!^$7fqB~4#+@mrCEP^PeE(Y5l3W?hvXD*Ydyr)jjt`<&{Sf*t*OZt&wSO?bY#ddjQe@qF!lnOGX|)j zz?od2cQsI|gDj-;UIh@(kjj%m3kw*)4yox3rU#}5(1SNLu=$2Lm6bQk{%xB%Xm{hm zXGDsW0)gJ=-k5)CslctMyOdR#i?)hW8)f&RC}z&Z+gD|0zVPBaCy|==*>d0&RNp2s zH((CNv;q#c|4quf4DJ;s>r3zz0ka?adP2e(#u#1}GpkJeB!gYhNDjsc*wnLBa8RF( zgDtkdwfq|m?DxveXFlQ%%$TuiC-)Q^9vU|7sC@@r{nF@Cbm-zUvW6yS=g7Bk|3dpv z;Ek_?E%!b5Ts<1dJp~CW)0jKx*l%BNy$?tW`DY(+!{{)$ zE{MFpgOb783bAz*Ku(+z0)xcWDR3*auf^CWqvGdJ@@J+Q(ZE5C6AvHOuu-jV9UKyw z*Z1e9y!<=a1Fak1pD;mj^jo+$>7OSnaNxn+?s*;!q}ObOHNLlZ*=KQ2HMPj2ZPU@` z_Fv)cCIKgV06xrVU8m65#-GxcxEVB@dY`I3a||3 zC9ztAy{A+U3y)rK0a8l7zwiURBv`fDyu=Lun+i>H=bF%m=8U;>GuY+uy4&;65Byco z_i7Z4id#Pj4P~AW4Ykc+<|gH=;?h>-Fpaj`5_4`wAFTqlB$W+LU1bDNdGhhgT5X>r z>wEDqSnQ`nu`H2W;@Di}l%DXdLq=iO!&Oc(54cLFr|so}Xac3NmQzT@lc+G7_WkiY zvqK_=Yz4Buw!TtP#Lq$IGoT@Z^Q(CALs%sR#b70Yg%3(z`yJrs`9CE@7N+#ezUN`? zX`UgQ9-lIqQT!=N_4-l!GPoD$ALdoKR}uxet3(fg>?I8(z^TUm5lA>kTV{p`Y_M*y z)s1_rW4+Bkkhi`eYYy8OXu2t}KHW60@WF~2qcLe{R_+?}){;K&4!&<-^Sp*_g+ z5F2ML9yfHrz-(pq?du*os7qOvpEhztZG$1Q`|hoKj~O9X8|R8L>twsZK1%@mBHapd zPp$#m3{)9@1t)a1$8rW7Mf6rC(}Koq#l4{G7rekd_TB?6EzLt$FqdrB#|FqUxC2f0 zp|=ib+Vc**s0j_hbM>i9U&?CP3Bp`8;(SyrSHaVTKU%^5kj-3*hr+^ezuC*b2wVJ)EQf!{X^-nsQ1^mHY+6W$6l4jSl>pUFjEciW2X+eZw_Vr;D+-N|fhvfa*1Z!Lax zc~ei^!jtraoJ8&i{xk1O#NgKjz`K8$G4{n9>Gif-nIG&o(6_e!O5bYV>KIuQ(4&dD z1BL`~K<_}WN!VsLkZtg&9oHMk zb~oqs5LHmB z3OSx9Y|9{K1<%VT+j#tE$@2?@x=L< z_J`ST$#IyCaQ)M8|88U(>JngE#3$5X!8Z7niigPYQnC#^FXQ1a!`s+*gS-(uUeN7u z{unsU!vkl+HulMK*yV5>?gZP?m@)k8^t^xxO#i2CUJ) zljHsP!?Uz7mlHMc~%m^6oNAQ|we!IW({Cmjx1BCPG61=Tl zBpg>Hk^5gKY_m;ddl1PQmN0Duy3cHkSkLJ&Z=kwf(80&hBhZ%bZefCpkfpCOTEFrgqm39}utaV@Kol{ork)jQJOOt`cp0x<~Qdk9Hd@FHt0L#&XP3jrvwM_Lhux=l!Jm{~iC8P#j` z+t3)CIrtMw$s075yGUoWegka;c2_KInRUlYP3VoC)mu=>n37m_?!Kny|IU5FmQ4Hk z^rQp3wsF&8EOd`aFQT}8JMnlJ>K?$|0vJyk$Of3bGAJb^<$|hozR3VKha!;$6fulT z4mGflLS_Mtoo1+42NqSGK2ugu+HlX=gs-0X3WaT2wfO<=t<#H! z_i9S2YYwYb(mO+%f@obxdC;`#x1V0IU!U^w)=z%CZRj^MH>J_LxcH={BS>x{Ljf1C zhY615B!?8`= zRKY8G&Tub0_tpy^-^^@7=j`?b+gD@I#}n-R=nq2b>HjuyhTvbg-hY&@17`tzQFz{d zz$bWSFm{mNMjV>QyaxskZ~3YVv@s@#apI&t-~({Nl@zn$)G03gjW?P~AI-^lwCd-F z_od#+y+5vI+Am1PUHFyz3=*fWn>!g^2O2vDo%##6jWg^lPzEunay7lNyg95Whnu2- z_f~g@7qV`mKSB>;Uh4q^dY|ACbh}eGfnwUpg};w}YJH#GY5$rVOvl@`4rXnF;O#uX zn*wUIJ_ntG;RplX=mwI!1mDkyyCzT&n8mKM+JbGgG#&6R6g3dm&_TX$ks6zztZH3~v{rU0w>}*}bDrkIiq>|K&;9&=FsPy8 zdtAw7ysQ^`=*nfh>~`s~1G>tkF3hn#32F=FvI*q=;`O|&-wL|G*?R%TBh)|1Wedpp zE_Kmx+@-D-wq0sXVOywWl*{Ij>v10uXD=H#?$QSUwuQQJxeSa@ye;iR0^35}w_FCA zK!8W+*W)-pp478X;;(n~;F8OD-6!;vmdkkEC-gj$%Xr;J@JFs6CqAFx3n&fu7x6QN zYzuYta@jQTdC9L@&NUw|! zg&tb$F{XP|x(3#wNbTo5?hIc;$qr_|1Pi^RHI%R%=3YZdXL_%p{ODsOD-&ucxqmV} z_#(=ils<|mdp`62S!ogF`uUO~O1`%iz~oPFt!JIRtfalQzBuW-x0VB^{~`U+bJ1At zOq3ksmaoTVUxOGG#AJQd!P9y6BXr*OTpxAt$?2|j@XJ<6>fq@|;920MAY2g&CJPDE zndwKM_M2OSdbwAB<2vX!*>W1!L1R4zlF-CoM*{nvbdgIXlkYnV_31?BRKtzb|`?ZIH?bp|6H~msm?4CD#x= z#p4e8dB~%|+XcDw67>Au1U?Jkj`<4D5e@GP!hZQ_em@I+!C^l{F~P2T>Cy}G9N=dY zcpKnl;czR&N#Jc`LQ4}bHQNyUb(c6YNBBvI`H}99o7Ch!-_ov2|ReVK_tjq=RW|547i1eA+g_{R7QO$?No`| z{fSNYB#i)}gC&ihA5;s^55psH=`n<3#eiP39{!4*kL0936#Iy?+0ft{o@(8S+f<-j zNw*>H3)>hNgtO_8%+WSe()9>B4>@{hVm|nT&^wozF7SZ^H&rk;h-f%)V{W%`hyi!b zBFI4_+YpC&5W4U8j_^lAW?fe&9|{sEhp$Vhkv9uC|I z3@MS8tXyabLF6Dh!3)ca7(0Nq+eL}_s-r;!7K%?p>xePeKkdMpvxfu>2hSU@=bZBf z&qq9OFuvfOYRh1pWlntIxq)9C`j0-3r}B| z0}sGhNdGhGEF_q7Yrf{}j|PK+xVFd)BHCPpG<0TkbZEoKlC2a)yORsOv}MD+(Li_@ zg$8Z{-5ul4W$qEikKy9!j^#6ZkH$LXOc8l{sXy47tEsP6@V3ayJh)4&JoWRI%Ygk_v8mZuZ7});7BqJ*icvFFe zTLCXFqKDi*uskKQYU1hvlY+Fn1~iPSnUJd;P`6^%Htu>D4J+$blagJSk%6oknSGsv z(}s=BE3ndZui-c6n0NF_E9g7Lyz#9q>#|KiyAfIa%gnGIo!QNNT{<{oxPHBNh#uZ@ zgx6guT-k~svLVaew5j#3;E`xNecYDD{?Zx>xWe_{;<>N~B2Fq3{9>VeGI->s-(F(Q zZt9KC-o$)lGj61h+abaRey(txhU960-b5aF04(6uW$}iJJf$jLo1PXMuTsLd*nY8b zQOZB=5FSGxPt(RnDV54ZZJH4u!spocxj!7&JM&}KKo(0LRfxL@VzFpS_ZWi5ZbBgG zn1Y`2_RyDMj8T!1ssybOe}j=EXfFl{14UIzWkPJ<*f=#gfKO2=X_ZoysO{@GJGM`3 zyjp4d)ot){$=I_X=Ftl}PC}g3W0*W6Np_PyJ^bL^A@vU=?oEIbk5?R{mV2k;V4`4* zl{!Kw_|i8H@PvY-w`~J-;6;IMF(3hZK>T(zHJ+L*aE=He_yRyn%7d7AcN*DC&UH*S zyjRM51RdlKQZma;n3;I$IQff?!gLjR4Ij3D>Uko(Ho)UNYOo4|_7N_;;Dlfk!I$8(_a}2@zmS zf8lNFd&rw$C+@KpsHTR(I|5cxw@@wA2A7#DGB5=AfPs0%Anw(Xy%MZyF{@H!uSsNU zypzT|b0O!>(K}H*QKD^<5u7RIam8NGDQ@)K#l2DI5Uzk|El4h;QpM9l>og$e;492& zLH2p>vft$8+KY+Knb0T*0wO!%dbQI2Up#p{$H9vUe1001Li`+aP7ESl@mx$0&%@*L zcAD*Tj|n5{C;a~NBG0keH`~E;P^YDm14}TKl5J{tmUta0CV+3 z|ETJ;g7?%>v#5F0B5G-y{3Ff@!XlSL?|Z{3GuBhZD+6Q;c*Qfy_0;l#H4sD{CD>N8 zs5S8Z=#4n0-^T2{OxX9sZn!c;@nK6`2}=28V1!8Y1-37SK_0+LYA1CkbuV>)Thud( zvRAY)u9Ds1jhyUzKq*R@< z^WA+7RlNN%Pm#mT5^Nb%ElNPav+?TW*najft)SvZ@Z$vEzgV;pc#SVrkrMx(6`59Y zo_7F!#0t1){e#&n7r4h1QvAe*)IJbn4+C(2BHxxk8@5nKaMGDrg};dEbWFo<|-*q54^ULcvz2hY@sB838FLmj4$T+7tI+&V4rOyR@mzjipqA3uPC3`oJg=Xqxs^o&^z zb&`)lzTL}EpZP{0xJNJg+@b)vT0TNo^oFe8)ea4AhT^kaEHvx?O zpqE4y0g+~6*@K3F6ddwZI4F(iI8Foa*!?V}?H3_}E%D&tMto=|fRP$3gI&v=|Um&7jc+QA% zOZOQ677A=)4%at>{9@cvu!;Hn&ZQKlJyZl;E?S&47L_g2(H{!o9YrGQ*Z zX$8;a;uKzHl8GQm^>Nk<1)hw&Tyss$cB@JXFpj-RB3J(e_oq#T+9RC*!}}-hsatZN zZegIz6Ym7+n8O$VSHv5cB#tEi1nl6n<4Fd>VDU;ksc$0GjR~~FIThEMcj=v`*M`sO zjnLPMAL^C(;G2s6i8tRA`zJm*Kvex6=X$J`U0K=dl`XCbpVeY9>U0g1DUkUHlyQm4gdvZB_UT=K%f6EW{%6SxY)p1=f^7M_rxqRHmbA~kBdX;UO zz%wP!IBiy?Um0UIz?|xhm9B=(72)LFwteeATr{t!Vz1I_<>iv&-ev6nqR;JB68|sy z;ofGM)JNof^Hk^@ave2=T0!0B=A9E!Dyit!5F=8`_fo;Db2m>({Wx!ky48=%$vq?m zy5hj9hDGJ(z`S34`n9j!6&iLU$XJH1@( z=FFoX`J!=0MkwD`0auda<^aO8MR$@>$^N~tuto;vbasV{Na88JkINbs@8g$H zgQ%(0jnsDP5kb>R3FD~|uMNWbaR09+x%~+{RNF5&s`J3+gN+!m++wM!J^+7N$%_aw#e#?|E60K1azi~m=Zi$#A@$T1P> zRknd`_9FEj^>2hO9~b)*4f$VXZXaQi{~?a|=GM6fPp;}LprAvn5EJwsS2<1nllm1w zKSznRAPRMiB~JS%kDe*QH$%b%*IZs%)lBq$jH5I-xHL zyqgfemz58`@Rk%|NhG-;)5}ZH#Y5W2_{fJ#fvkL*_-yDW<9IgKi;jLWl87UjDf|c| z@l8qYhIB}fv){S)rYoGFIB7_iL5jG0Cso<+;>e*bxpVAN|A>-|UW!1;@Ci^v-AZ8RtaUtP-CM5Gsh%g=InVQyig{ALf%*(L+zGazW8R7U!Rwr^ zV~_EQ|03z+2{2Oqu|)!pXk2^m(S}tjZ=g=YfqPH;gEA6M6Pc25AHS22lZk{;il_1u zSAkFF>tw91cOboIDiIc_&g=OTCBe4{gd5UG#p{`Cp#*E2r)1_tEnI(3z|o~T}_ zrxsRPkyhX>z=v)&P$bUIk!FP^+P`#!8D|8XbU2h!LF zspAQ3{G-b#?W}p#6*PCk;_@H$xYvMF$4K8tgLAs2zR6{?!#YBf0EZRaV>SL#De$2N zF{aZ2(%~E}H`)oYAHF}TXdeWWdVa)zt|C$WG4HM(p?#Qq{!<_7cpS9%j6IS5sMk{b zsUz&bTobV>e>vo;$F%(C80i`V?+Xh3QN0>p5`%1Mc)pSLN9{89JfdIz#tOTF z(@wW5?GmE@*zbjPeOS@36zgH5x%NlB0u&Gf2_DoUmW9-n3piOiYjX#lRGqNIz1V23 z{ZYG2J$|J?q)?!ub%CPkRH80_M+*FfkQ_eXX5l+yR( zc*KDS+wh4bgm1*fBabW`e5(6`61iNeUnFrI$e9XwA5-~4QO-jcoTO#J3LA)gXNDHe zvXf@uI8K8E9QU)7OsF*GEwzpLd;+mQswD1B%p?!Ey&|TIc$I|5ozS^Q$0c2Y0)cNI zL3HL~Akpt)54*wsm>-6N~q*qh3G;D_fCZH-6+Q=Oz<;D zOrG#Eb;v<}?zo<(l-R@Wv3}AU)i?D-MdgPta+xBaUYA=s>!(}+GIzppxeTPoy@yPDN;Y^%IRyLhU2Qn*EI#Ggb11Ka=A>GYhx`Yz@qP} zFWI>9YapyD@}33<&wBJil~(RtHck;OEzub)a;p;xFd;>;CK{PyDN^9E%*Sk86TBs{ zd1=m*&Yi6n5%u(b;tF^0Cn6Heb7_#l6y7^gq1$ zp3wXB|AzO;+oTefY?~Rb5ua3K5K-!g93t+!Cj3;dtRwM1gtZ*k_j=&or=kuFXuj3e zQx_6HBoLb5GW)dNxXjHFacUAF8`|0MOzItg=Ri*MQGV$rv7Gl7F86wG;lW;`W8PU$ z;*SWtC(ZN2?*+lVPcIYqNj>0e!iEVjQ+nf?t6&iYo>6&ba_i_SmakqR?uA}8K7DEE zefs}}H!aRk$wc~}^s&87s{cX1yA63Q=G1%Xed^+k6U&Xyrj)Eo@smNio97qrb>h53 z;#Mb~PVONo6zA{Yy;R)IO?U54LR9~iX*(*q@3c4j&|7sgGJ9#HYJZDl+PSt|<*#<# zN6+wbHG}|N)~USpKlQ`2zcz^Lm&<<@N$w*QaJBhB>N5&8`!4-Yefx}(neAfAUmN`4 zbuE)t2`M$WPjGCXSg4D%{p#BF=PL4yH`(ALyl}PoNL;^?#vWYypZew`G5+I66zUpg zug-=gt}&MDM7ZLrvZ1)HCHRrvdY_6`>g9`KTpPR@U{BILym1w|HSpe~SfO}TS(uXg zlYEY%kOzR*W)D%XQ|G8(Jfk6m;kuexC$ah3$OVw_wJ58l=DfJq-I(W_HsKpctakDk zcT=A1bZ-ejE|)O$Ws2*&M@HOvoIKXuS8y%?1ge*z8+!|D1wwA8kZ$M})5E8ABE3Mu z?@mBON5r`c6?$nn>AxQ`)63P^4}l)1?Z!`>Jx+Npa}(bFv7S88{l<9f#Jx(;zrh4z z9=OF+{=e$;>1UXwP+(k_E^CAdogQ))Z77XC>?1#^5KUdM@Zj0$=LgKX5`TN)ly`o zEQRTYRJp=ZiOdB6I+FVd2ilGH+4rYbp%K%D7B1`C|4${`9_J1g`wNe)YX0n$Vw69w zW{|@)u_Nwy0T+E7rzDFHMLa*t@P_W>tAN5**IiRGTb@vw!_R-l{^$o+avvy^Tjmj_R+qAH>w5cvHKPsx! zdfn0<@UwJ4N%1RH=|*E(5Ax|LqpqXIcLHM|YIvr%%E=TL_?w^cFb}ZM=4S$)9)6+; zZeCcfeA6xQdsCWo3ws5H99n;zW%Q63GsKV zxw-#7XiBI1>^XaE& z-u={efPTB%LX+(U5BsB{6R^t6Dv<6Dwd^Nc=;@U`s<-544C(DJBp2)-@9f=c!jPFB zW3KDOm{A1c$pNO4HYOJ~KzL)#Wc3pOrab@pl7lB-d!}ScUiapr6)TqCXl>5*7ns(6 zYc-$0`No%8?ZVWw{RbX>G%Zy+A5~x>7ea=613;GQ;2UDo$rV~floHg6TnU4fb=1bC zTUnKC_ucpG-gD2e5jTww89H&o(4iA1q8)zOyG%Cv;KPqT_~4@lU(0ANMyt@=-MhK1 zyJ!1n_IlbBow3rjW)(6AgGy2PYo%XUiJJFv)6wCFxg{(8rOyR!51Pm=<7&`$ZWh*s zqCFQw>8Tv>GKzsVqGk_9L?>Xy-lk_GaWGD6td&DHRH|>}PH@L=R9CGZR&KS^?fVz# z(BjHIJ9El*@8M>n+wQ%uEN5q*9_(p<{R@8Fy*oVWHk}K_;Hp}BjBy7 z9UN;HoCv?b>!1(eFAloqL-6?OPmSp)wmRxS_=O@r{Re+h(+U59$442}hw8AL^X)wN zg@d+y2Y-R~-**t^N(bkOtK;AoOrqc5FBIPV27bJKI?SnUI}Lsf-n-A>FCcCB3}?A$ z#S)0CW1k_Ps-SQLxgDROV^X&*hv8Ol_N&^>er>~F;PU*lV{aZ|W;ZuoRtZ=nCM|7gER%jF^M zy8seA^5emAJRhTJ)97z&Y;D;2?ToQa)81pec%J$guiwUeTjTd(zqbR2Pm^8ZI|}@e z?Gqw5BfZUGOYQP@2I*poi)&8&c&dDk#<$_!)&SeLwc8nS+vc?gK68nN^}!(>jn20P zkiRx`JD~Y%HoMcw=#x%@k=)kcMZ372;R&X&Bo@l^Nfzjq5kAMz1>?7^VRz8pZ3ni_ zn7AJNR!Q~uiG_|DwQYg!rybi4$bOry9`w$J7?*F^;M-Vj3%0iG({_OIWs-X6jx3*) zay3HR8mnk`wlg}>)btn!wq1NksJ4yEwg%W)WJxN^lh1YZ0u(AHh1e5?p6*~>2F1Ead~c7k>zuJxc_o}kc#^+dJd_W8@-`H zLOTPzS+|U;q#(j8E3(MpRg*$Q8vg2FHIA%0^F!Nbv)ngYE8yEF&7;DB@l{0=%L z9mi1@eH9*`Bnzr55~ZV#Oh-`Yr*qSB)OE(^;qlcEx(aqt2K#o1I)bCNoTQE;r31c` zFc-Tt4_zIrj=(HBTOEhes}IJ*Gqh`RrJaeFr}taC>+mV^fJZ>|MiSo zhSWxx8X4R2=GfRK?g)}!j}%BY8OgZ6PI?SI%^jYA{mx0;v*<5ee>Xploz1;@lzZdo zQDi=fe&Ob1aR1_leD)dIhf*_`p&niv&W%FU{!7h@$Kz~A$dk9tjAE&J0t#iipT6favQk~b{G>{gci~fwwvIdFLFJ& z6ZCg5a=afy0%LEzO?V!LT^xTX9>14+11#;`p4-#*0|-E_&(OMJxLs_wpce^Z2<9Ns ztcN@L(L&hUQvipn_Jl`ze*{!D5`1~km067@aQERypcmaA!YxM(veEJ%xN7b=ex3`? z)UNQLJXKpyXzT8>U!6C089&cY&M`|V!)B56zT+5n_C(JF8ZCp}ex>nBW*M=d0(YUK)iJekp&tm91mIrIh>i=_pjMVf z_VX;-GxCmG>hBr**oG~qvY6tnQ>Sj7y1F7Zsxnr!h-`e!Z`f0dOr|2!Sn_!;tz_)* zN>nOuO=+01aC<}D9aeLO;g%7Drq9?rrlu-=@Q{QEhKWcRGB~}eX3X9h(+7>X#gJjP z-ci@Eec_CTlvX+SO6BmeC30H6_UN-4*Uvw2a^}hVZ!*W%8c<5gS(zMC&A-l5TGfP^N0tKRjV*r5!Ck>fu{H{E^~rvW0Ev)vTC%ItQAsy;S=+PDVH^* zM$nN+u1HVQu?iq-E(3cQe5pp8EQ4#4S#6O~d>(?7VR%BJ6UPgJE?@11cI{r2qDv*CY2j~G=-Qe4!TchClY4}CD_!{=jn*!#cp z&i2J=wW_%KGHYs3cx_pIoT@f$ad@P9nq_!@Q*>yUPL&^$*Eh*VWv+<;rI zpEc|HTL#3%sZt}R7lCRQ*g+`nD54TKHM-Fg2lfLV(J7S;zu=T&0+1Gv2 zFE}V6JUg-^xO7Hi;do1G!Qd!UY@8{kF)3ldfP|#R7*kxVDQa**s%3m(%wqTxt$VQbZGJmq8U! zJz&LkC3P4$9P=jm)k2!O34W1Qo(gPR033?18Ak37`*N*TN zB64t43v+2;GGK3*E%rCkk%z<Y8Z&ciU2U&^ThZalWq(^%IchCu%NnH1 z=v@?J>E5IF^#juKWLdqUH0ZFbsdr6xOH5Jk4BeomVexqxN;WJ!yBcke3JKA$37J3L zx$y3L7vA|(W`ZmxG$abugm5oLXi;)J_s^+&52Ei5?w!j0Gae%g|C&D5zNJi=u9c;PsTHc3A+sWNv*TwiI)0Wr$9;D8rNy)2XC*6Vg`ka- zs_!Q4PP&-Tw2nQ zT~o1QOG)z$J+{X#+E^N_xpiWA_{3W^!KE7)#cuC$LvzWN6_GU&gNEl;=5oJn-o1Mx z3WcwOhDSgEDiqstLAw;8L@y>$M#x9P`ES#xJE;BC5pUX&AvL+?0=z6iIrJeakCIF79q#bOdz-kdoKwtyRW>Gp8*Z{K~S?-8HAjv>{DL`oD zA|eKf7$bU63(}9l%trJr$h0z$hv`PSLZcPrFq~{iW#SqaHa0HYrm-qD#md-hV>0~G z;bbRwDK^EJjek)xId1#BdG(tUo+)eI$9=!*yY=YUgDZw4C#4J@R*D&g|<&hIvZ4Oku6kO%7fVSls;NTRz^&?jt#1^?1v zWIrd1NlDYeFPg!hVc$1o?}&yW@6YExef_S(Yj+{FuW$#>j z_%1oMcg4E9<$GJQi$+(~PhUQF_G8wD2J2(9=PsXKUp2ZYyJfHZ?sY5dr(+b6jit%v z+q=iKEFV5$!tmuSG2L%BCzm!xf-=PKOBMYD4OwR_q)K4Lss^uFA)+byDk&qjqWG^e zi5tL3z9YSo(m{B_sxmMtgGys8vKnL-gN9x`sI0WHv9xRuUDLR5@L>32o8@i?x463y zD4P9<+iACPJLsoJH0)isw}B4po7iO6U7yHYDjNj1BzGczKXSF1!6$KXX;3rAal26S zXQ7$M$UTHcaUW&g+1PaW@DvyLwO}=Nr!272T1EAQIpX3uxByCUVJJ+8nV^?^SG0iu zla@*F1X8 z%W}VFq|`lB6LJ~#K#nwPk(YVc`jvn-AspaD4tDLaf451%Ia^~Jt{<}*?btlVa{>zB z3vQVJB8~xoFZdjfx8;lA`ppo_b%9TqceonAea<3W(*}T6u+V8u+kD|SfTvCYfExwE z3V)ph_76gZgnm4HWniJZiC=0C1u0W>#ue{QqJ9{5@ z7Jsq-(kaGeO>fTLuzvQNLx;XOd;Nyl_Fp>6w+du$ zOfnV>VeB%3aVx;p9bsEnA?ImL3XVaQ5(gBDT~T40UWZL%4IiCTDZ$PGJBr_{Ez;|> z*bV}No=#ejn6U+I-)u@+U@SOfw;d{gBos$5>C=cWxevI1d=+ty+2PQz_M?K*<-+M? zv=Xg?RucabCc)>N?4u*X41eKgREO$&KKbba?Gl7fQ=&1jG~ zm(N9%lti4%lxW|@jf3eiYmXjXyLya`dr+7gXtZq%T~s${{`^68MSuTg_UuG#YdGeF zA`f&;1n@%=)NGcJIRQZte&&1NM=LWC%wiYA4YN3=iNH%Xszl_e=yy=}{@mMil?NIa z7WVhIzXE8)GRzwq1%#k~twEV<&|e=D(Dk@%>X|w3aYtMMN9Sk1H^*KH=-TWRTUheZ zNtv1F?Qea}z3s$J_pj+Z$Pp6cdnD8dn81H4aqv(Zkh<9)0t4kBGMNZ>_yWt*#QKN8 zBj}aorpUTv;$88xVUly1W?VsY_Fx8V#5kK3dG?iiR2Ie{NIdo=Vr#Rqq znuFX9JlWSRzP?9R4k*(dEhq`?K#48Ot#1o#>p@?ca^RSh^t1$JxE>+ei%!EcyIbB& z8Mr+3Deg;@_}Y8-HBDweL}rKHb~B`ldZuN>YJ=1zGI_2P7d#Jpr8UAHB!I1f=$Bsd zU67A>7E9&67h1EF0~wrEz)F=*>F-8t`o98^g}>aS_GP~mJ}t@P>x0+r@Vg^oAO>fE zFiJXvQGjS#GZ?jcyx$CtEAB3?h$bhPV1HY1s@>*|zf`8sa%(2HJj8#1<|9-T#AttUC>2&!KNqzbx zMda&rs5UO9q4wzCD|=KKBe=}=@nYZaBJf~NuufTqEgdt3?Nq_OH-O6s^v53+$RJlp zw7tRP%-+}kJ z@$+a`RmYUY$DKPD=Q_`^U!($Eg|k^LIAu{EqsFroYa@MI)Mf8L`&-YT{WDKF6b;AO zZt_VqW@8Kd*bg|g30=}YCEUk}bt;bUka2=c5_CixuDi>uYhaGq77N8AVP9-w(G zE%ZiS34miy-0`y=8@Gb=pEHQ~#d!9GS#Be9?BZ^JQTk;|3tH-HRIai;0Vr;Dax&pt z7kQ1xXweHX5lbO71CwrvOx&h;Mh^heX|#loji6!hjf6Br-t{hpWq|RV7J}(ghIW2| zn6O||1c4{7g!K<>p%y_b2YVfO3Km*1YSl(-I5HWv9b*ysbv+V_z}2u-FvmiWrQN{%5p;QW5_8R4yW~@wPCTw?-4{AW`U@CCt zE5?0IUFoDR`ii1GGO zf=AQlq+!40euPu+D(n^-+xtZC#IRILv2Lj{&hfo4BMk43RW40o#^76Wt3>yOgd*Hj z0Qw4_^dmQXdQ$2nG&w^*etbzr>ZC~-spF?7!{5}567cRMd=^S+NSqM&`0ySdWx%bK z#VEAI!Ik30MdEq}Mc=$`YY)VfmvimZz-hC4U5 zV^B%?Hw;$AaYt4RmkXnIP+&5@O2METIwL#3m4ZRPBuG!;uTLrJ8b{>UFN$IHqOF

WD@#T4*yLiDN-*s#APg~XSQML)$y2;b!68k|Bx9Wl0xeS;|C8(7GCS*HvuwIkO9 zme$h^={Pv7?Xr1w9zujPd&r!I-9QLvo03>f=WHyyvDgezz~a8yCJ5b|ph{k1XGTK) zBi0=_j@OHVeqTmGxnp2J>!ZM6gn4hN(xKKBLCHgsgXpr$fTBG_@q@xwE6dB3OjDo` zCgm4C?%54^AFr=MbQ9vjUi>tKBcc+EN@_WH@QZ9Mj%;#Sf<|?^Z(IZkhH^%qri3TI z$fiPY^HW#j5S@o8sQrib%?eM35&aS+l}PfjtYm%J(H~9+wxE;ur9ex~NP-X&X7d&z zX5840PV(S??tY)Lj?QR5cD&k+2~mUV)IeXhFAtd?+6p4u!dzI-&ECXOl|pJ@JJM33 zx`oU!5u^A$8sef8EWSeIijT+nBS-rRdi`k+lrm5EF72-qmY{U~OMBx5Y+sUbHyr3rhUte-3 zKa<*_wI!*J$9g1|^svOM0N_o5*A(nYNv#Iw`xRQ-()qj-g$@%B$=2PIU?0{bAn@T6 zTNOShgTs6Jk$6Y;ZEln@IK20x?Q^^oM(j7?I`%?hd3yJpq<~399?};=@LYxigpeZO z#X9pNP{saaiv-mE8JTR4&@aUHn!&x(+{@EQYhSq8_I`tUL%!jJhJ}lu5v)`fN^OBM zSQnr=r~6z%4S*@&74FVj10L{4IAczsU<<@S4&o@N7Mw{fq1I6Asjbv5>K^I=zy;~! z%fAc(Imjp@M0E)KLQ{-Em2OfQEWqL>g9W&pY^vl6l_pvdV}R%tJ_R-n7MC!xixXaC zg@cH6vgca|n}in6pkz^uMr#2UGC>1;Xd@0=kWz-f!>2~e?pjzkbo!iu4f8px$RAY# z)3k;QQ^r!8Y^+V5wYj)9D@&hRo2t*ssx96;JE_)~TwCN3r)KA+YGd^&@fw4Dx6WYD zq2T!q1LsU1TDR~|22Fg5K31Dbr=+Fmrs`862P{ILniXpsXwb!FqKH&|S_&KAICD|I zfpg~Az9l^O72!W*73SUDe|jF3<#yv{k1XnwR$YD%6kV{$-c0X8EO#6>tJ8X0N22YS zx!lV~*52dF-1Od9y*^fB(Ah%J1CdeOHh9dyev4){#_0?iIE0q|8HH?AXM_a(7#*CR z9J@xJ^G;mSJvn;2BJ_B0w8Rd?ydUa_zvU3SUXWq@UwCmPUgzOgz1o@BBnTwPua9u} z#<6knh))y7JD;mb42YLv+z81=l`AgX%P!m*U9{o&wEzobbMzrP8I}UTg2VAFlGrFW zFcNL-Pvp^c#>-9&g)AXWcz9Y=h%C&MQWhmB;0`Q)dBp{Na$!MaC>xd<9TO3m7Oh0b z1Vt?T_2qB7Aw~4*I+5Su3Uz1lJG=tf!2}OCh;W=l$cK|b3|$Sq`zC=`!%Hd`pVYz8 zfy#B0?-c}%{jj^|YReUvKXAEiuM;^!FLd#8zY*p{V3OFxSg`^ynM~FqoYgokVp}xq z+Y%H^_O3I#cAeplc{n+KsD3mGU%QJb19!$HZB&3)$juO>D!=Z6^}9Il1^tdA^|&K{ zyPmC3Pb76`q;k)fUgy54Z?3Ozw*5t*ypW0Fk)GG4Cg-B@9qQ3TK7kbdvmml+6bt?a z?l(j{RL8oJ6QFj%Zi{-t+L1kpP>~+mW$#^FKg#84+t@j8ou{$kQhAUCa$psp!*E0+ z1v*{qfJ3-Q3=p7m{58U&nG6aYoXD_1>N>3|#H;?44ndUzicvC<)DERXu=H;&QsWe1 z9MOq{Lggd`mWvLI7%+pnV=oL6-ywlG3agGJWP}76{vx;#YBKOWd2<)?LU2tG|1U-s zGYF?P+4~I|w5YL>`vWh*(Kiu%+D;Pe+VAf>EO!00in7O%g8mlCA1|wzwmx=P--7^ z7BUT<5%SrSD2V(0iE|+{26Jb*Z}Uf2zW#Mu%IuO0C@L|-Fzcsz*~aR`@~YN`WZi() zs`A8YWA?nCW*IUPx$iHO%uY%B`lEECeq`D8!&23?OCr}67v)8S9Nc>D+?Io(5qY!M zMlPvUrw+TmY$Q-jn$IreU1B{wnskho7=v1g2V#|2r_%k#HVjFcXN%OfW;gTWZDX3( zY?wTL%fc<=CvRBOJm$7M!MOaBU=DH*dl-T$LO| z2JCaI3iiZ^0gLL|7}8Qp)^!`!5Ycl96b*il$3mm5=RdwAI63{51fx)v#G zYu6Pr`Gs+|KWb|e;tKN_F?6wi3->4VQ+CEwHkiv0=l(Q7I$Yn&)uWSC*r2b011XL=3 zj4vowv68$6;_I=xG!rgpfvipBQV+=|yNWfD-YO^+{uD~e6+)>bu9GCil18XEPldWT z1LkL_K9X*MQ30Y7hEs(gtRct6SE(iqbAI)h$|aMpUtD?L{Dr%3lO+{#|KaZ8&X*aO zo%b!Zk5!=lGVYn62vn%yPDW{_{27-=-aj>7$-SXLxe-C!^D@*&?yanHm8_kzN!fIE zMo`Q&O_Mrd;W55&^4P*8CO@VHFPFaN{K}Q4WyBX`rFGM-n_%u+ z%JqqVuZ*&`zoJ$#Y!hzlv1>LOir>c9z#+Cf>Pdi5F! zX~!ovir+X!5vmB;CS%u3HuVmfI58N)e%Jc%(>*ZI=mUxV#E(R7iNmXC1E4h7c+g zFq`3mT;MV}a$zZd7)qO|P7sEF9?g~$G3Klb6V zg_-fnH2MooUQI#tI?QmVoL_l{%TCJxhARYy>m6NLUC6A+$o9fLxtXTyxa@W2a^vs@ zcb==AkWp@4mmT+nS61y&WXvv9B{6l1TDc!ASQwY2D$F((m76oCO%fP!?A)9==!D36 z;y15M=qk%Hp+e;zsP}z_I!?XiOO_EamyF=GYru9_Qn2BLP@GkZO9Dx=4!$;>yrmSp zC72Cx0w% zwqKSmJY3#gADNS+)rZCPtjSCtzl@2Fi3Zn_wzWgzec33%*U&2+G zp&Y#kaFx^LZ7YBr?N=PB_`p06qBR1G;dFiABq7JGu&BKT1oZP)vZDa~c}QtaZY56i z&!?-qJrG;_pZ6>hd8dMo}S7KEUy^NQnAnfQ4QQ{UaYIy(wf0$Xk+`)Yo=L3gk@ichr+H z2&Y&q_`lGUg8A>M%^8UcQY{BLJ1JY64T&6F#kJU;f?K&cBdc$b<)E~mNd&Bfn_!iA zI@nDIsiPo694Y}v$FKtEQr8lPc?C8Wj;Dc%RRw`u(z%3k=~W_+A)QLdhtmibY;Z8d zrPh^NnnpeCMX>!}l?kJE?OmcsRIDE^v@Zz~ zteqf71Cz)JH)pz-?5(9018&$fGB;=BrW*!Spbv??1#&0UDR%_^xt`ihJw!bLQs)SD zEWlh8xZXDi^4}HdsR|<$vXcHfPCpZiNh73z1%{6p@z6aKh$O zC$}Q4z((j-l9;xfq*eLPQ&&Wraq7vF$CsD?au5c+8C@iNL|1^QJOfEuIL;*4Gdk!W zR!B8L4>8DM0C_+bM4;SeO}f&_u?U7iJw456q_fzp+53jB9zh?Iho5v(05!2g2} z8nRo2Zb-5yeLFA--Env999m*h38`s;8_&g{P}t{lB)v{@0U>%R); zU^xpVf8(jb&E9H%Po>jDdvrL8&I%R#8*xqc7r80U?nY%Fpih56BPETJP>-niWMvjK zR{}o|H&;^X)S`G|*KK$vq6(MwWHXEfx*_FGj!mkHc*dm_iS0uk zabs27w)6)&a#HEw#>ImMsrW^93SD}Ry^DHP9tHgmLaG^xVJYRA(|Ia0V|aDZo$@Gd z9rP8oeISdt)J=Al^y7K3I-I56l+!_|iIO$3LCQ0>kI(1BRpsXk@~HYJBZkyJsTwkR z#0wCytcISiK~&JCW5p_K@kqoU3?DMO{z)x=#-(HY8C11!h6*l^^LjblJ9Y@Zt=mP_ zstITUJwY}>J{QfU=gQ{FgV0z6-4l)Q%Z+>eb#CeFuglK9{`%`cLu6mTe$QQy{R*(G ztX9zgF`_7@hN3UBh+$ae3FR>W=I7IS^@B$!l(N_G@5oemb{Kv(OPvLb+39U(g$78b{ z0zB8rzJ*BdqH4WH8Of-_6o_Jiv|-0t_3?zDi^)n9NhvhLVim)Jla*)ApE<88fge2L za@Z({(w3;;7t?`^H<^q#8C)LM@xnGB9`-xN`;0O)C7vFOVEGq97_cf#V*OYlKy%Ux zWX55@bh`~20fnVy+&FbuT18%oHZ@Y0aU(r-NNSm^L?01akR!i-u`(e!Jj~op-gJW! za2*D?-av4DYqbih{b`C}=o$(I7T1|_N{%#C6iTDgkOlQbL#d%?548t9K|O&Y0P753 zodK*f1nX)8V}JF;onK+BqacF!zq8+8%9s5P68eVf5SEhB7ZofGtAS@u@Tff@VpKse zssKmQ6{D~X7=`^(W(Ur2KfhXy*&vL?jF7})R>)z4Dc}XQ!K5(QA2{;vhKC;7Ap4#B z_35YG#9x7wjTFs380W#8e+3!)v(@%51V*Y(qCcjhycnpUN4TO~x1`_djb#eIQ!HpA@ zi$m_)vUQJ4UQ}W&zG3+?*K3-qBNP;SJoI9OVptFwMnwFML0qUQOB&cw|2$>0ITY1K|xef1k=kS1s?~+AuQ)C~3yy){R^(J(sZK7Bh z7?ybFUFDs-5CD7=f#ohVuxzs5b^5gIqc=ddeg(YuG?^oikyH%`U=%^{gwyx|Gmpz~{hOd`=~$hBI(`T72e>yLQ2UI2mo- z)x_Tu&c7sg-V=I#<8$#fF6TGlD}*szk~_u*V~Bw>Rq%_SPENv)XWi}@$=?;9r*NE4 z224K4Rs5xhT7Buf;=lC0R64*AjoUWH;8-qLuh1dal*Xi6&Dea^n4zN+iQ$?x^8fa( zsw$tdu+Md|cYn*--#xJcI-sNK!#!S~b~|E^zPh|D>9898{#KdgaKFJdw^&NkuSY@a z$BbKdW6xCDS~{!nhDvKajQ7%|^YF|TdW2FQ4INqdZTcp%-3TzzFt!JS#xN?d=Ak|i zQhr%|nq)}&0wYeh;WKcY>6{ufG<-~+Bkq~Ly@K4+RNqA)chR#Tytcgs)9iY-T`sYs5&SkOPE zh)u{|u{0&^?s{#(;9bV#t z+;i*BuH0@)N{b27K9}61swy(7I5s3UrM4<9E>v5hrY~N%t01#qe$N8c;gXWWs*L`* z4cU2HhDRI@(OF`WvMjZlm|g|BvGIla5KQ|ST$ubA{RL#TRRiteY2sM;v?@$z=uU#s z$FJ$~jH-t?pt{JF_R!_)a82&glI`-0?cIjYEuLHuEstk0o%3=kR_%V`4Q}(eUX_O{ z6Uvc%wR8uORkv90udba+|sZ3BiO`sn%_ zADS@Z?uOFUwUf}OWy9+YaUlt%$#Lb`WzoTaW}c@p4=CkY$a>(nG2USt-eoAA1KL7i zo1X*!kG$^yjH=lBpSg8Q%BB&@LV%D&dJG_h8W9i$>0&@BLO?`C5RsyQp$I(jiAJyi zM!*ILh?KxXsUjd&Kt*H6_FnX%#zuDU{(oogy}Ngp1by%QzwckZz`5t%DQC`{nK^T& zZaU4;oBC%5-zo0fU|FctVlJ&#URR7o>9$4;RPxe!XTN_?2ZCAM3d*__6m%;qD8I;G zzeCez{(2o2mKETyte{)BP>9Vlmg)bL8~Sq6TSw#AKbz~{1}yzCVzvQ zbiogWVq@h&K0UoF8(>sg{&R#s_&eX<)F<)|_~c4^kWQE`YS~Z9=N4T(J(rDD`kMKd z<_LG97j|c>j5xKcy4jbVx{}=0+3YW&4rX}eI<*hnS6S|b#!1s1;mLiq)7_%8-lmg& zvD8gX4b$mm(}8=uvL#H1+yx!m{X9CY`2nSFHFR3@W%7<{=veM19c7EfPfmJEshd)l z^3A_ybts-Yw-ms@@1h8@NX%_n{CLGhbHmz` zM+KggM`_EA)hqZI6ahPB48;k?Q`ie~WkI7PSe;x-@+JAYdzX92+~4d=@%ng@FRw5yf8{&fr+hc8@z#tq&PSxQ;n)U-3Tf(%OWNW3(Nks8 z=VgNiZa2QYF*`fhpiDhgHD8&1F8$rnqgUo${Jb)|YCiFUo=VD4Mx%b`VZ7mePIa+8 znksRZ%gcJj`2wAhY#vfSQ%(b@0JkvXFxp^0VSO{O-^uDjv#1dJ|sguvZW6GVO0oo*AwuJGDPnXJInGUCRipt-Dx1e^KL_4io zVBQQs&Y<~)+#hWeZKI1h#%zZ=>%&rP_O%Bl+&|^miC;&*+@)gRz~!&W-v^S7um0YG z_y}!)&P-~l#|G}?3z4HoXF!|uDI+_AGp^ham4y>c(2!}bJ|LFeygc;lqhK`9{j zP;(;=Wr?21I2o}O;XkKZf7p(uER;FQZ6;l#E8;yXWV;{y zd5>Pda^Jhi(*rQbM@ru#%`+m8Q$k;;S?XDz#>3yr6g3gwUkd*f#x%*R-090^KZ!cB zDui2S(f8y0urwXI%Nl#6WTrGqY4qwnTkmONZ2f@uE0P}xDDbARp<2*$6xM|^n@q(x z2iG#UzeLS!Px`HWN+W!0yxp;T-pXv(JFkyXUY>gKjk&Gsf6M!Q+_Y$1EhV|?t?fe+ zb9+}7v`p3WK+BywelzFFdKg}F2Z zbCZX{QS)*!9Y~^W!)Klfu0B;(Qc|K~DoTveX4>gWLzK--M{EW2KNK6|d`h<|h6@za zn2LyqhI)WHuRJZ6VAPz(IuyjGrT0|*u9PJ1kMCK_S6Oj#hc78NsWeF*k)K~d>=$_T z(nObBiqngwuGSTm6=MFtSF7PAzU+n^st@N}8Zw;MYm>&~cX4gkE?m>ytlzMM#y@x( zzlWzy_)h9-oZCfSL3e%A&G^T`=jM_eFy0CCQ-@tzkW{-K_(|}((v>vs_w|gg>pLWv zO3O_vO_N8YTTI34r73a5R6=@boj5XqVi{|k1F2z{R2_3$`x#e$H=#QA>X*!Y?jTQ# z;jd9dUL>v*S0eX&dZyK~cuUMpEKL*wgV;%4@hNg?(o$DxO02wOlOZ3&7`{=vu)J(u zknAw97LnIViyLE>@sIkV-q3j|c?RaRJ*;g(yy}uXak}R6%ZlXnl^@Cr9Lg&{l$4C= zzk)bba>+8+?cHW>}kYoC>IiOG%R&$uoi<8($xI@x=qi*L^sqBIvp&l?cD^*X9Ou&AenpkJ*G}%Zl`lIA58Ov{D@j zMUn};i|U5<3i&B-$y1<@SyS%D#a?ZA8|5vd!dPRhImp+Y|nBPL7DQb!Iaj49SzIRqNOOD*s7Km)e)0cs$5!kWQ#&H>}V?jc4qn`EraXvp! z^J_gDqF~Fn7GPi&0E*)yEDc+GIrlYK?tUPNat(FYxgxg_&IGmM_#vx?Slp)Bl%z&! z3_k#a`32fktVznCD{ou4@C=XpYX1DGeFjOj1NqmkEL$~v_^Ps%a)P(v#m~O-+?X-X zz4GkE4ZXpICCc-YM|bEjnjTbF%y}l_GYvCMUC{$&_N?zu0+Z=}Z%?H$l`L3?q0k5s#=wp2TsXJcTPW6}tBMl;QyaByt$ z+dm>#5^F-TUrzJ8dh&+nvnw=l?d5WEgXP`_BvTU;uT%$O7)^3=ntCB7V_6LR=oG7D zwrAyO=$1H^UEq;4rh1k5xP*AD$oK-~$B)Bk+9AoR?k#n4I8UNVoz;9MRNp0Jr-FT-A(s2o1sN$1{{sp?{R;gppr>hiO zbej~oiRU;)P%i)T97~g9I2P?d+pc~uPF}SyNKDR1f=!g_k`nNvTs<3*hC`=~HCQq+ z;;}MRT0gONsmIX-@{XesG@?0##@P0Owd(pf`2r(@CXgFzIA*A}GA>*B+ny~i@bM$> zmF{>W&~QYD!?)ZvQu>$@J@Gnzq}A=` z3TA`?odcakaeCEKtOu1!n?0sJ!(R-_Dl1PcKkJ?o#abi8&x-%4eSQyDG8E4PN_{*=cL&cx_N10e6d`s)a!#CiIqw^?S_FeH7jBL z(8hBbYe*}Nw|I*|ZCkYH5@s(;yv6LJyDwp7a>K1C3owS3c)Hm#;4PTfa z)CfE22t#2WTtWtQNCx#Ff;6AxPf+4)7D&X9K~iFIyjWQ|#hv6=nWm)rD$BQyVthnCY^OtL1F-ltPpMvP z*(CXxY%b8f$)03YQ?a?EqT=G*Ms0EOFh|DOL^Z`cQ{N~}T3@f-RZ9-aV}p;bTHLm~ zCUO4AqkPng@^YR(w1gY~{Cn9~D+}_&?OpIvi}fgoORKHKrTSfxTjA*#Sb-ZU%uQT^ zi@D=VJ;kYZy*BwtNRCf-xm^}N=-b75R18b0(%)oP{CQlJ6pq+Z9b1+S-^7+tni7{{ zvX$y&s{%5zxw6{ApW`W6Kc68lKaU&ZC@e`+r%0cH7ZVkbu-QO4iDeI62JB4>o2<|G8?wWV^ zE6a^ZeC+(W&%E;^%2lSmPVkt5dmOV^J6K3LiqD@~*Iz%AbW*nH7CG8D5O0d4`|Ct#`^%Rl#5IFexqASFj@2=qMe8|5>cn(6Oz0c@ov8^qZRnj zLyA<6iy1SoT&lWV#ZrQ4pony+X*g3#e+ifn5udQqz4I7*nx04`zH_ z8Coq^`<8^OwQaTsL zd4wN_xWn9#$!>`~!cTCc(Zv>v&5q#Fv>7z&uaivqq$g&?CnYC)-KlYeLiOX1Wmee4)%x^ zuPAZq-zfnEw#AwnaMi4U0C>jSx4->ojL%@#|GE&8(B99ho)S z=>}n0xA`?qY?Z`9(?;^Efgh!-LAJ!^m+GsUit>=q8(y|e_oxaw!b69s&3U@>!mX01i>Ws!4@P-Ng9_}5_&BuN#;okdQaU~;j6&I<*OLR8*v?gD?knhZ|A+H8oA0n z#svPY+P!KH>|oRMw&Y}{>tVgNyD{UcQ0FKT_99^o2`g?&InR-_i;~uLT=IivqoM1J zNm^}(q{E2^Z|2_`6CCNoa5EV&WqB~1Zx52E4l|r@k$g@JUb#QBcuL}>x_8b2{*q~@+}Jrdh#CEEj=mSBTpIF zvrFfqk-?Qz4}y*5DN@nGWp_Pr?OmJ)w?i|FzBJ5dBi6Y9zpxHI>try0D)>xRd}2P! z!D&JRaGK&NO)E}{Zzx!;iB}p{UWnUZ`%@QY7Ztc#ekVEQN@A^>$oy$wH$_RZW+6zh z)ThR0fZJ40X{}<6N_;iSH3bVEQOrimwJb0L;RX>$5fm3H}VFc1EqbelORP z>S5aJIMqT^ZgF*BhNMfVg!M}6+Y>ipz>7V7v^5fK0~^W2SxwZzGy-1EbB&W?*vlsw z8*Z9iNW;B6>RP2n(yvkI#OwRasBwx3{l}|!YPV3>dr2}opH5X=R*9I@VBEFB-x4e3r|FLxUn_D zB29alEK(W8g*l9`L3Zc!=a5}B+&N_DgW^t#*X9P`Yo zj-fWy0%63MbW4=YHeu5~C7V(3>D4pPm1n5b;SyOGC_CLSPl{zxufeBnlcY7W%|xOp zD4KQI+20nt=Cn@nzm=7!9xn^JL#&?3hmGCF z?jV2bEB?rm5@lZ1oRS*)+U_s9{@q4@(7e9S?@X*#7-k(a0 zJL4PVJ$?9TECu~oA~&8qszZlS=1P#6?~|xAbf+kTKEz}CCLWACqeS88?}UYocFr-nLvJT6Y%xE%6V*UPL#NSJ zxSK;KXLfPYo|9^K;}!mf*||mfHep?R;>luH{F-fDTQi<2`n7OFcl2pQ*6e9O7mYGZ z*lg$2j|o1#*qmc_n5Jb-Cc{qZ!mLqY6iJ()O^-S)tDjfC!YII8H^zj%$her)hwgxZ zy;H*|KoS%BCu4eetACf|lX8Ev%MH8`nco`-x-=$_k0<-V>e{XT*6at}E)S+$t=W7* zJgQr_gs=HQZ6Nw^vU|EizA`v( zZE>#4+^@RH^63n#*5usFVEABG61%7UpPI=UpO^8e^z2tw<0>u%d#=9_hntwQ}<`o&Ge_o zbANKr9CJlHo{xZ8gq7wDd!;#ES)Lj1Szb52PH~gcI1K4vfZ%_wbFDVTT*(!>3MQaa zU-J0y((+1LbQTJtETSqMEk7{FsVwcqj+(0qMVE>O4CZTxp8)0QNBpMR3zI31ndfiT=d zL5}B@cg9WRe(=I~)Ohe7<+G{`91h7)K09}6y)l&R1jOy2(3uSpm&V=Zs#2zM_s-y% zik(uu@5+pi@08~UW-FrubLHYd{-cOV{lF$;s8XohiFu@kXe;f+$!yv^Z{8P;7oXG! zzf00i9B}1w>18iW+kd3&uDg~$UuGERuHQIt;Oar!w;3Nz>^^?%*{`LigV#Ut#LMfX zI|I#)&y8>EH+Zo{vzHXQkPmffwy{jP9H*mjhKkprGgPTKbRyi>Sne(PbHz{-D{&a?`5PRgs9(Q~1 zyKrwQw>5fK=Z@{|7g74>`-`(%t#~D!-J(77Ha(nKLi)TCrd(d+#KVq7%|mC!QlB^dWnG*lYbmd?{q^?3e^Yh-CVlLqH|gVylsY4% z3A%BbN4OJRxXTS~cfGxECwgIbla4rZW%Ait`CRZBMNjyfe72VUV){Gj3V)N&7=NPg z{!IE3d~Orzwo+%<`P1@*`*zbErDVwk2k0sM4yU_GN2FuYX|2ow2jZNpmM7dzI&_xI za@Q5%Zql*b@f$u&->9X6{vJWUwY)$cWYSSj3U_P^g?w5|ciHaRI^q7Z)16Jjxsk0Z zorODOy7LzH**5t%tpM(?*zWe(Ir*#zf7o`X@W|I1I>(E1DVGTMmhw%O98QX}k=b-M zlIjhA);!B+oD^pyv+0av#NDJrXWs-Js&7A|_932=9@>XV5B~f)WlkhM>LcjF#iUFA ze?#pq@0!41Q%Ac&BV?G~)K%ZS3eKPJ9*Zmvyb{*wOs);5Y3vJ(=z5t;qhZ=g1_rEH5{IUiKL7rDN4|x;I9@L3X3I z0K7tbV|a!wIoSG94YTyaJ(;PhtE5$m96wgDm9!>?t*)a!DVxy3KPeFmxYVv=u8(41 z%DBg^^mGmBHMP^j0zx(EMG1!zUYzfnTz#@a8pAtyM?X_Gp?>YTNtC+MEhhDzqkL*v z>x-@t{RPTLKP}oH<|I)5UDaOl0;|5fDcnz)?$FAXyE0d}zw2~o%R)!BcZB;%vmQy` ztG&W>ZbN%D>9k}=)N-rd&DF@=q+_|WWx;2o=;YdZ3+=$kPcAzWZU;_&EO)AR`f0s6 z=)-P}&6jX*>EvIG8RiQ2R!(EyCva%UxhSGDHS9=Mal=cK(74%N>MbMb=VV|NWH_wQ} zpM7fUZHdl?NBrBF`8EAPe^a&eyGq%CO@h98_B;~5t%x6c_B`Tm(zpCs3GNJNF6`Yx*l%jQ z?yLMhIX@q#Xpjvb*iDYGnDwLx;9*S0}CWB|O1EeKhlZB3Vx^gsPqfqg(V3Qn6Hs88h4n zl8Lb|x(M9BgxR~S-m8p_y#Cl07ncnkTGry?iKkEh4N)NTkAf%lt*%VE!6gMVX7ukP z?UeC+)mz3USLV+zxLf{mA~u{EEa@XYNXg}cgC{-xe>vf9`SS}DE)(>|gPzx<=cb*S z?cBVd_S3m-@{T}rb=hw(DN>-RE7SOAz*BX^ID105MM)F9JQaLYEpRQPQ<{(zHh+mL zB=HMG*nXbVo=&4n8{*Eq`=bAy+5?K>uJe91@h0nC`=(;gmRHlte*0~<`SL4`H9*hR z+qKM>UAr&U;o5z1r-$Y49SHZF+I_Vo_yCiSPor?C*)|_7@vE%ezzdcG#2qRoJNA7b zbZS)G2O~I3u`l7E-!sm^LVFHRA?pEl1DKvRdBQD4{pt(8Qe#MqfAfD$1I1ZR3fX-~4Fh48M_YJO0YIG`?Zg zhaat(RWGnAz5DnFR}{6W6TCxur1qsZKKS7HHdMxnEeCcL8;JAO81u&Ds1flTokJZq zgdUSaaZa|n9OL9|II~=~@*kUP$)5%>+z2uF->yBdC@yvB$d&x@S!-%}ovtatGt~;_ zfaGr+JY)Q1iA5RM(3F%@J~#6+TP`&SYn4%8hxnCm%A8;}Bf5w#t+AM( zcc{&Ni*iDh=XAA^jAmM&w@=6E!uIJTtsJKwp{9x79D$LaA}$$AS0>dSJjRuS<5pWS?u z#lNLtEGK?K)b|HU#<$@;-yeb+|JQo^$H|4HW-@MBwRzmQ??x5Qg{HA`6VWq#?>GC9 z8(UAF6!}S}ePNOO;CJLGLwb&xp$0sI>p7|HPi9BC_^PfMLzjE*7#|QBTKYyHIw}fBqIc=^lW-OQZW?8sR@zFI5ta z&jM2eQ;pA*IME+w{qms)mk%Jj)r42{svof7^*;GNo>5gPdd6~h6Y3ceo*fRJbj<^O zA3hqgIy@7o>b%n#A~nl*S2c9YuhFOD898auY==uxA7@O8s)e<}W8l{#$5 zpeNTFv%db!n6>7~!Gi}6TCha^R9{u4b zUW@yG`041Y&OFqC20)+v@9>XpkJ#FgDV7bjzx?aD*e)-Y9}kTCJ}^#xT%Y`JW@h`u zz_=fX!Vfa++eq2`2l+~I(=V{&=JgymZfW~B@-<(=8HV-2snUJV2B!w6u9xl$PW|^Y zXp|8pkPA+ImMDRobf0aPB@7GOYYywoT_<9{S-PI=#0;!TkX49Xca1DPZklK)M@w_^ z>7+th8ngNRnU*q4A2P<8+@t!jEPm8z(ZZqpvy6QHj^#i-SJ2J*=f>}W>n!yy{bm?nV8g*W(Nod5 z%FcHSaR;8&FX`w-FToFoT~LvSy8*LLO?`htY5tUqmJJH~UhkAdbbl0VX?13zU%j^D zzU#)f3f}0dy}C_&jY|96?r64dxh22#dW9?DKRX zJ7AMx>gX4Y=W%^;_IKXO8R+o8m}qvQqvO+y3!hy!rFXZkvmZP;skcu}|90=#E75UAWGmy`x6rmS4ZXlcc*uiK6tFew zk;e08#=GgYr4%F0@BQ-Ktu&4p8=uPmW+Wzi|M|ui`W`3mH69JdNpW&*X>`S&7Y@^i zVNul>t!yG3P{D9tMarRj75+>QPk-?F+Yzm@F*q*doJDpA2|w9D`w)#ZpFo z-&K3xCQc-5&zI&@?Ad(89=RA-uM#5{!P>9V&FOFPycen5ggms%QEoDRi&*f*MXLro zBJs?I#3daDGF$g0xP4q)-(fK-ujb8-Q^C{J(z6TtufA{T$%CcFi8~s*z?p6R3?BVY z-o!Za;hi^FAF7&hcYk62H-{Kp2`~xeD7&e%*NV z`Qu>k^^ymV2X8JjM|`{tBRnqp*7UbX;lD zN7^3j1N&%g@KLQh%C$A*B{WE*yjr-as~!Cmu&toY!808KXosKIHPv{yDK_DA?tA66 zMT@=O#l}oaT|-8r<69bIxqGp38GmW9@hg8in^c|E9D}LHjBgI_)9%056=-U;LV26< z`(nOlJm0g}_+3WZq=smY8K@u0viWOI{bWv5{nycHwl_6|tswj#KWD7uMbA~)Jqi_M zqtBk_HyA6|2Q0lVwCr;qZ!jK*-H3VKmwtnw?w0buF*l@ZpA3hq_OhTR3aO<^t0N{hQh}z1NhFjLZ4kJd1m3^Jna? z=C;OTmKbjvt@$oqc=@?~qVh!e!MwkvM^k8VSd9`4s(T%*Jo}<$)AZzNfA;qNvYvxK+i?-;#|r+X^rqWYnJ?sCit)de>W z73t9A9GiEf4Uu|_nW38|3YAn8Cxxn>quC*$5NURZUVin=`22HI_dFcvYt1=bfIfDS z)I{IttqeV%e&CxgzA*bF=(mw&e}C+kdZQ9M|I9Kl&4j-b2bu?L{Oh}cz6rySL;BSq zWRcwIso!GMXDq?^_1$v=6NXvt!jm4gM=Gzg@$@FG2iyx&5f>lGo7gMbHnv(#XY2LH z*c7b`E0)@@S8$8@7Qi|Mm*&C$G{UczzF2)_vM8DIrqC7=jEbnXW{3%D0h zjId_`tBHmUN;g&l#uuqGnGZLS9#vPcsajLu1a=g^4{%g#gKvc$m98TQtx&tceHz@q zMwkF_Up7@aVPf@uxTUg1$`_$?xHo$%(bT_XJ=8l{7q|t0ucN#W&%mz|4+;xaAYbd1 zui2f5yIUF0rT|Y-D%ce1ZPuFJ)nv9?%4hqf{LqKW8uML$jqN6P5QPC>*8$#hFQ+~9W#aFFQWcjra|#wd(WMf^>G)l@$C&V>6C#2=0L`}O^7i3MXI`}>95 zN%k>-{JE$ss2rSta_9tDgR(GyHI$FXE(a=$DA>m9;G6C+-|7S^3*|k~^*~;bdv&0H z2zQhPm4g#d2Ct$lmH~eZs1)+I%7NaUAjWa@-FH^4OrAs zl)1=1)e)+%;PsC%)n}?JHDc6tH97R9J_PuFHUn}#gM6NVtYzRI@%=OK8GLVn9994y zrMjtN6|*$|Dk9nsH;?8slA9WCSFY6GD95t-UB=rbbB$0%8}}HEY{|+*KIe^ z-XP~#?7E*#b+tzR(FUm>oj-j0ALk827ze6uR2ARJd znk&k54s+?BKu4S;eTBC1E6QaJ+XeZ1h#$18t!e;z400wJ>iyUasIzzI52Nj*Kz{9! zPAc0)as$2%GAp7wC2cW&H08r;+j7!AV$Xxl7Og((ryfH1pP=L3N1e9wIfRv}Ly+eu z*aYNz3UtB>e2);{Y0yQ;FV!FUEMOx`l}50m-qGx+r-&VOcSad+hFY`3whOZ!hrYGburcycihLAOU610~YF{qu zA5g#TdTa9>P6v7ka_?=shjC0hMjoF*8V4aK75ED~eH){kUPXP6)brSSYSnkUUaJaQ zskC61Q=f)*eirv(9fh8ng1R$KX!UGVXEg;zvScWFWSp&xBND1PqNVi zYzl!oGZaJ`anNaQw0jT2Z>9D~?E(Gg0QnRffV%HN8OD(;Y&~Y{6Rp71Tga~m`AtCl zUyz;`cEl#sBkD7$58SPcV{`33RDCd_yS2ZvyOhCfp>io(sI_Ga)dA>VNl!f>;!aWB z2zv;96hO3d(nA2Wn*?b)ds_OP-7Ys{edMp$RZ>T`Uws;N5`C}@YID%a6medpJ^*cF zih6=Qq+ZS@%LCBg6JX+%bd!7q`0UCS0B%=K;++7pz&)&|3FH?bJFC4}eK}Kp0DniJ zD1VJ@2-GW~qbNU}*);iLHjR%DeJdriX=uZ}rID;R zRwNrzIH`(dLtdq@?Mh(}YyxZr9FnFXEWytvz)rxmfCNax0kHqx#W(B>J{5R<_a;_H->1-nYn!taQ)z_W}6rvUEYEX#wO%@3R2Y0hHb;;EMng z0hxfl06V?0@JIS~ngTm`CcdkdBK!=-&V7gX3Bbwt-iSCq0Asc%02;yO;00?8BA<5t zL5KJcd<}dUnE0SPVP%|ffMjI@(GW|!z(|YuAe!_Z!fu=Z(U}|#!DiO?bkxD4%DrsA z_9gUJEA&gKTLePSwn3d{e21+A2%~PSCLIEIsz2|ke_=kdOV!^(Uu(aGev*5HzSHgw z{ir<}`b>K~^dZ{1D)bM=0%V`qx`^~5=}c-*)Mrt@Kz{Z(fbk_{g7hM} zBYqd*kG>M|HF8&bq3uB@2|WWHqddu$lbc$P?L#|nC~Qg6S;N^)6o$S@7b!d0UeW_( zXDc(-cCUgoXnZ7Z2o)gAi-5ga1-ngE*>rsdVaz?8{)#ZhA`?Tgb9GRPg$nu52@dw zF(8#a;%fzL9MN;FW*4cAAR~kofOrT$g!r)QM7Z3^W}|W^nqRP)qTDY*yt~+D*Iz+v zAL2a6I_rsMI^gkE*IIT0{n}zN4*4g_YA4FNE%Le;<%<04?N~4Bw^Y<`0_7BNebyJh zNwu5wxhNB0TXqWQi+$E@ z%BD4?Pky3bGRK~kqCfAz`s%r$(H>!l58{XFBGM0+do$KR9nBi(e?h(s^pkG1lOC*% z`*P$TcPm--=q6Wt$l?+7E9sEgV0Nwc0mjQR%d_hl6;@`-w<*HfsbKWQ+ES zxLBH)1$fq z{50@j$U#GWycg}q=9B6Pm6c^X*_i4Km76{@bWXb+`UrK0%Fl^OALxr%s(OMY+L-Ej zm{)4gr_d(bp`DFjt!$n&s*4z>*|hY!D1$tjTY#Hw^I6!oRfR4?-9WiaqdH>aa9Xxp zESuQY#WoI?r=>saZ>syEj-jnk9ilel#5Rw1xlo&-daGLHW6LRwXR`-To>N?L>;b(c z>dA40uVfq4Nsz^Mw!u}xF43Q5t;jalUqU@3Tm5q6r!{oJ7U)D8OF`d4Rzs+LxrehM z`cJ6uIiT}*)PqaeV*LwtJMoXUHpL$6kshJ?;Cc$-yRyEby!&E&RR~$%0o=%(f6#O` z0x+D$DOyXoWw1Ts{Zo9yPhG_JC_UI7eKdY3bFL zJ%~MnOiM(+NA~_UHbc9C4cD>|j{J!}!mI00XYRxIPZ+CQ1)6suT!PuYv_Wi=u)koB z@lP=acZR9_>rhDUiYwq58;-K4Ig8!uWvH+HfNAWDJSz>v`)>6c%MW3s7O0Ewq5V`L ztU1@QTlt7hR8tVf#g>wr)FN~e7z+%r|M2URxG$gLm~r`7w3oNR z4OVB_EA%cera4JIRvm`Cl(Is(FKp?N7^ikb9$NukG2g-OE`AL9n*2KfmcqS1@^CB0 zg9s^%P@Az;Hpt7^ zYypTv@!W^lJOLYl0s45hj6gYzb`FpVLlv$*Y#wyTP0-WJ)VJVQiSqoEl~5W)gVH4c ze>4YN9UOr>Y)keCWQ!340e&Nav7eN_ybMqaavTKc3)l=;1h^W|*@7bAh0>!aIn-6< zI(7}^i+BR9qtZ||%g$>}*v@}-I4l!eHq~KEUUDF`LFoec+{BcB8^W?AnL6Z2awHj6 z2STpMhu#$alOTU!lCur;T^&$4L;=c!$|MR_APmah=Fi4LNASOnzx>xN8A$D*^MgD}k+e+DOw4 z3V-^ZuQWB|$ZJ@tMQ4VSb8_`Awr zXZHk@BAtJ-=}un7`zg3p1H>o!*T8R>H=9Qr6D=i)E#XVp5@D~9&0@oQNGlp<;(Mk( z5_$Hst>@vJ;ocTwOdCHWzGvzYd{&-`H6|5t0or#6n}Knd4*OjP{}G^X8-%+?W4>mv zF%|+Z0@&Dgx3TRW!Dr=5A@fUF2aO|s8^pEne|fsIkS+i=@^l6B6=9%n8!UH>#g1y( zkoRxEe#pYcw!4jO_Xs{K*FqX7L&DlysJm|=pC)*%Z*mK}i?QzIY`SYE(n2^J(>vyD zm&i|m9vF&bS+dYEKJdp1A6m4fP+SFjQ8x!bVfJ1&HGh2SW zY}twYlirw#zQqY|va3`RrUOsci;&+0QzwM=fUOT~{2m(&JwRaU1FL;g9n_veIX#7Z znn3U2Z)G0*AtTC%Yjn+ke!xjb#P_7IB{pCXw1mL^Rv*Rpa#3IHdLLCsMP186-m_RE z3+O%k4cT}|_t3g6#&J{Sy^u8l=A4O7587~iq6uEcmJrzA^4p;b`3Kl4Tk(s|Q$rQ* zwxJ5o&oORoEVlr{?M1kG2-h=I;dME}O@>=uB%G#(DzyG^TMIyZbvpJUB*LCb0H!&e zmzYlQ@!n%i~gg=-fzL$0N&-HCP<&NA=0O85V`S5u*tB709$67+!N5B zT`v8CaKHi(7h}szxeJ>lAApTS;fAtoxjmbSwUJag4dDhO&bKU!Xwe#q22UZ7H2y!9$>5i(Mh!KMf6~Z~hs( zR+dpF;7jm!1>(Fb@-oHF1L*aV`e1zr`GCDM5_S1<=?uaYAn)K)`ku9y=U~l>FxJp1 z9&Aw9-BAGBJkTBW!QAga^Q@_?Ow6|;Pho7$jfM9&&|VzOJJj^8!cKxM`G@-+6wFmi z*iRE)-z7bo%*set(0qi6u@<1!o-3;flTM~OVPh5R_HtzZgS`*J#zPRc+Wil) z?lgyOuk%-jh2EyQ9@5tr!c^X*ODt^9nf(zC&*@onp^mxHs67-2XMWS1QYKTvYoplL zL*)&SM7d{U?ts=V?E3d#h5uxItf}6jz70p4?g31DX7<)p?tkR2+xt5H0LPXe)~(Gw z9e;rB@~8>Zm_>e@HB@Om>q3BXpfZ82t^zc~)3qC!g0*U0{edYoA6p%$Ti_4ScO%|$ zreRG&*KbBV)PoCwwiWd8OP4Op_quj44Qmj(YXW|8a|Ys@c&^ImL;s52$D6+MZocXuXKml@9{2ZVer%BOdlcu=fa8CnJ3k!eX6U zzaALt+#1%Q%{6h#r@498#?o5!d2r)HG3S*o=3N=b{3|;SsOFF6(+Lo^2sE%aQ~(2g zh4!~%-qzmtA+Xp(LHj6dU@RZ;@|80DkS0KSo>i#VunN$jxHP9+;~(+m*~6y9WA74w zn_VOY*+rPYxybo0;XVer4OM^`1ptqHI~Tc zp5rb0XsoG!gt=9Kd`{$%_UpcHK^N?ktxxgs-9>(sJwSOEG`H~%$`;(|)sJ1IZR6t! zX#O7i*NVWyWO^sySgrV1{^WM-8ewy`W8LsR9&jDtX23AOU?m@Q&jS7{yA^PQoC{kG z@c>HzVxKPXpN~)WFxkFj(_*|L-a7+ZduN2*NNJF*3p*0^IujLgDCz_41s)Ap2q-4| z2e`4P6~=!L!yfvUb<%oaJ~bVE1nL>!B0wtq-vVw0=m8i3@M+j%1pE{r6)+Lt0bB-~ zG6m4Z0r;*OK;N!xb{T;Ro6;3v2i45byZG+xT7oeL*81RntG10T^DiL^++gyusNhp)StyZ$U&M-R?KhY%UdzW{0qB5Eeci1yIDP~6AeXQ)JvbhhP&@$538$L6RZm|=<*}lF^nwCO~$&KD`#(tb7WUt8B$EhwrxsCy*KK**;bN$SGo+Ws{25zwB^y#dNJ09%} zb}^NU7!SiXrga1PIn4FfK^vWed3_oK&PMp}5Pk&S?-OpFw1-$HjLSPwcx6254XwkX zUb%n4yfVfLgdxYN3ikdGeeib;+8f#&Ukg0V!qzu$h;IjowuvyT1)!D{?R{n8~!H$>5*`l zmp9WUY{d=ZYWO8Tz7BxAV_jH)c&E5lcw1H?eG2cC-%VDy_HZAG_ueL^cL4>_--6~W zgae51lm_wdNGq0K>^n65+l%&t^$aBwx&rn!f%Y4FTpf;edjQ@y6R0oaT!-TwXB=8! z4Rs}fT7>rCIQM}yWTw{Ue*r%#E}icIC`8@s5{8|uTw4#=#_OR?Zze!|^wWF`bp3c> zA7S-FTBnZy_RCDu24GGH`w?T`>6-w0YbJq|#-1eX;Ol+I`)Rzdp>M4HycOe4et!h9 zv`%X~5C?!h>P^7we}cvzrSSo1gaPwd4-v>auqS*QJ3oDsX9{oArF=QTcCQH^M|dZ^ zY|(P!m!0L|NXw+z6=@S}V}JXX!Kzz!9aEZNy&7PD*N7wejmj6wU?u_h7z3CITHj+l zcEkp%djgiBjtgK*wPjd;moWDR+X47@#G~>&56E3D$C@7i@3ws2!8?H8MgBH;uV)Vt z*x&yq{wyZmDHoGhfHz^sS-6m76x&X1xgkuHzZLE@_Ay=wxDC4gLGTVe6ao1g-pR7n zUbV3{L#otfK%dwcc~$CR48_>5#G||q=gIc5du*&U=bO5-;Gfy?#H^ zLb@0aYtRwH)YQ;?%0KkbBYy@I%o&>?)yB;hMCe-vz?zSI=x8vLRU-c&%PO$0x z--=`9LwxcXmP~-@-O0O<)xY5Gj9b$?(X{=<`!o2q0sN4gG{M9aj^s<d) ze*|(0>K=;#(Nm|GG|8_z`Vn`kzkdeQn=vK;*kP3$Fqa8Hx-=IQ1`H%M0^2S2yC(OV z!in%z*jG{sn!teDEKKh;g3=yq1nt4!5b%630&*RuIU!8Lkq1hX#lkPhB1EA{#5Dn;v4bXQJfQta@0rLS136$mdZZ6!`>-E@d0(BIdjdLHf z-M_M>9*+H!Em#>~CxOzPJqAp4NcU{C;0fRjj_<9?C#(eDFIafC#>iE+zGZ4RyT`xqG!AR5nklFhOM{3j3<3WSos!xOt}g8UZc0*g8}>S{u{6Y zsKEOO0OgO|zlK`_;9CI1Lr=hS0P@=cze|CS0zSw4)qovj<-20mD{4)6M!h0d` z8Q|`~6wmr*@5_gA=C&Tmlh1(P9(-??%GpNw6U@N|*hZ}(TL5?Xvz31U6viPhelhmF zuBZ;8!#U%`z7wB<*NKR881M1Q5Y&|x*qhOUFmDQc02pr#sg->z08cXkd+4?i@Xo+g2G;yQxa^1ymE%;v3IL7m2&TbqodoDl zeUbbNY|~%ZW_<4itW%~!|4wD=5bj#QFhG9-cXu|;3mX?;?Julrvp@Bb70{$DS@|CIm#31wV!9-%Xe0MJ2n?+0hHmVXwAG@QAo{VFv7O7H5Yn2Xm~5B&vkr_2PbHHfpg zs&&Sl){e0^Yz&ES)(+}ojR2rd!`}TF zSSw0n1rj1$4MBG&h3Cd--4phgW32E$@16FV(S9jFFIqFg+8CYF zJ}35up$(|rLZ_74u$LfbT1ygj80&Df7AM*Rl_9NPQT;|c0(25<6x2>o9%9|Cwpibc z-2H^JiFO!k4P(A2dpb*K?Rf=F7wrv|FkS*YsJw#tWdOaCn}Tr(0n!!svPg8t3+5&5 zbFi*XIGa(xy5c>l_i*Y`&9nLl0f;B}SbOzFY`ZOA&Ks^SbP=?FVa?EMJ;%C}U>#tTr{LCFH_gIpq= z#`^SO?2QH3I!V{S$Md+0!aAEN_MYOd40i}?il4DN-KD_)0LHz<`e}Bj{tC+AUI*T2 zeuvityFmx8X9?;QtXF1Y4e=T57oCW)&h5}wYnaE*pU{!1p(^zgoR`cq&rCw@Q?UOP zdM#8H#?zRW)*{uj<&<%Tk)&K_p z$$Ky*}_JoY}#*nvZY9Ri>E! z?qR??0A&~tRhn+l%aio}rki>ics*o$4w&4V0#kT`U2ML3G3MIneJ9T390LX&r4jm( zwRqo%ajMP7Q%JiW;^P~A#Vl<)bUmHR`7;2Ws#t)#hKgcA8HF}b9iZK)^KmbV#{_*3 zYfS*%(pvih^>Y>50NCVy05F?vitEfa(V0=&RU>5Qk+#ytC4Eyg)cWukeOlQ8PxJOcSm@N*OL`!9aO zPAbvT&GVRqZMms*CNlwNXggp}{ua!i(^=1#*)lsFI-{vHw)m(9i?f>>BIRJyjdf@L z@gLp4LVtAc3if{$G8df%7PvOM(foF}*?t0tDy^`!3Cs3b<;muGWofc`W|{H{y?HZz z$MR456z<|Iv$Nib^Qu>3Zu$@E-+x>`V(J5(?e(;wIUUr6Oq`X7{igH2ufzW}l<8Uc z$9#)()l}9a;lB(t=$y6VTb#GXe2#J$@+wE2qx0AF-P-()Z66w)$KJ?pvfE2+J)*P0 zSf5-b;k<|RH0aTp?ZIrRas}Q?(SEG@Y1bcHu6Dh&>!DpPHV%hh7HS1vTPe#zUjl!L zJ+SHEIURTi@DQpqv45_%tO5366e;C6e|tCZ8^A>-$O~X6Gwhw}h;`N;SpTjA_fhPK zcOu;40SHHa(5*+bAK|x!tx!H-M{r($1@8DNQfnj5Y`o8ajXn#sVSnRJoFfFFT}0^; zUBVO&_5rPB{ExPQ|10yaeuw>Tr(p-*$G%bDg*2G*hjcS@ium|`YiU=_QAcV>)AtMuNUD8+zYV_{Vd&O_9g7C zH`!O%$M&Au&U|;%d2G0oT`R&M9M)6Jdr^1cj-kI}eX1P$%6eme;so3|5W-%xGW3hI zM~}{=&^QZY0x@nQdmnd%kS!`NWe?N+LE_v8Y=3(kAZ%!iB?ybL0G+RZ%`9ws+5;DJ z|0~Aa|4sM2LZ<1sH!2-xs?9rbhXX$ZpuLut0`7&}X&$UMt`V%&UPI{9t+{llB;*&e+RJ8zk8H0U>&zjivI_!72;q2so zZI}7(ZUVnc@O>HE@9tz_xKTJeKDkp|3Mb+ttmj*HKxPj(Yrhfb5sG`Ym(0r@q5NUsx$b54k*Dpf$eq& z-tPs(V%v?}=R4qD&!l!t9H7r@LshCt;HV%sXiai}x-jMjX1MW(HvZ?y6C~Bn-P6ch*R+ z6Gp#+yKD4~gb^3_)W}B&gD&o}(RvV$P8YT=?y`X!0q(d_(T5Qz;$9oV_{N<#D#jB8 zPMXfR&U`uJM!nM=IjumGz##|cJIcA@kL*G1MgFju!=iihaNd)~(dPl`0M!F0ILA#j zy+@6m9OI~{F_L3^W#5(zcKE4CYLd9;@_E%H3^--Gb^+cPuziwXG?Y)o_Q=E z&$hy)eW(ZC^FxdA>=?qWHmpjOu37Yf8bAfm+6wiCa z^C3Zbxp=M+&xghH5%GLN(A*|yZWqrT;)%N)LAgvk_li{Zi{}AB^N@HR7SB(KXP!+w z^Nyg_g!$m!ge4(-6INe5n;@+wEL*%c6VE*HY%8AaL)XJMKQtT9j-h3Eb`~zZgzx3z z*;_oXfZUp}LE?RgcwQr(!yv0BY&c}q1TvY1=Pe@SJdy5v@mwID3&ryu@mwO}J|v#Y z#dC#tJ}jP(i02c6ljp?ydhy&Ko*TvUdGXvPp4-K9hj{K3&oc4cEBM(jo(Dv_hs5)+ zcped9j*90o@qA4@kBjH);`xSnzA2t>i{}aPJSCp*iRWqY{7^hU63>st^HcHsoSr;E zJQKy!C!V5=c(Qm;5zi)+U!Eo2v&DOkc)x_og|{aO^A5s2pK^o}r|^qK7$FlbWWqNI zm(Ai?F2V?|_*vodlXzAN_n(Q5dk)ce&n5cqyTyC4c%LWU=Zp8V6yE&?C5YL9<*;qU;A>JTW@CJF(lO&lXWSGT25bq?nEJz63q6VJ00-V>rHq(M&( zS>a?J!e=9=^rW0-BNuog6?%3Q&q{i_iE_4=Aw&+eQ#qdb;z_c~5fpL+g&gP^@|`c9 z3&eAwc-|wPB&8g7K)fFk&%^W-`N|Rb%Hfq11}WkRo=>nG@O%_c4(-vDB^f*MtdCM| z3Qov{O0X$wFP{I4wD*szv#jI)-}il=vv9J)HWL9Ajf^SVaKprjNkxT*3ymo%7S>8i zR%BFUERpj=EL56oBSnV}6_pt&+b~ht@kA`iXi`#Pp^{QjVNp`h_&l%I_xs29zwhJm z{p#cSI`_Hm_w~N6`#P_4-}kxBbw-IwN~ycn%9d|HFJ}bS+E#5>s=P`u&C-Z8Dve3w z(g&pbq}|d7r4LCTmOiR%k4bx_`=$MA@qqN8^pJEw`m|yOrO!y8l|HAOhouSW^U|dB zC(>c*Po=+7t(T?0k^WBliu5fV@4uXS?{e*WCrDRIPg42G(oCsy-CEDNZms8Bx7Ks6 zTkE;kdM)Q|r@7LzU4Cz!bc6I9=_cv9((_#pd5^nxe<*eSTd zec;kqjt*gn|lpnIQDL;&Q zY_H>5!Ej!O59m@J)?ct)@!skFwT>lt#e=l zBYX%w@7Nn?A@LhH^PGlm#yK0Th<~MjgOw`YUGXW4d$-$8_Zqh zH<-K1Z?LDOgVJZD&r02Ue}lRA{swbbzzyD+?syp`j+aq#C7WHlT+yzx4aeL6-r!!4 zyFc^<&g2_)2enbovgD|P3bP4=YPa_8YqycSN~d3Y1A zg;RGP-lW%JleyPoleyPoleyPo6XVZbZ>8JzPLgIyPnW(|>Rypej1|Yba+z!5CS4gf zaRxiY-MM>R3N(fV8p8s{aEG1C816^idH7sjufu4LO3vg}K9}>}4e0s2UgzR%7g?lQ zCA4xbBixN%F0GVSDRR5iUAxa^R9!817j!PKuhW<`F1<~y>{ZSOlyjf7Tl%2%A?d@? zN7c$>(q8F)X}|P<^q};RbU^yFbWr+?^jYb1>gQqUhW?7utTssdth)UN2L6y6dO+Ua7m{pXZ(9GV>}BgSQu~ z&@QCB(7OoblPKBF=bW&Bp0a!u>dp+DleD*laX25}I(27;^Kq}+uH?@Ae9q5D(Cy1x zP&c0E>zr^ty8SPueYg zQ2LPcVd~ z(gj>|osOvPi&A$!xj^@;7tn|6>;l}oAN3gRB6_k4J%MXzk)6Wzv`FWNBHhas>0Yjg z^TQjImuhbr$5O(t%36>&sP-JM$z zzIE#E+=}?j==1@#wNKhDeNg(4^kM0vYW*>3uXMk(UwS}#Phu*CiElYB#)=YMt%~%jUC3DRt*u_i3sruh&cmg44doZJw+%gkakvDe?qIi=yMx_g?hbZ~xjTU^c*Ci?6WD?`oVt5}ExHHTqI-ZXx(C>z zdw?z61GrWOrO!y8l|H9hho$a*U<;#k*e+uL!> zBd8nOt@eoaJ}P}o+AG~Jb$3Bq83C7pe^CuRC_N+{kRFl#Li$T9CFT`vzv}AhE@&(7 zZny33g0^y>a|P;gUAT(la(V)8zl!5(L*3`KtMrOrCC^+%Ce(3?}LFpmsfYi;BU1M$@>>7^cuvN*) zRlMsb**=9=uu6TXQXi`5;Y+rixmNxidd>q*ojzw)6aUowv8M-f$1wZoX(cchoM%Ie)vHznwFc+jcWT+igthW`wqLz9K(&J~tS% zo4m3+H21qh{n?@Z>`+g*qbGg`=g$)Ke6F5cPpHc|Ra(T^X@`2XL%rIeUhUva<#I-( zQE5yXm%85W&=q!vuCT1wQHi_4?$GSk4vxiTcE_?q$HK}WZM$RHp<~&>QP3-n;xz8k z+~&(;4QM0;1v%Ae4_1D>U?~!KyckQ~_-DW-mI(4(V&E{rz`L~OepNkl%0$R`o` zBqE2@<~KKiO44r`6QzENJQ_EhVy5|K|LoV#3! zn;(eCClUE1BA-O$lZbp0kxwG>Nkl%0$R`o`BqEr1xg{dEMC6u;+!B#nB63SaZi&b( zQMn~5w?yTZsNBNmKr%<=mZ;nkm0O~63-5W{5|vw`a!XWh;oqWGEq7Ll$}Lg3B`UW> z<(8=25|vw`a!XWhiOMZeMwHJg{O{DARibiBRBnmNEm66J^_FVIxh1NzN>px%$}Lg3 zB`UW><(8=25|vw`a!XWhiOMZexg{#MMCF#K+!B>rqH;@AZi&h*QMn~5w?yTZsN52j zTcUDHRBnmNEm658Dz`-CmZ;nkm0O~6OH^)&$}Lg3B`UW><(8=25|vw`a!XWhiOMZe zxg{#MMCF#K+!B>rqH;@AZi&h*QMn~5w?yTZsN52jTcUDHRBnmNEm658Cbz`omYCcU zlUtZC;8lppEit(zCbz`o7G6c%5|dkEa!X8ZiODT7om*mZOH6Kw$t^LtB__AT(Og@RpCo%aXCZELRlel~mmrvsI2{THJMqECL%O`R9 zBrczD_kd60@=07iiOVN(`6Mo%#O0H?d=i&W;_^vcK8edGarq=JpTy;pxO@_qPvY`P zTt11*Cvo{CE}z8Zlel~mmrvsINnAdO%O`R9Brc!C<&(I45|>Zn@=07iiOVN(`6Mo% z#O0H?d=i&W;_^vcXO*~o5|>Zn@=07iiOVN(`6Mo%#O0H?d=i&W;_^vcK8edGarq=J zpTy;pxO@_qPvY`PTt11*Cvo{CE}z8Zlel~mmrvsINnAdO%O`R9B(AecTyBZWEpfRe zF1N(xmbly!ms{d;OI&V=%PlQ@zT0mtlysx+TOs~!Q+#q8ub@-+Y41(bGC%?$74 zu71*X;u)uIPJXB2cPf4-_vfzMeP_B;GxIw&GrvQB4+)2{xs zt3U1PPrKS}S6l70MNYM~OKt5^Tf5ZOF15wV5@L4gTZvt2YnR&ErM7mdtzBwsm)hE; zwsxtlU21EW+S;YIcB!r1YHPRJ+O4*Bt1Z^A(AI9XwOeiNR$IH()^4@6TW#%DTf5cP zZnd>rZS7WDyVcfiwY5iW?NM8M)Ycxg#VQ%v+M~Ais4YJKsl>ged(_q*wY5iW?NM8M z)YcxgwMT93QCoY|R)^Z^P+J{pt3z$E7KfZ2YO6zSb*QZl9ao3i>QGx9YO6zSb*QZl zwbh}vI@DH&+PX(Gy7y>Cw^Q4l+P+uY_iFn-ZQrNuy__vq*0s=h zw%2oWXnPr3N;I~6HHX&4D|mx-@d_TYF7{4Zm&W{lUf(nAe#PHUe4E`*`F?v;b8C-D zd!_rOPY~H{Pbl&UA`jVZh z0mTd`<~h7HY0qh9<~c^`2DaUM=Q*7fpVQ3DVZ|I)%wfeGR?K0=99B$1SMY?|O6dKP z&=owPD|kXzaQ-y}GADEePv{EH=Wxa(p(}WTchhxje@rp%b3sB^@Pw}530=Vxx`HQk z1yAS-p3oIMp(}WTULB&A`xKeb6+EFUctTh3gs$KTUAq&yb|-Z0PUza5(6u|k`_%Qp z%`+r)?M~?0ozS&Ap=)!iGqlsA&{MpE8L${R^}BPnkr<&C7g!KmSlq`bl3 zMx;MUc_XR2yQI94lsA&{MpE8L${R^}BPnkr<&C7g!EbPQEt2v^Qr<|)8%cR1DQ_g@ zjikJhlsA&{MpE8L${R^}BPnkr<&C7gk(4)*@Yc_S%rB;}2yypfbQlJZ7U z-bl(DNqHkFZzSc7q`Z-oHU%-WZlQhUE=@ zpUJyvSl$?xH-_bnVYy;ht{9dphUJQ3xnfwZ7?vxB<%(gsVpy&imMezkieb58SgshB zEBIFq)#83>zx06gp!AT`twhSWXz06Ncr4VL4$~P8gOGhUI}_d0JRJH zAJ(fstXF?nul}%J{b9ZO!+Q0H_397nbsyI2KCIV$gjbDU#p*gTqU*>ABS{JQQQfh> zsF)WO^P*y2RLqNtc~LQAx+@#gUD+7%Z8oO(F~yJRu53(qWn;Q4<1b5-e@u5}V`Ovj z?yi2!++F>cxx4x?-PMoL3VU@|Kc>6-F|M93*%(K01a)`yN7edKwSH8sA64r|)%sDj zepIa=RqIF9`cbuhRIMLXD@T?2s4^c_=2y60Pa5kHxS~yw`EJx*m#36@in^5WO0ky= zY%9YQ8D6p}G90l*U9}f^H*H|st>9YJ3a&-1;98{5r%~coa4l*D*P`Avi+aB-a+f$s zxm&@t$h*U3a4WbLd3QK4GrDcuMvPNlHqqMA1TGl8nYm}BXO3NChWsTCZ zMrm22w5(BD)+jA&l$JG0%NnI+H%gvwpIpAkTj}l|y_I;Z4dss4dk>?xA7woi%6cl4 z^;9V9sZiEap{%DuSx<$so(g3>70P-ll=W07>#0!IQ=zQ8K=~ApvYra%TMU%-R4D7I zP}WnStfxX*Pld9c3S~VN%6cl4^;9V9sZiEap{!m&S-pU=dI4qi0?K+Sl=W07>#0!I zQ=zP&qj z+l=0%cM_ww1La$3lsN&EZ>3T0eo?-aM)_761^Wx`u9*@~t%6+ykS0 zD~d@GIetu)HF(kS0bqkJok@~t$=x6&xzN~3%$jqaVJIQ@3jqB`E2(_FmGUbX) zxgyhZuE_M9D>CJZOt~VPbL^R3Hs?vFe7lJ9tr5z%0Vs35-WiHHLosJ4<_yK0p_nri z!>^rqznsbVtU_5~h%)cm$Jf;@`KVt(gEqy>LDu(+53$2St%c-QpqjT!UbF;>Cv&M6?n`@$kxh9mkCX~4* zl({C9xh9nLS}1c(D059Hb4@67O(=6sD059Hb4@67O(=6sD059Hb4@67O(=6sD059H zb4@67O(=6sD059Hb4@67O(^TNyv@9SDd(L_3FF2nY1}SitR}sSC`VO(k$Xq4{3j#! z63TB0QGQE^@>@cbb>b+$B}7?Ig7RBJlxro*ZwXO;ONjDYLX_VUqO9yf`7I&JZwb+; zl;0Av9hWk%%r-N=D08tWv!p2Vmngp_MENZt%5Mo#eoKh*TSAoI5~9zkmBUhgIk@tl zxP=~a)>_57v7BQI==+uryS3LG*D@<)-1b`OSt>bO+v}w3r5mK&ixRm>+XdP_SKAkG z?EK3lw7Bd(yP=mYx1pCW-+*q__EjpsTFS~a%A3`bh%_pVN#oL%V>^hsO_AKGvdx_; z%AG38ohr(mD%zz!+%J7XB~Plwezkr;dQf^uIv{108g*HvhK@*Il#VISl(rYO&2KAK zE^~(Dh`je``y_2=XggEeyyI3buRe8|IW>1CMDYyDdkuZRw%1A-VM@-HuH)ATo~_sR z1}X1KN;YY`K-=ePn_qBHe*Q7OHNj&6dV$J|RC4k1Bqf)rgcX2nZev5Rx$H+T58&Wb2!MU=B5%2fvCDuZ&BL0QL+a+N_@$Bwd&9c3Lm$~tzGs|?CIc9g3O z$~tzGb?hkX*io)BDC^i!*0H0kV@FxXj&hYjS;vmDjveJHgL0KYxyqniWl*j%C|4Pj ztIX;Z?ka58t}-ZBnbj-YRR-lMv-(sdOa-sZ^3+0zw=mW~bI#{;5rL2Qx`yuJWQhv!r3BTk*`6U<1FS$^D$%XPu zE|gz#q5P5y<(FJ2zvM#sB^Sysxln$|g+8aF6eVWGYH0~P@vCaD$Wo0kQ`4e?Vbedb)i@GB^&8_T3-4UJUR`#N- z>_u7Gi?Xs8-7n=G$~Ny%ly@k~I~3&|in6j7Wo0kQ%3hR}y(s5QlyfG^%HGwd(euNr zFJJwo<%!jwLVvWn0^PTIE84mG3YAw%uR?#c`f7CF>T9%JrSeZpKckXrbl>XhwEbC? zZXh(I2g@apm@Z{a3=n|C1j6Nq{vlYZg!*s^zZ&jq|UVh1Kv@RuWs*O0!Ypgt>=a^8O@S7((<;oe$$5G; zjG3Lm^Y<2*<;YP1lme*yruF&|A zBv5Dlh}nh`vwZgF*O{He<2fA5#&V#IO>M?)joG=hf9|B&2Y5U$%AdRKF}onotZ0FM z4up0;*ac%|7Y&*f7ysXkn|+A#4_BIfq>h)R9mw&q9N@9kfX9o;b@7zhC8=hgAodg8 zW@TB>YIZ4YTsmX6r4VM#K1rod)&sfA*;n3gc3Bjtb6FCm%`PYB<@08rN(JhDss=jD zDkjXf_Q43y-qr;^>e9{?#ZU?K@rrgBFuRhRSCae6Q6Oh!8k9i2*;PemSMzvvD|ExS z*)_CvO%C*$Rq?!v+}D!(T4JxI{cF2`Hb2t|{XpK&kheMo@_^jcRnP?8W}mHqT8IL5 zK063AW}jOF^mE%ZESY_t=btZtZP3V{bj*S>XaL&$0_}WZ*6jKau-y6tr z137Leg;ulLHP8dpubng7&hzbsPz6oU0evuK_T@Y%hkBs=%Y!fuvu0nJgn6?%V(RE? zT@6sbt{aG}BllNpVae>qY#`>wDj?U5#N0Rr^zUobsm}uXS5J=mcIbyum^S-*)a)C* zkc3H?H~VHPQJ6DpOoIX_gKf|V-7pB#W;>|+ofH^@DYKi~&6;ShX~gUn z+HX#WaUeF*0P|+iPP14&P$%AGc55LFn6=dL=fKJL-Ey<h)fFl*LIe{LT%yCci^ zCto0G)rlwAGW*dQ$b%B7f_i9$ZWx4d zm^FJM1X;k?Jy`@5(7~UNPlZ0S1H>NWH8@1wfhcrBKa9dOESWvE2J)Z;s-PZ-f2td( z|J1nIk8`2d>}g`3ZZI3HG<&89D0`NA&l2+-ujg~^W3<&EcQ&V*kj5*%9g-X)*g*uh|RaePPmUl$<}$f+e#T2l?}}wfsrj za+o(ex(29!w7~2o%EpNs?>74tIetxlem%*bwIAb8t|$3(Bs~8OZTvP03ueDdfhzu~ zFdiq{V8-nCX)tB>hkmmu^1MpFUgh~~t_ zon|wAW`C+M`*SsnoBgE;$TM3HwDZ>*v%jT6((LcV{hhl1;Q2o~%>HRW?tjsjf9F6y ze|oeOIF<$K|A+Gb(8hmy&HmfS-zzVIGGPDG8Yt)Aol607AEVu6;<-QLGo*Q5AtcS? zFLilAE_9j4J(e-n&AO4XrQ7Fz`S|yCfDjUKrZh4yw$VZauz~0(C&%!=fq;DhauyO1t8aZnxGwe zVG!uUNgUfr)JZP~a;1;L6wI4W#enitsDH|gd8ZP8Y9UlW6#8Klh&`VWdo$bT9+PNR+NJg9+g7=T&x zPABi_YakbjfcVqN#r>;yI{iE&4e0k7P0$0By*CHQ{oXDh_Prx83FO0qUJm(k=vz)T zv;g(qN1gYP`+a>dW8RtM73DnEKqCyon0fCnfKs4c?v=gwx0{!n0$ET36|fClc@pN$ zTTA}61;GBb)LGjC^l$9|jKG9>XXQdA)Ik$;0{hPz2l{?CZJeDAg-{B_p4|pZ=H;aV z?dRpeBoLoR`8tkq9r@OgZ`~BknYZ2`4O*cSdd=HV3ytRGQ=UHr)8?H+d*@^W?VVE& zHPB?<#xe6Y5x;5HyaKk*?K1BJ9M=b^cU}ujm{&;O3h757G3S$aGdVWX=gmp;E?{3# z2s&WFybGh|eUSPW(Z)s7<`p+U8}tAF2Byp_FE{Tp z`gd8sd6$<0WuKz#Q`D=dG;eDfWJ5j_L!Wuv`+Ha9K@pUhcO}oSq@R_!PzcnoB!6X- zc~?>QD%!p(Y2MX!K-tx8&;{hWhBmL6Ft3Wos%oI`RqU^tfd%uf<@vQOFl64RY3tKf z=6$Bzyy_I7&(-thUB~|Gh`)~b&r<%`bRgGf>!1n9$Nj(exe=H!Z(BaJ0(CxL0pl=h z-WRBIeJYHaS5pJD{l#LKH1A8)|57LPns)<_wZztrnYTR+y3G5sK?_X5oOxdCG);Y?yu7Bjm0nsY=5l;=u>^Gd0)?h0-*dG#D1gAyl?XSo5Xyx0=7ZD zc@4DRK+bQm?^|U+`L`Nj+`OA;^CohCy9(%2V-xVWgV-I^-7#j~cXG|UnH)FIo7Yqa zw0{eAoBPa*tO44Nv_J<;n-?tr+KJLmECuLmJZj#p)V;M0sCVlKFb*yB<2G{KRt*d0 zeK!v(fb#FsuT}&1I1aPs-A+4q_v?W8odrNYcG8cXJwW}t z3ZV%`&1+AG0rPfM!iaghiQnA;3+C-|lifXCrP=EMIb_}|DUc3@K#t$jhu@dLta*Q+>jH!*Kl!6=a9?IrUT)<7PV z0C5Wq&;}gS!VpZD_aAcnrxJQ0Y2JTRARDG&!MsIsFVcr4;+BY8qR!Hsd3-kT{zu&Z z3W2^Hs{ra8qt0?QOqp-3&<$hedv(wO{pS0{Pz4j_2U)NU8i4pvIx2hjTfjB<5 z`0pHm379kgT`9ovysHq(ftWP%r;$I6{Ao#;gn9E%NQE3If(odGDA2|Uv~dD$oG@$t zyUSo3uK+e^@kc3Gfc1;MfAP2w%X3T$o z1$3I9OP<_3s0HG4iCs&cwQVqH{#jM#bKmcuO?e*euA{H(=FML}X8s21=jXz#`RDYR zzp>8zP3b@%3d+qtmwM+C_kniv&#Qz{^9#vwKHHlMVaogqddx4PUl$VlL4y|PHvb~} za1l8#8Zp0^{uR^Kht>e~KSaF`)2|N?ng0>;d}Q4Gk{W0-|DznwN6GoIETHX=Etvmt z%1e1(N=#`Zkn>`4UfgT`B{@JlpP;=@OqgFr{!3HL-;xRq=6|x<{PH?r-(|#JRu41g zU!D(9VEa>LK>4SLsVId(^SMv=w~~wbdH)JxuV@AKU&;2B{pNFD?pHPdv6V~aUqw4t zb(w!PZC_2Ut4GbhrU+=`8n&ylpb|#Rzm}M5*}it({7)0}Y3hB3{h#T91@o)7!KC@u zl>+TvH*5Z9dHgK#pDTv}^S7;mI+!#6^W^wEb-$1XgXUk)zU$k7dNs8$V*VEkfxdjn zpaGKR-$1<^`pmD*0ru6-n7^Il*gj?cmn(souVh0fESX=I2h^>b2A+R46{!1F+WIQ( ze{~2Z%)hY|$a5pHH?r?W_T9+78{NJ+^S_n?VAv5-^zg^sDN6CLZ|sR8RSDTR08#GqTaVlpbF}t70A;_o<{OClBbb8 zjpS)0Pa}C6`(YHOVafa*YakEEvx7W*&hU4P!HoIeq0R541MPmN7n0`RO#3&}{>>9G zXMR%(aI8&TFaXqTqV6r!y@k5BQ1_NnsD=h;gB}Wh`wpJp$(Y^AnB7?iEkK?-=gn_R zg&ZgX+HR|bD0D(UjKVZ5ng9JYkOw7D1?@0q{!Z%er2bBx@9Z`IF8XvAeYz_jXzMQ8 zx{E&FMeJSlxt;xdp7Yz;-%g*~tDynfpa+Iv0_M!$l>(J8Y5wjsr~&rxX8)cFpuc-& z&F=_778F1kY=cIiPaX8BBMIc_m^c3isX(89Pz2=o!HD^HXG0+ne>d^>(C$5y-9yh~|0e}I?+u_S0PGfF77Oe}v~FQK0UT6i5fkjx+#qM~FKz z22U)z=HXsHNf-HejxYH)1eURpbN{7?Fkt@CHkdd6r79RRf4sr`U!_8)`M=JG3TQF^g1$|VZ-RdQCLJ1K z(EQ(~0I|O%$8Q(R|6L`Jdy<&RcAzh>7_5O@AkQo1unmS_9A?b_eGN3gfcbwYfhM4w z&%pi^$M9+ukn1)2_F6qGng2TbUMKGLKA1Frnl`8FfPPL-oBziUbejJL1Ie)IoC%%7?tY5t#!fP8T+#GRp#LW>mN8B87ZxQzvac>d#RwdLy3v|H%jKPfg|1e00d?B-%ltMK$KpXVH5KO?F`Tt6RY$%3GsDl>hf&mzV z8T0>bkPi7!3f0g6ZO{WlFadMsznucvPz=PqP2AhWy-nQP#Jx@2+r%vpw?N!NA(TT6 zG(iXS!3a#jg86*k=>I1dilGwfpar^M0LEa({QnxHLq3#3H8emQ^uQ2Iz?}JuDUc0? zP!2WF1Rc-^BQOOE<}al|E)+v0)WMMX|0Bo$x`6oq5&u8p|3~~W;*Sx3jQC^3A0z%) z3lMv30LEa({AGi5$cIv>h6ZSZ9vFfNn6toAAR7vy9BQBmI-n0mUhf&mzV84Fezq(eSbK?n2!xmJ*C#exOzNCR@cgIw>Z zggPMCJGx;ICScxzlvK!pBB+2`h(agy!x+q3a9j#x198WZ?>O=uN512_U;v0aZpMO@ z2I)`$ue9RFAskEC) zyQ##b(r#)!v_TIH0r^trELcU{D&kfZ0`0CMA9F;(D%xE|yQ{{5eD4fF78F1kY=cJV zfIb)nj^kYh>5vblPz?>x2DJUIA((^(3)0dc4~R=6Us@B8FO9e~;?l-o28ca@*b}m$ z5Qsma8tR}0sB=OejKCBS|8C;noeRZK3BFiG@S9&$@8l|@axzY!K*z{QoPUiJInb-4V zUeA-ufbl;WkDN^G$=xsry!I#0S&%{A3}Tr#3o?k!AU1=%8SOx92C*5$W{@{C1jJ?% zn^_L@A(PllVl#=&?1xd9h9wKK)<7Q6->gcghbVMHuLY-YJpaEbAx8x?fy+5z!Kob6 zspLMDu{yOD7@t$8EI2I$^!cwe)8# zZLg(#Z3}c;a8??WLIaTNERNwU`gwK;vVlBj6LL2M1dRyy)XphK%WbiEI2m}==-^aPzKdd4=vCMeHMIx{U4yt zdE21df^8)*Bp{>1^H>`V0ZOG7|^Z^(x#n6jX@ z&4TSb-(CY_7JQku!0_xX|z?21FHAshC zD28fifiCC+;=Vcqv~^<&5PM@T6hb-FK?`&M@i&eEv0qDrY#{Dyl~4l>K_6@eb8G;;OY#X8$e5(MaE%-L&jk!<>#5NAVj0HQ`zoW^5@38MXw0ko- zZ?1uU3z`f{p&i(F3x2qT$7UXzt6|E5NUa6Ye3-W&o&wa3_rZ(>w`K$F-P#5t7POGB zg+ARzUv4V~UR&nYg75O$eUCifo3x;neXV)WWx?%fPypn(y&lMOJ9TcStviZN58fbZvtr3+~Scay?K9gD`8szH*qi zpt}@$EqIXU4|c<-1rMbGIUXYJ;dEg8VUF+N1q*sofjm96&l-9_Kh8FNQW?-{T_|{IK1EzEKOFsJGxr>OD#Re)9IS zuYcBp11V4d@Kg&JB#JWab#&s#9q zZoxAp7Cf5=T|i%-qux+16hk#M0sDrMFm1tM$`AJf^%I5A0PK67emze+$yy-KPohA3 z%-;mVJr?|w*q;(NLjOj}fOd|g18p4Xw%})Z&;hd+yg(Z-^ja{={!z+)o(9x?u>l4x z_(cdf)-m>vRX{6DS@6qz7_#7K84&kU9&o%b(bjl25I0WTuZa6quLZwm|F6maGWlNK z28}Rj!9*5RLpzMXoCUvG1JwO3WxpeKl6;fn7Q8|`zwff(55!LqGsV6sp1(@YSBro? zzuE~&V0>On1Suwa_~)6|(>vfz(V=!Yo_-XP|Uau~GW&0HYn zO}EXy8QPsGgC?L)f1>R_ag2W==bwfw_;WT?0Q>(;zQ6GNFWaCSrY)FFgJR%0b1=bQ z$@kX|3;vb`?H2r?D5&GWaZyFku`24H;tlL~pjvHqtXDElwl|LuVx3l33bQ{<`N}$&w9xV7I=&isetZ+O0Xb7sfqkhh&<(_{ zqMcRwK)zL!ucCa_2$1)kDUb_QKwIym>|K<-s~G6ZyGCKgLTN=%W1$n$ARAgO^lk&{ zt|sopP79^yS%}XNq0A8royzuUv~xz6h2EE9q4(DVeSCi}u>bwj7RpTr`kLDUNm#Pb zTH09~g)x}7&{@QsMcG-^Ksoogp|eI|&O&FWK><`kBXq(bOj;<9zUAct`SaM9N4-39 zuA_V%edC@tw2tz1-QdcXEX2KUXg%fYn}GQBl&`0J19>)(XF~%Jzk%`%3l_@Hf>Nl5 zPDsL>h0dY;oD!h?9P*qqX`zj2K-oreY-9{JPFrXbahqswQ!5O?yoCyAi~HSBK^yeL zl!eYsg+i!?7NGoG%0I9MilG*|U=--rd3jI?Q5b+33l-8=(7o?k$n3+63Uvpc{z!Abt4Yf`u-kjf-gGBKmSs4Md>}`Ylx4WT6iiS?D7M z#C;?eN}v+R_mMW}g)x}7P)Q1812HAl&Rb`PEG@WT9(# zehu4IJXY~|EsxjcLOC=*Hw?k7g+84E`4;+2Hndu(nzp`3Y;7vAUE2c-7TR6~a~AsY zpoPBD0@ST*v(VSpSm^7F-8b@pyx*k1->kDx!(xn+@dZBJTbin77aaH5S@Oo_#!ahhWY^57t@eA^O^rw9q5f7UG^T z^cdT{6Bc@$yg%Fq(-!I@_mA2w^aRKB1pRoj7-*-T=l$e4Py+Q9I!LZVQAk>7fIbYc zJuq#drwW1XAEyJkpB}Q%Gou!ImdEGVf0)<=JYQm=WP^o%LLa#&3;ndzLL*HUIzpYF zOKqM0mxW%c2Fk`OfMXl)fIe_#Q?Ov6Uy zg23@CvDHFv)LZCH zV&9y$&n3%@JX!f7Fh!T`)$_=G|r{si*8yVSy~^B`&AHRNHQ zF?oZrL6 zl@|U`w}n5F28}>}O2|_(VBwEuK_OH?9rVGHg+I0iIJS?|kB@g*xRm249kuYq#TLG# z+QOWR!=LE4a2b6n<9S&pBw^0Nm!?Ara4eU00OglbzJ)fn(B>x#V9LUmWkZjJFCVgS z1^X&SEPUk}3s-ho_-Y=RLkV9qYvF4{K>VlSGYb~3Cf{}B`&^2Jw-o|8KVM_vFXY3Z zg|DaG8rrDoweT0C7XDJ1g>OiMVqo759YBs7<}F;?1U)cj;q5ul0A0ZIFB@b-9Sp&& zg};&o+ko74?5`^aj=he3Uo8Q)Z=_#0vj1x#D1&Yb*XKi@g}+{8;cuh@aofSVE;ct`Y+tmAZDO3Z;*+|{SF$?c#v+#FXEqpU|Zl1O9EtKC< z29(`0W8vl;piXlyELb=~8-jWf`V$#~ahS7kl)B7Wgrg-uohY%6sUJA^=_r!txFbe$p-Sau)l@A zxAX!1y3HU5%ApY`zikxeEd1RyPz>9k4aoOh%D+eX_bC5fEp!3#t>kGffI6W4R?1t6 zZ=C|#zdajh|91A@PW!j_0qx&Gy*p_Cj&h*=J1D<{e0R)Q_)glnvlwXq&Nd+5os_px z-bQ&F?Q<;)w-Nt+@_e86zh4LB|9&5ke<$TT$-lE4$iI{Fos{22`CWNX4aDCy1oIYd z&xZyN^_(}3gIP2BE&n6vO6^6jaED0Bh+-9!C7OBU|P2I4!| z-$8pFeK2m}9~jWTACv?A`vK)Y7=<|t-@OKkVH>mo`R=Ct9?I{b{GM9q0^&Q#(^&v@ zK>p4?AV1gP@V(@}w;afSFXi`AejnxcPd3f%f-NzK`;5%Daj0t_I?}hhW~q z59UJ!G(j&+S@@w;D295V{zJ6?a2nA5!%ff+a~AF)Ur!yh!w}3`_>pWV1L7Vb?h)c1 z&4O}>0&$PdTllejsD(Zte{UL)zn6Nw)a#wI@P6{|uY+!wvhd^NdAu4rf#*NW0rvlJ z(87J3Q~Jt*zWj(7?o+}~roya+`wO8S`e53^2hyP!C_B&tBNjfG1~nEwM7={x79Jq~ zQz=0Hr^xZ+N((<70{SvY{lOj!KT`z6J-ZF4^IU_4hYX@HZQ;WuPyw~j3L_Ryus=cl zL_U;4HBc|nX5r`APUb-)j9U07lns|bpM`OpZ4gDwjn$%j^$v+&O!i(YN1KS$Hx9*q*F`UKqFVD;&owMNkdYd1czdzo)(5)1Tjy zbBg$>NejQqalcCLS36+B!mrW(Yh^%Rnac{lPR`f6EIduVY09SuE&RtUXoPMUweTC1 zzd`*s*8sWRtb!H`&lEr_%v<M3q8;GB!?5{;IV&T8B?{6)@ z_TLMExWCU@c&-wX7JjP+*!PbVpzVKj0mt-D#`2$i7M@QB+MOS>@V`oc`u{edoqzYj zf`#9X0*?!{`yclImpuQapNs5Y3IWHmlmi^YQi+BCmk<3GKF0R3ZVN9L*a{2T3NIDv zV8T}Tg|;Fvs0E&fhzXTIhph-#+lm!gkO%Bv(FjYn;vJQ+U@KCRw&FO-SC&JctvEgf z+H6H?m91D+Y%AXRf5>|iIIpJvkN@25oO{pR?^M$Ik%^YkzG;d|8+i}`Iy4j-`CIg`}_SLe|gU5-h1BX z{eGYKS?`&9KLb4u^f)UCb&nD1Wq}f~kx)Mt949nrOK40TkOLqS1N{}M0bH*z2uuRA zzzVPx90aEbt*C-(0RC1?23Y{I6&He4U^h4n&JbG307RyMQCLeR0GXGGROjxz(TMJ>;{Ly8A30%KrCnr(m@`W0hWMu zU_Uqp&JkLr0*C_%U=Sz)vjAkPtO92Ut*QdZRc!^jfPMgRueuDN9;)sD2fz_l@ZnPExpWqxu@yiGI6!D^52OO< ztqqylrwF}F1>*sZFSkG(p>@^~TDJng^}1`oX+rCvP1VDBy_sM!*a40cS|2*=w*v61 zKGNz#zWyPCW)5Nje2PQfxLE*caRGfMndBu8($4Te>~1Rz(xm@wIg(OTuEpG&Jzm35keE8GZ8vFWr4GVc82}Vvk2`1 zon4@_%P@dEU5*i&L_jit-K50;{v|<2SLo{s`L3-1(z_y0SLp2unXaqA4sZ}0C$yUd zLIApPKS#U4=WbKLLI6A6ptIW{LX!!hDahLs>FGM5eKr!>7kT?aZ(r!?Hy4~Dv_I1O zA0c$WFtCo$ftA5NLI*+rAm|*7yo2+=ZbFBof@Opb4S|`24r>J#6PghRAUhng!x5L^ zrwPqW0I-?W2+RQI2+f9`>|=zEfXxv*2px&Ek+7N51so(acLg|0=%{{RAEBdRXEgGT zK2B&JWb#%KI;I+!1)wLtEr34-mBC^{3+n)!UxQEl-kKK00_ZP>f5rGLX$7F86!ymw zFon=@!w4N80?P=U038#v2))(bq5HY47-ydHyJvw*FiHd z32Y>EN@dUO1N*@-aE{QK6+j$F0E0jYm<3jVt>7RyMd-~cs0NyWWRL|W0m$BhSl!YEAO^P- zf|+0$*a!}QUc1+Y0AHfO`; zY}lN=5Uc{b!C`QQ(Az8k+1uKJbdU#TfF)oZI7a9kq|a#v62Kr(@(%$0b9NIt7xw4E z?%W(O1uO*nz+rHj(0Mwj24H_4$~X^YoCo{3*QN880mNz^0dO2Z7Wct)0m`%hWm*88xF4nqR)DSGAUH+nLKVRNLfBuJ46?u^un?>Q zyTM@q+1sZ8=)NNl%m7QkInV&U?tcA4uRu@-V3|; z?jv+rKY-(9kYBb8Yy<}YWbPxNGH3+40NB58Jb>)|=>Yncj=zzRaw zGy{{sIzpeQ21>vx0J*gUBmn4GdzR2A$Ahheu2TVQtb=dsrU1yV6W31@x*pfDA5Yg0 z1JJvEF@Wy%hY8()d>a~pegOG4Am4_A;2fb(#e!r2`%f(c`vCN9tOlTOV;)!twt{1X zZt_4Y06Uvz0od3C8=GNca~wzqu(5e1H~?T{3v6tGjV-XTWeL~~U<3EmbZdJ68(U#x zD{O3qji)Wp48X?IGr<}F8{1%G8*FTYjct>_3IH41&Jwyk1Yl!3Y;1>(?Xa=^6rnpR z0NB`(1LgwQz_ruG54*GX+2&?$zmT z=-PdT&^?U+WcKU;ka;#494B-y^zDV6eMsMz2M!YYTpdtI=<^l88bV*d`3t)V-QO0# z#*2`9@d%+WAto=CfQ^K{tODph&}z;19590O+veNzP$KrCnm z5gdS2s1rQ6Gfdr5avOo!#0TzN4guaFHzLg44-nWtV zHsbM4Bd`@9CWj9a`tC4L2&RC!U>R5gAp7nCa0Hwt^gRMR5CV-rdyoo-fkH3^%mvH9 z8n6Q#07t-SLfgutvxNDO3ziX9p#otQ4-$6K0m76A;KChCaekaHakvOOQg4MMp!%KX%C(4k=7o%;*$yMpcB>+ zb`#nYmI%9@A=~*BVYnY*NuVqAB@ZJkr5a(W9@tM<&wYgTLZ0*~g!O^Weys=_0KEfq z2phD5upzO84Lw3w#$m#;Dibzh7GWcI5S9xcMx7=s5Bdx05O&Q%!b-XjR*JN7lL(st z9avMd$;gj&Fq?jmup8j(P0I+o`8Z*-kUpmoVe>f3}Ndx61D;UY=AGA zN7%+V03DmA5VmVR3`EMdBoi<_ECtik8%Dn&Od?d@kxZ8 zNG0rK7Ga;ZCF~T^PMsp`v#o?-UypqOPQ#}!!B@Ec^*O?@SI53tN7%Ou3HuIw4|_jA z&yV;#2R%QnA?)W8!hY#TbOPj9qAOWMSC0{$s^AdO*%YGd!-&pT65Wm?y1Rtv{tBYU z9430DYDBM`NAxPVUiB2wtL*^DTYU}DYa|oBCemx}CVB|@FR?&-aD?c!@L6jTfX-Sw z0P7bku=doqga4I7{@p9%uw0SGN$%C3-#Ntq1vf2LQ@dANuOYg7#n#7!MYL zHDEtDPV_hxAYUB(jq3+W0CdHz09tAaCQd zL~jE5rgZ@1n}q=EHN*AhxYoQMfPc*o61_zn7!P(6y`=|`)(U!Cts(lA=>VUtbubJd zf14#lZ`%qi1UPR8nRbx5ssbnkr-^=b2|!wV*uZ{{9>0?49V{>&9430lDMU|z-Gl=~ zPizL35xtWN<`TVgELcPIE;#Q3n_bosJqfZ&tB8)h8old&qIa7_^kf_-&je?P-n}1y z-IRqy?*Z8!Nbhlw=&6uNT}-S7!C`QQ z=!0QvFmw)v+~7f=1VDE1DggU~AvYMhhExM>K|06-Gr$S}nIXpjWQJA%&^0sx3cN23cSdSO`{u z{Qzc&qbZjBf$LRpJ#t#BVh(2Ko z(I?{CMA*6(`LCS;@O@*1R34+Gllmlw;H_`skvlOK<8f4cL6lBZLP!{ueEbhPYPCaUy(*|9=Q%^DWmK~ddt?bw&tz|r=9JXcGZmrZPJ9bD8Mx4lXk5p>2sO;Fs zD71xiBn709Odv)0I{%S~a0v;KIwX_S#b*=J2>+VmXgDN7qyuD1NHHiP*(8IEA#u2x zMDiiu07vm85C1|W?N?pJ;j?Vy$wr>B_?ZPs{uivaK+cHObXXb-TchAk3zmaq!b!5kv}7>P52hVu_#dpe&^$7A!LT5B)O0a!Q%p0&j_b+LOK={PAJM9 znNt#~lUX;^q*0@$q2Uuk9db*GONz2H#>9n^@-rKR;`8!CX+o+vl$KqbT{Jd3s{#LA z))pdbddAo>qYCmzhB{>A{ELhU*`qT0l!h{MGV(`e7l$&6vO~G~p~BMPdAXUPtb#Ea zx%u$y!Xowx7qA$YBbj?;OzNt0{r|dS^kPlpK$#aBSxZ*M9XP_cGHrO zKG{XZxdr*5CJmb6?0i+f@h{@u`2ry_BCJicxDeW0q{T&xGom0Lu`NNwNx0RPpw?RB zFS9m8eMM?|ENZa<@)qE;2sN1<)>niUl#T28_*sIiB_)Ne8#c^BjK-EWC@v^1%FG^7 zP&6{TL4G!pJDul2w2ef2$M1WC=sBXLi)@3udosWbFWKxuQr zamhv7EQFn+aD=juJ_5(0#fttYTJ_lQHBm2-NdC4Q<^8J^GUfg2UzLWV|Nc?gBW)l9 z*3OUG@5dL8zlrzv{|W2A*ZH3xv;JuG3t==7iTrYOB_Ht)y0Xwh?(n`^|ui@GBE}Gs?=xks_{?!ivx$d@VtG#QPCA z%ENULr8>X*SZ*OidO>moQpO>lh?1~a9usk;5Z4P}r4&|#hf!~`!X*%Onu{2fLWf9= z#DV`>hTmHd+C>~P;aMs4Mq@G#MfMTsR(L#EO>tUt5UuG|>XZs4iNoD52;B(H1URiYQ4zxZK6zNQ{C+ zW;osvucGxg0#c&9Vw{fFkSMumiK0#-Evy(bi|C!{VM`)r!yzM9Gv#$qUQ^MS3V(h- z667PN&?{P*h(LL?qJ5=2Vxk{J+xYqUMCru(MR?BPi}2(8wibzoxFYob>-rSs6nP_c zKPLPcIX}Oz|5;s>gl8f#vx-qblt`@Z$|Ln>dBx}`R+SO|g>?~g(U+qmn($rpH_<;M zV_d{jF}jLbK3eDH?Klz#v4Ru*JQC+pXc1=|=c1*Ez92jm>s>J(MO*hs$d%8l5lhiA zRFp2_iI9&(HmWUJMv?O8Ws26em?uOD%3GKyX$b!ORSzz-^V@j*z8@8b`+}%%VL_DO z{2CU$AhJ3To{4CO!awtICdQR$oI{X`ToGlBw8%&gh>XZ0E&?%oWQR)^nX}6M85eG2 zS>dSH`h)qf7IH=+AB}D_2gjHloo#EK*UBREOERp9_7|OBM4O7tNs(Tbk5M4f52F2T zEbQc-SF)(RY%)Gv)1pM8z6$X#GJA<$FJ_tOICFlzM|_T6;lG=2A`uXCmYB!I3>Rtl z!eh~DE^PbJb|UIYNdD2-E$T$9*F;+uBX`vQKZ+_pFLLMC;ot46VrT%rs@zB@=S^E4gT%dKAMmC!6o5%H`$&eEX2 zOPSB{(47?4C;X3tU!B5wQ^L7KJ`p+LX*@{9&oo#~f`;D6CCclf!4_af`GCQK#|9(LL|Tj_L(>!vprGr zNR34Fi96);OCjp1Z@4DP>myPF!b8z6gvDsg%4=WP6h24&5v^IYvS>Y&M@95M(Z+<= zq7{fUVL|i}(Z)nAiL_|{6gEWLiHrfg!+E1xMZOE;8-k?hM^U+GzCRl~A~A~EzOV*G zYZbAKco-Q;L>wY|BW)?t6Mj1;MMn8V%xhwXEG(PXijg<6(v9BtMdtbPwM}I1Ie*c?hZO*HWGJ%+2N=~<;t%{?v^5RR{7nV&>5YRe}8{0+Gu2VQOK3AddpWI zk=0}5o=D7z5&y-CNUYBzYe2Eqi>yk-JT2~JBiFGiemUE z=6>Nt#8z~jQm!kqt`u?+Ya+rCZ-kaeG@~no-`_d$urER~dS@u^NX6>E2zthak4wT{ z<>Nd9UPNSt%tV|=?^Vv<|A~5EUI&rAncvnyv@E}mKffUUVr5v278JRc$_+~vVp*Jv2#g4)i8X7~OA*KN)+g51t;2bv>rhcbu~rf*^hlc$(G{^4wI}X# zg-vm%5s$OT9#=HBBJLd_b>Xh|e`D9_pWd_nzqB6|+1nCzcK*67+QLK)NA4g*&Ha=6 zLBj6u_k$v>;lH;ZB)o|1PW{nZynLKEe7tFB#d;3Zvj$$X~ z|JuHi*uVV!zEXLg{_pH7@juxw`0wm1MOJc=@m}LUH~&-ac*{}JuzkM^_wvwKI; z-Ti+(qx^oyC;I!&e`m)>*op2)h~M-6Q#(H5?ngY8^!Ijr#IBAQ{r{&sJ{)U-=o3Hx zDw6-y4o%7LR#xpX>O`MB;o)5wan^v4PT>c%#G`8BIk{h-j|~!I)5dQTEtSjz=efhGsNNf#mv0YEaAzx+>)Gv(h@i`Cb!Ixun>O65>NOQgi4E1P*I$? z(3osdY94;_u{b9#RBj_q*lbu(6e`ZfqnD7*g{x)7|BY!;MyN&1V30>b7dA952MtTu zz92&gff`X-l%HFigT{x9Sp}iug1As|>F`n6nI+EKW z&MRj@KH5c5XiNb*+#eM?R5GD3dqf5dHHbKMVHw9{OhBhZ_N?3yxuT(EbqS=q(8BlE-I9vSJY$Rt{5MkbV?=jQ(!)#A`^EQw-s z7zhU}BQNxa3d-_C{V3N4_ww^5gmTYo=3ER1McMfoVkv^(lvtEXB=B0I+tvz)3^kKn)?bEaqbCWirKkf@Z(|E`!%aiv%N}2X z9uvwaEJXLp7@mjbUr;vWK(h!tkM}bip&p$=@hSa7-I7ug;zEi2QqvN9_2NC!LP_0Ilams0Eh(jAa_@ws zl+K|J$d}S1J(Qf(Jt-YZ(|d%omZ?fg>?M?SPfY9B1)AeKBqb-M_mAV9lG0P46V9cD z;zOzNY3WHFdnd=Ig;IN`rS|BR2ptJfo062$DGgQukSQG( z<9K>nd_rRP__S_uAt>vCBBzDIA`Rdrbc7Q7h|ImZ#3v_*IwYm{N>59S?=ECTbULT> z=$^s!@)&{ z&WS0BY4OQ%pqI6zMLhDK#pP5~VzE&LV+>xO%Jd8%hPE1Vb zlaWCHExL0)I&S1z_z=?)dRBOPi;Ql+jsxN6_%X_hp*K8yV0gq#6d8K6#%5y#EEc1E zK@l$ygUPsDOr&C3!Rn+SGUtVgGxA^zxx|k%w$$gCaWl1>-P}PKJd=xyUlE zD7OTINhqTfQ)E%@#Ik8vtbatQ!X=6q^~2dwA6$9Q(D_Lg8iN z1#KdZ|4-IRk+(#!R^sJ;gj@Rm(po9JVE7NNm3XAX{*Ts5{2yE^aWUye)=J^({~K#1 z9$l4%*9CuLtrT8+7yrRpDf}kUUt24M>#c09bp9Jl7c7@BpX0XdpIRUbX0RTe|vSrBWujxtd1^R zXaAe4BeC4Sprib{@DcZ&f4uOCENTAQ!iWF)!spjrfInII@bJdZ`A+_o1x-ox^~(0) z%@f`LAK0D}Z@T`ycLp1V_w7bw@2(+;Jb^tPJ6__Ayh2>N@LBXff5WihxZKgX4RdjC zH@-n(PGQ5c8_WyeloStvh{sRXK_MXtniU2RBP(vDn3t|smAdy5YE z?TOfr?i7ALHwnMP=!S@O$NsZ;a-F7Ll3c8S)WWQ zCdKbMe&r80`7xlV#Xfz6NzJ-e?lfaW=WA$Nt39=B%S<3ewGZ0OQvLzUnC;gk|Vj2BzclA1yYPuL8>TK zk}i@eOBYL3q^jgB`Ch6fRhMc=HKmYriBwCvREm{qOP5KPOLe5WQa!0Y`GLGC#YtC4 z4Wx!rBdM{}L~1HElbTB{q?S@E=}M_J`H&oy+DL7scG6YS)lz#YUg{uqloF&wsgu-M z>LMkPHRK7YtJFM5m3y~vN`963fllG3H#QXi?W)KBU!4Uh&(gQUUI z5NW71Ov;dkOPNxZlr4>rMoKwSF4-cDl158;(irkF`9#W>3Zz2m8mUMsmP({jX{zlcejU$2Ya|^n|oldQw^^t(P`PPe~i4P10s*i?mgGTG}RUmv%@yrCri9(r#&w^sKa3 z+9y3HJukf=?U!DZUXosx4oI&^uS%~;2c_4gH>5YEL(*H)+tNGIVd-7zJ?VYvi1dN< zp>$L_CVeD*EPWy!mrh70rB9_((r41=(ihTc=}YM=>1*kX^o{hb^qq88`d<1$`cXP3 z{UrS?{UQ@tl4V(uRauj%%w%0QWGu<) zUG6RSk^9R1t~^hkFE5Z6%D2mR$cyB~@}2Ts^4;hXP5F@gmi)H-j(k{tSAI`^Up^v#Ab%(ym5<3E$sfy~$j9Xq@=5tq`IP*b{JH#v zd|Lie{!0E@J|ll4e=C0{pOwFte~^Ea&&fZ@Kg+);M3EF(Q503t6sj;qR}6(KreZ0! z;wY}-DZUaYF-ircqEbn@NU5w`tW;5|D%F(gN)4r^5>hTvYAKg0u}W>_GUalmj#5{t zr_@*Clq-}5N<*cQ(pYJtG*y}@&6O5POQn@^rP5kyqqJ4pDOV|1EA5qdrGwH@Nl+4% zPD*E`i;|>tRk|t3N_Qni>7k@5J(V=2my)jZR{AJ?m3~TpWq>kJ8KewWhA2aoVM+$M zR~fEkDp^XlGC~=t((uxkf2cij@+jR2i#`BX=v~l?lp3>3mGYRfT6tVqqdcLk zRi0GVDeILD%2UclWs|a5*`jP!o>sOg+m#*4PGy(!jIvwVqdcqZRrV>*DbFh}DEpNc zm6w#4l>^Ev%B#w2%0cCIO8HtjqkN-$t9+-NRlZk#P<~X-DL*McE5E2jl~h?( zR8`efsxnnq4V9~=YN@vBsIKa%z8a`8Y6Z2TT1mY~t*l04Yj5kQZG?! zsh6s;YHjs0^>Ve2T34;7)>q@yE7S&RL$#6GSZ$&b2@5^*VL3dc8VDovKb#r>is68`K-so79==&FU@ct?DdwwtAa7N1dzA zQ|GG-)P?Hp>K*DLb+LMrJQ?^l=Ok83@kKBzvVu2dgZA5kAw zSE-MwtJTNVHR==UTJ=eFow{D#pgyH;R5z)c)h+5)^=WmRx?SC&?o@ZFfAJ?gXS zUUi@Pocg@_g1TRQQGH2$Sv{b>fbiXsvc86Qa@HdQID%9)RXF`>M8Xz^>g(L^|bn>`jz^%dPe<5{Z{=>J*$4N{-FM- zo>PBPe^!6dh$d;Wrf90BX;fpHt{EEFOwH14&Cy)V(|j$^VzdfcMXi!{kycr|SgWE{ z)v9ULwHjJYEu>wd)zU82Vzt`ZW!mLh9j&fbPphxRX;)|sw1!$Et+CcbYpONVnrkhz zmRc+AO0BilMr*6J)2`C4*4k_FS_iG8mY^kSowUwc7cEKas&&(nweDJq)QtsZ9p5+Mzk?)LYvZN zv^i}-ThdnaO4^#Xp>1h9dKJBzwx{v51MNr?Xd>-IJJT*SiFT#kXfo|iQ)myGN_)~Y z+KZ;s-n0+xOZ(CObO0Sl2hqWF2pvj?(F{7AX3{L0O-Im?G>7KWQFJuTqhn}3Eue+; z8d^k)X$dW*W9c|Lo=%_>>9uqcy^c<%*V8F3%7x|Tjk*U|NK1AU5aq?_nwx`l3~Pt$F5JKaHd(p~f!x|{By&(ghg zAAOEKPhX(>>5KFw`Z7I0U!kwk*XTj|I(>t_Ne|Jt=-c!idYHaT-=pu-BlH9MAw5cu z(U0iI^b>lVo}ee`r}Py4jDAkPpr`4V^eg%`Jwv~t-_q~sS^7Qwf&NI((Vytg^cO~$ z#AK#0m1&GJ#&l*d&P--8n>oy79`jkiVps)MkyTpYwPo$tRqSfk zp2f2ctRqWciL4Xr%(}27)|GW*$*em|VLezX>&ensFP6@Fvp%dZ>&N=D0c;=}#0Ik= zY$zMXGT3mI$+B2B8^K1h9G1&QvC%A#jbZt$fEBW9SP?5`C9IT<+ewEoOJJ zyV%`q30unUVfV6S>^^osTh3On2iSw`A-0k|%pPHnvQ_LcwwgW8*03koTJ|Jc$JVnA z>?yX9ZDO0*7Pgf=&9<@aYzN!PcClyJZnlR#%l5K;>^b&4dx7m|FS3`|%j^Jqg}usN zV+Yym><#uNJH*~%Z?kvUVfHS2kG;>1un*XW>?k|NK4KrUPuOvGf}LcavQz9c_Bs24 zon~LMuh`e@4Eu(C%f4f0+4t-R_9HvTequkfU+}ePk}m6tuIidjb*AgOp>y5TE#1}~ z-PJwa*8@F9ub@}dE9n>MmGz7DDtcADnqFP6q1V(y`XzcT{Zc(vudQFEUyd(;tgF|< z*EYuKSKx~k8|sbp#(ERIsoqR)uD8%z>aFxE@r8wL^tO6C{VM%xy}cf zT+h_A^lW{EK2p!obM;aBXgyCKqvz`ddZB)eUZfZ6C3>ko7GDiFUY~$3gS%Frq+h2` z*00y6=u`D+`gDDUeuI9aev>{^zgfRUzg3^5&(?3#=je0wdHQ^Pfxb|`UB5$Lq%YR* z)bG;o)|co@^?USt^=0~f`u+NHeTDvj{-FMlzEXc!e?)&&U!^~$uht*e*XU2^YxO7f zb^3aJgZ`AhQQxF**0<e) zep3HbKc#=Bf3AO_pVq(BztX?f&*mQG&Y(TO^s$obEAdP(r9H|X|y)l7;TMq##P4E zMtdXP=wNg-5{yKnlhN7eVk8+|jc!J=(cMTfdKjrjPb1CfWuzOujXp+Sqo2{=7+?%E z1{s5mA;wT+n2})&H!_VZBik5Zj5KnLTw|0m+Q>7;82LtlQD|Ia6dA=viBW2dHO3j^ zjS0p?<62{qah);QxZapzOf{w%(~TL%4aSYeO~y>)X5$v)R%4bi+qlh`W6U+?8S{+= z#zNzE;|^nyvDmoNxXZZPSYj+S?lJB)mKpaM_Z!QN6~+U`gT_O~O5dW0SGj*kWuoo;J1_+l?K@PGgtxjIrC;V?1l@HTD_L z8P6Lp82gPEjhBpIAVNYd}tgs zju{^r9~++-$Bh%lN#j%Fl<}GIx$%W@+W6A=%J|wiV|-(LYkX&%HNH1~Fn%=789y06 z8^7RHM~TZ^;VRcS<&5jx;GCP>;x>1<%RTP%fXDC(ydtl}FXEN?#k>ly%B%6}yauny zL;Mn6i(krPd2N0fzns_Mb$LBrpU3elcmv*$H{y+X6W)|Jp2BKG~K9k?fZ{fG{S$sCXjnCn8`8+ef&B8Jb!`j=P&Y?_{;nN ze}%uwU*iY)>--J=CO^dA;&1bJ_+kDoe~-V@lm6X=E3<$*gYH zFl&-W&5(JCSJ!*={yAo0v__W@dA< zh1t?WOg>Ym`P?=vzwW0b~jVZ9%ic9 z(@ZmandxS4vya)=>}U2j2bcrRLFQm{h&j|8W@ebf%}g`P%r-}uBh4H$*BoVzHuKCe zX1-Zq7MjYOXRLGgq6Bn`_J`%(do|<~nn|xxswO+-PnxH=A3`t>)9_ zHgmhV!`x}^GM_Pbn|sV>&AsM6^EvZ*GRu6y+;6^UzGS{^9xz`qUo~Gd51Ox=Z2mw*!;viZk{ktnxC4d%+Jiv%`eQ;=9lJI z=GW#K^BeP9^E>mb`Mvpr`J;Kx{K@>;{KX=cWXYCdsg`C@i&?s5Sllu#%d#!UaxKsD zt-y-0Dp(b*O4dbIW$R+AidEIBW>vRpST(JXb%|BWy3~rbYFn3Cms@qLx>h}_z7=O( zVKuNCT8*s6RuikK)y!&cwXj-Rt*k4p)>a#Dtbx`bYp^xM8fp!*GOXcNrj=!7 zTO+KIR*scxjj~2tdDa*!-zu;Qt!u0ztJo^BO0BWhIBUE$!J24YYfZAQvnE^DTT`s5 z)--FnHN(2Wy3xAHnrYo^-D2Hp&9Y`&w^?(nxz;>uzO}$wXx(nzVJ)&2TX$M_S$A7Y z$YN`$b&qu~S!pe^?z8TV2doFJhsa&lN_^k!7uLhpBi5tVD(f+8we>i8z*=KH zVXYI+GK6Ewpd%Or>$*diM8F@VePbbSUbo(`-n0%`Z&`1Xhpcz3!`8djd)E8b5$gl%L+hw@ z%=*as*!sjeZk@1BTAx~{tk0~^tuL(8)|X_C^_BItb%rb?w_D#>-&)^UXRYt8AFLm( zbJkDR&(<$Cu_ar!6Y^j9tO5Xjif?vMbvc+g0qU zb~U@YUBj+vhwMx2TK1)OtXNW7oCo+4b!>`wF{(-Oz4iH@2JDP3>lObGwDz z(r#s6X}7l9*lq20_Eq-Pc6&SC?qGMc6YNC0lik_wVkg;M?QV9m-Q7;Hd)TRVPdm-- zWvAP{?LKy2yPw_P9$*i&2ib$|A@)#vn4Mt{w=?Z5JKG*%kF;~_Tzix~+Rn4b*!gyW zU1(op7um&jiCt=swa3}x?Fsfo`&xUFeVskozTTcM3gx&4KG+Wyl1%Kq9uV}E0R zYky~-wZFH2uz$4A*+1Do+rK!(ksR4k9M#br>M%!l42L_WV>!0tIIiP4z7sexP6el; zQ^~o=sq9?rRB@_0)tu^14X36PaxQUdIhQ)IPHpEh=W?fxQ`f2I)OX^XE1U*SL#L6` z*lFT4b(%TNofb|@rC zcPGW^;iNh}oiwMHlkW6(`Z#@^eolX9fHTk;vH>sIWwJ`om-q+omtLo=Qd}KGuN5t%y$+z3!U4YJDf$%V&_ihF6VA%iL=zX$GO*8 z=G^Dp?<{v#I1i9b&V$ZF&PwND=Mm>oXO;7qv)XywS>rt6taYAr);a5)4bD@}MrV_= z+1cW3b)I&%Ioq8b&Q52S^Nh3G+2cIx>~;1z&pFRKFF5<17oC@!mz@L7E6%IVYtBLE zb>|J|P3MsFmh-mrj&s;~*Llx*-#OxZ;C$#Db&fe7IUhTpILDn6&PnG}=aloA^SSed zbK3dR`O5j)IpcieeCvGYoOQl;esF$t&N)9hKRds;#FbpxRb188T$tA#xxO2?F>VF7qFc$m$gS*N>{fBBy4Bq3ZVk7l8*(pkYq^)Yv2JbmGWT+~j$7BQ z=hk=Q+$-D$ZbP?`+t_X5Hg%i1&D|DmOShGKrQ6zVh(4 zPHtzni<{(jb-TIAZg)4u?ct`nJ>4|7mz(bPcKf(}-F|L=cYr(49pnynhqy!CVQz*y z+|6{e+-!G*JJQW@bKOzyXgALtjr)YV)_u}l z=dO1*xKFto-A(RhcZ<8#ecIjTZg+RMJKbIGGwyD8kNd2<*WKqn=RWVg;O=){bYF5` zb`Q9(xUagexd+|X-8bAf-9zqM?%VD=?qT;`_dWN0_lWy}`=NW(J?4Joe(ZkY9(PZ; zC*4ooQ|@Q(=k6EoY4=O_EB9;njQfrIt^1vO*8Sf7!Tr%a=l@l;Rq zsK-3rGd%8@p5@t|gUM24$ud;WsSH-L9Rr9KQHN2W$$h*X=*ghU-MtjAhnMR0^wPXuUb@%Y>*MwH`g#4m0p37w zkT=*H;tlnNc^TetFVoBNvb_=BNH53B^+tK4y*zJ>m+uvLh2Aw@kyq@Mc%|N0Z=5&Y zo8V3KuJtB)*Ljn@>%A%7RBxI$-J9Xv;N9rm}J>_lmHhG)9E#6k|X>Xgi-P_^q^mcjAc)Ptl-m~6bZ=d&^ z_q_Llx8Hlwd&zs*JK(+Iz3RQ@9rRxJ-tgY^4tZ~RZ+q`}hrM^b_q_MLBi;wzhu%@| znD>$QvG<90+&kf&^gi`Yd7pWodtZ2`y)V75ysy18-Z$R2-gn+v?|bhD??>;P_mlUt z_lr+_$(Mb_SAET=KJ#_o@VRgLmT&ux@A{ta`+*-FZb*Cb^UsNeLv2>!f)U=^c(q&{U&}>znS0MZ{fG}TlrV| zt^GEBTfd!um4CJ0-jDY?_#OQOKhf{xclNvZNq$$qo1g4=_fz~HeyZQoPxE{E>3(m& zkKfnt=lAyq_yhex{$PKIKhz)QXZXYYOh3!d_DA?5{Tx5nALWns^ZYS>zF*)M`q%hH zez9NTm-=J3!++C18jKCb1>=JW!NlO&U{Y{hFgdtBm=a74rUlc38Nm&~jloU9 z%;4tWmf+T4RxmrbEtnI`4dw;&g9X9D;P&8-U{SC*xHGsbxI0)9EDi1n?hTd&_XYO{ z%Yzlc1HpsAL&3`6;oy|c zwguaR9l_3ESMW@*JJ=IE8|)4C1!7IV5!E3?6;Pv2*;LYGr z@K*44@J?_zcsF=2ct1E2d=PvX91V^I9|a!=p9IH)6T!*g)8JI_S@3!AMQ}R!GWaU^ zIye)26MP$d7n}{g4}J)K49*2V1wRMB#E=*%MvhTp)EF&>#;_PY#)#oDW{eeM$2c)= zj2Gj_1Tir&6=EvJREoJMrgF^1F;!x!##D=`{{K|>7JhPD*ZY4PdF+@%qDdh|m+P)r zTg=Qz3N~p2O(|{H_Qu|14PAQ!ZBu5(l$n_^W%_)|%*@Q}_A~uH(s}Q$lE2{h%j*?i zojV#m7o_Jo=ib#B_XPKP?)BXpxHoifnHp5vbD-pxJFy}Nr4_nz*(+)y|ub9cBq-Cgdydx5*# z-QzB}7rGa@i|$@`pS#~Z;NIU|au2$P+-3J-_Y(I~_cHeZ?gQNixtF^Sb|2zC)P0!y zaQ6}JBi%>2k9Hs9KGuDl`*`;W?i1Z7xleYV;y%@Vn)`J38SXRPXSr9n&vu{VKG%Jo z`+WBW?hD-)xi5BK;=a^keVzMy_YLkF-8Z>!cHiQ@)qR`$ zcK03E`0zD8;=a>;m-}w_J??wm_qkWP?{`1oe$f4p`(gJZ?nm8^xgU2w;eOKnANPOV zPr09VKjVJZ{ha%G_Y3Y9-7mRccE93&)%}|Lb@v|8f7%ox1<^uHjwNJHm6kb>6^R?``mo^fr2%yv^PgZ>x8dceHnmcdU1u=X##! zdw~~vL+^O+THdw2>v-4oPVlbhUEjNbcSG++-i^I&-c7uldN=cK?%l$>rFSdu*4}pS zHr|XEd9jyxsh4@VS9qm2>)qD7op*ch4&EKTJ9#I1BX8_YyvnP+#yiP7**nEM)jQ2Q z-Mh1QhIgiS7w@j#S>D;+Io`S6-MsU>yLG(Mp?8tD=X(|ecqZtp$bd%gF0S9q!z+dlg@Q?I2`kVaC{uY0$f0Tc;e~f>uf1L07p6~mC zANoW8c>h}dwf*b(*Y!{EujgOizkz>4|3?0e{cZkD{G0kW^Kb6o!oQ_|EC1I1cK2pZKYt`MF>Cr9bQ6*1w&9d;bpp9sN7`C;B6Q>`(m4ul>e9$v@dY#Xr?Q%|G40 zvwwzvrhgazuKrp6+5S2Hx&Gb!^ZdK}_weuO-^;(Zf4+Yo|GxhH{5gMzzti94&-)kn zyZt@>f`6fZk-zBg_4oPv{R95}{U!gPf5>0cb|G5%xy$N7)xulL{JztMk_|7QO!{#*UG z`EU2%;lI;=m;Y}6J^p+B_xV@)@Ap68f6)Js|6%_l{zv_f`5*T`;eXQqAOC;-Px+tr zKjVMa|D6AM{|o*X{V(}n_P^qP)&H9Rb^jawH~nw<-}b-bf7kz>|9$@l{tx{h`9Jo5 z;{VkDng4VD7yd8(U-`fGf8+nw|DFGP{}28j{XhAC_W$Dl)&HCScmE&$KmC9Chy8#1 z|MCCNpZfm|t`S@_I3jR@b-^H5A8ZJY3^oRvg3ZB}U~6zxaCC4?aBOf~;09jc2SE@9 z!{GSfTEVr0>jc*gP6)0STtB!$aKqq6!Ht7$!A*jj1~&_C9^4|hWpJzD*1`7RHo;5~ z1#yrBX^;hZPy}T#8{9UyU2yy04#6FRI|U~OqhK6Nf-0zkCO9cLIXERaH8?FeJ-Bml zMsQ|um*B3!S;5)CIl;NX-GcLiy9f6O?it)GxOZ@VaG&75!To}{U`Mbs*cHqN7X-V5 zJ;6e7VQ^8f80-!91^a^o!Tp1!;9zhlSPm`@E(tCTE(;zIJTQ1raCz|H;32_7gNFqV z4;~RbGI&(*=-@HIV}r*9j}M*@JTZ7u@Z{hr!Bc~$1y2v25j-<^R&Yh|?BF@UbA#sv z&ktS@yfAoC@Z#Vl!Apae1uqX?5xg>ZRq*QIHNk6x*9ET+-VnSocvJA^;4Q&hgSQ25 z58e^HGk90m_+Ie+;0M7EgC7My4t^5+ zH27KY^WYc3FN0qNzYcyA{5JSq@cZBo!5@P^1%D3y68tszTk!YbAHhF^e+7qwe+T~w z{x6sY{|&DZUNbx*bi#GvAY31A2#*XmhMU68;g)b~cvN_FcuaU~cwFd)Ug(EG7>2{} z`0!fcwZrR#*9}hyuNPiFyg_)w@J8W{!)@VB!kdOS3vV9YBD`gItMJz0_V700Oc;f6 zn1pGVg?U(nWjGt&HoRSU`|u9o9m6|?Cx)YN98SV2tivWeDLgqmB|J4eEj&HEb9hF0 zW_XwIuHjkX+2J|ix#8Wy^TNA__XzJ9-YdL!cz$@F@V?>w!ntrqxHH@p&W9I-yTd)< zLU>_#QMefH4flon!vo>{!=>5H$51$b}GkjKfMfmLSIpK4|=Y`J? zUl6`9d{Ow~@Fn3(!hLw;Ys1%tuMgi4zA=1L_~!5};akJEg>Mhv z5xz5gSNQJmJ>h%9_k~x6?+-r^elYw{_~Gy);YY)dg&z+;5q>iKpYVUfPlcZjKNEg7 z{9O3?@C)G=!!Lzj4!;t9HT+um_3#_vH^Xm*-wwYMemDGH`2Fw);Sa+fg+C5|68<#& zS@`qt7vV3%UxmL8e-r*T{9X9_@DJf1!#{<84*wGVHT+xn_wXO#Kf`~8hr@q|{|Wyu zoQD4$USoL8;Sob;xNbNYt{-j~9y#1N+%()g+%nucJZgCK@R;GT!{dhT&>Q;0U>FXE z!{dk78eV&No#AzdCk(GQy#DY8!y687G`#U}+wdmCn+|U_y!r4J!&?q-HN5q3`|viy znPD`HhsiJ_a2@kfTncufzw0Caj(*AuL=B!*l+Mzen=GV`4 z#fH)T-FiXmq78FaZl3I1SkjxF7c9F%IMJoudfYozAHA>tKZ%7>;Q zY}&ngxaB0JBG@};joq@lXKX%s?Yo=ztR1gAdB@z+x;^@5{V9hQ7I)3BSMCKB8&07o z3-sg^>&b!*-6^zr!O9~}IrWHz3pby-cHPYvt{rbVt=Et(7hT2JtrRBv7WHQA!p`-l z&+SxxkoAjQvE}ssd5b+`{pmJ9i(RqqboGASqW)QbhJAmZegBN!SGMfy8SBrm@9(n# z-Zyt(zjBl8*A3&;`*u6?`*v?Qlg{hM)VgUr(>lMuE4H4wM|oxDmX`M}>Q(Hm`}@}V zv#bqDcA>6pIjaY|WvOSZKg-&%WX0L`t%F^$`Rui!+k9~Cc*{Ax3v4;mGuEGDmpRlG z>&|JX^iVsc=h~Dm+mxP5ua@c6xz?-Yt{9xVq&LwA%k95g&+YeV>vG@Pa4u87Y~|+j z)>^pvlHTz>ddHWo9k0Kq_4NT=vFVo;Ld_O z+YHXs>;3vG*!Ev*!-zhQz#f4;0(%7Z2<#EqBd|wckH8**Jpy}F56;JqCLW_89Cj*kiEA^n1*BPZ;kB_!ICa;7`DxfIk6$0{#U23HTH6 zC*V)OpMXCBe*%65eg%F7eg%F7eg%F7eg%F7eg%F7eg%F7eg%F7eg%FFehq#Nehq#N zex0p9$)5V%U14`h4SEfF4SEfF4SEfF4SEfF19}5`19}5`1A0U68+zZ+`v&}GRv9A1 zB=(TctsZU|)m_zwx%sZ#eCEN$IlWxkvgPLeYsX!0^mCe&)|z<6S`*J$YvLJeO*~_* ziD#@e@r<=5p0U=%Gd3FWjEzP-W1|tz*l5HvHX8AajYd3UqY+2oN8m@`N8m@`N8m@` zN5z)2d-S~JV9&4~MYJTMB@r!&Xh}p%B3csDl9-mnv?Qh_F)fK{NlZ&(Mj~b;Vn!kc zKL$SrKL$SrKL$SmKLI}hKLI}hKLJ0jL(13&nBs3tQ z0VxegX+TN?QW}ubfRqNL3_!{Nq~NFEr{JgHr{JgHr{JgHXW(bxXW(bxXW(bxXW(bx zXW(bxXW(bxXW(bxXW(bx=iuky=iuky=iuky=iuky=iuky=iuky=iuky=iuky7vLA* z7vLA*7vLA*7vLA*7vLA*7vLA*7vLA*7vLA*;|9bf_$Bxy_$Bxy_$Bxy_$Bxy_$Bxy z_$Bxy_$Bxy__zTvZa_Q>KJGw_I}qaz#JB@7?m&z?5aSNSxC1fnK#V&O;||2Q1Mw{Q zBgTJ3|8WUoT!I*vAjTz#aS38vf*6+|#wCbx31VD=7?&W%C5T7#e+2#*eB6QUfZLNi<6mn zV;2zgYV7Gr5Yhx8O%T##rrf$mKM~!fF6@2Fo}mOeO_0+BIZcq$1UXHR(*!w9kkbS? zO_0+BIZcq$B(gC`B726D$e!T@5ls-$1QAUT(F74q5YYq?O%Tx}j<-mpy6mHCokB?y zlr%v}6O=STNfVSbK}i#oG(kxdlr%v}6O=STNfVSbK}i#oG(kxdlr%}~Mo1C{0U=Eg z(gYz*5Yhx8O%T!qAx#j{1R+fj(gYz*5Yhx8O%T!qAx#j{1R+fj(gYz*5Yhx8O%T!q zAx#j{1OZJD&;$WZ5XuChOc2NfVN4Ll1Yt}N#spzZ5XJ;yOc1OD!AcOU1i?xWtOUVI z5Ud2jN)V_7fl3gl1c6ErsHA8HhxYH=e{k!r1*OP5s5GaowQ00?K0=vWUch3-RGxhb*Vera^~KJcDccfb9*OmI&<&*ZaeAN1)Y8stU-sh zZajZzZgA3^mVfLimm-0;?wH71dlsCD{=dt?g)QxMR$6j2C#=|;=2iz_ z%Y3gVN2&qV?Y1A?k+*E0uY9$u9CY7bw?n57cDJ1x?3!OZG`E3kt$TnzaM}-*Fil-* zFE+TS>(XM^B^!hB*%XBa|_qB+2X^_*wlTwJG5pXDoHi7zH4rD*C;+GC;+GC;+GvK&I$pN&uOnlPNlxqLV2)nWB>^ zI+>!ADFI|k0GSd%rUZ~F0c1)5nG!&z1du5KWJ&;;5K&I$(iaw|4bBaEv=yOT{nG!&z1du5KWJ&;;5 zK&AwcDFI|k0GSd%rUZ~F0c1)5nG!&z1du5KWJ&;;5K&AwcDFI|k z0GSd%rUZ~F0c1)5nG!&z1du5KWJ&;;5K&AwcDFI|k0GSd%rUZ~F z0c1)5nG!&z1du5KWJ&;;5K&AwcDFI|k0GSd%rUZ~F0c1)5nG!&z z1du5KWJ&;;5TdSe@Z}^5>TcDlquSu z5>TcDlqmsaNxSpQ8UM`k$izDf*wH|0(*PqW>xSpQ8UM`k$izDf*wH z|0(*PqW>xSpQ8UM`k$izDf*wH|0(*PqW>xSpQ8UM`k$izDf*wH|0(*PqW>xSpQ8UM z`k$izDf*wG{~7w9q5m2BpP~O5`k$fy8Ty~0{~7w9q5m2BpP~O5`k$fy8Ty~0{~7w9 zq5m2BpP~O5`k$fy8Ty~0{~7w9q5m2BpP~O5`k$fy8Ty~0{~7w9q5m2BpP~O5`k$fG z89JSz(-}IQq0<>UouShiI-Q}@89JSz(-}IQq0<>UouShiI-Q}@89JSz(-}IQq0<>U zouShiI-Q}@89JSz(-}IQq0<>UouShiI-Q}@89JSz(-}IQq0<>UouSJax}2fQ8M>UI z%Ne?yq01S1oT0}VdYqxh8G4+d#~FH@p~o3|oT0}VdYqxh8G4+d#~FH@p~o3|oT0}V zdYqxh8G4+d#~FH@p~o3|oT0}VdYqxh8G4+d#~FH@p~o3|oT0}VdYqxh8G4+d#~FH@ zp~D$EoS`on`jVk98Tyi;FB$rhp)VQwlA$jd`jVk98Tyi;FB$rhp)VQwlA$jd`jVk9 z8Tyi;CmDK@p$8dykf8?|dXS+98G4YR2N`;hp$8dykf8?|dXS+98G4YR2N`;hp$8dy zkf8?|dXS+98G4YR2N`;hp$8dykf8?|JfFey89blC^BFv!!>>8~n!~F(yqd$OIeeJI zb2)sL!(%x-mcwH?JeI>_IdM!*9Fr5rOimn=6UXH6V-7#& z@M8`?=I~<PMnYvC*;HlIdMWxoRAYIPMnYvC*;HlIdMWxoRAYI4v**Xcn*)}@OTc7=kRzAkLU1s4v**Xcn*)}@OTc7=kRzAkLU1s z4o~OsbdKMjeBGk z1-eq8D+O^&LEKW%{(|-ww7(#3DTrGN`cu%Kg1DuiUj=bXLH`PLsz9d-bgDq73i@B5 zQw2Izpi>1pRiIM^I#r-k1v*urQw2Izpi>1pRiIM^I#r-k1v*urQw2Iz5T6vpCk63I zL3~mWpA^hn!MqjBTfw{)%v-^{70gqyGEc-KHGXUjf7I|t4R6%&L=8{W@IwtB)I8rc z&v(u9UGsd`Jl8ePb|&2wGzJl8zWHP3U+b6oTM);zB@&uh(dTJxON zJf}6!Y0Yz5^PJW^r!~)M&2w7woYp+2HP30yb6WF!);ymz&u7j3UvvN0-2XNAf6e`0 zbKlq8?=|;(&HY|;zt`OFHTQeX{atf^*WBMV_jk?xU2}ie+}}0#cg_7>bAQ*|&$Zpp z(Twq_%5L3RW%q{zD!V^iP}yz0s_Zs?Rd)M*D!X-mmEESN%5L3TWw-9FvRn67*{yr4 z?AE%ZT@7_f8(!c#^ysM?X~%kO?z!VWYb=o581TW=0i5^HNKHedu@JX(_Z5n*|gXA zCYmw6QAvA^Z)DS6;~Uw`m+_5k=F9wI+02*mkZk75ct|$$Wjqwk7!RqW|HebI>A&%i zZ2E8dA)EdiFUh9=#!Is4zpax*?1QMZ{;>}t+xo{oh-~X0`yjIIy6k_*w(GM0A=}24 z{SVnTuIztA?0={P*VaE|gKPf2Y;bM;L$-}8`yaAxT-pDS4X&+w$OhNeJtFo!RDx^q zf^2XtUXTs0#S5~*wRk}`xE3$Sw)te=L$=K)`yLVd9x81<+4qob^U1!4Y@1K^J7j}v zae{1cZ5>56xVDZW8(h<$i2V+g;95K(8(fPgWP@w*glxNy*zb_dd|Nyr8(fPcWP@vQ zM8rOaN^mWXkj*$-{2-fgwm3pI{k3%z+4R@eQDlQ_>nO6pwRMz;{SB4iTKplK{#yJY zoBmq-A)EeM{2`nETKplK``h9V+1zIqf5_%Ov-l%oe?ukrnXRYDrvJ8{BAfo(dWvlN zZ*hxk`fqWIZ2E6;i){LDaZAMhhD!Qxeur%OZ~ljD`fqWIZ2E8hh-~_Aeu-@QZ*hxk z`fqWIZ2E6;OT_+$O8Reci){LDevNGUZ+?wz`fvV?Z2E8hjcodF{*7$#Qug#@X=ZJH&hQkI?Mis>cK~6 z+22q-_~$B%U1dKbVn0JA^Np^upP_oj+u{P*jJNqovcWSyNjBqcev)j)+v0+V z{S1}#7oBE5L-q6*on}8n_4F5=Wvy^y__lr*Www5&5`0^~lg;?q`kidX&(`l`Gk)kY`x~mK|L8LN8>+Yami>*0eGQf1 zqQ~rOsGfeK$LwpU9$Z`BlWqLWzJ_eOZ`jw64KBLOzDC5phDzq!*7anAi!QUDp?Yx9 zW%f5zPk+&6_BT`yF1pPAMwHpQpi26UF0;>}dhpR@_Bm7!KDx|4hw8ycPuZV{*q>0z z_s~=JCse=kJ;n`z4OPpD+v&{Os&RL^~aUS{ZJhF)gqWrkj6=w*gp zX12~3W$0ywUS{ZJhF)gqWkx)ip>r8Jml029=v;=*WyF&iI+vky8S!L>&SmIaMm(9J za~V395l^y@5V3C%u@0@$#+`N9h;`YBby<}*|2ck9j$g!jtvXj+%n$3Zst2F>VI5ZW zj064*>#(W^AAg2**r>97X_bak5sy{yc||-{!RHn6SOuR~wlA&k8!uG0e(WbTvnk26*^E6msRk71@Bkzeg*GW@O}mFSMYuX?^p1C1>aZjeFfiF@O=f}SMYrW z-&gQ`1>aZ1VHJE|!S@w$SOwo##9Aufyk8NARXk4>ykEil z6>(Sv?^ncQ6}(@;`xWt6W&1i&MLbr){}udS!T%NfUlEs8@P7sWSHxu%{9nQU6}(@; z`xWt61@Bkzeg*GW#A6k_U%~qo@mK}#SMYvCJXXQ`75rYo?-lV;MO;+D#}&_6Mchyk zH&nz86>&oapH}c`1)o;%X$7BF@M#5~R`6*BpH}c`1)o;%X$7BF@M#5~R`6-X`dmfa zP!Ts&tj|@%4Ha=i#rj-D+)xoWRIJZc#0?d3L&f@BMch!aK3B0mR}m*vtiM&n2^Dcd zMVwF(C)Ds&4PVvpRSjR&@Kp_8)$mmfU)Atc4PVvpRSjR&@Kp_8)$mmfU)Atc4PVvp zRSjR&@Kwz^QVnm_@Kz0P)$mpgZ`JTt4R6)(Rt;~}@Kz0P)$mpgZ`JTtjsIHXzt-?r z4Ug6ESPhTW@K_Cx)$mvikJa#4jsIH1XEl6Q!)G;oR>NmCd{)C}HGEdXXEl6Q!)G;o zR>NmCd{)C}>}y0dd{)C}HGEdXXEl6Q!)G;oR>NmCd{)C}HGEdXXEl6Q!)G;oR>NmC zd{)C}HGEdXXEl6Q!)G;oR>NmCd{)C}HGEdXXEp0gHM~~CYc;%9!)rCXR>NyGyjH_& zHM~~CYc;%9!)rCXR>NyG>r6H4Of~CFHS0_@>r6H4Of~*gjXzc6Pu2LvHU3nMKUKq< zHN07~&Q#+U*YIeKUtGhdHGXkjH0#gn-Y8pti52H{ZSIS&iS!{#nHSnM(5u**}vF zKI=^Eqp2Qz^n!gf)q{^-)aV8KX%YKrDlLAj(F^v~R1ZFSQKJ_%dcl61&I2F4VBbyk z;G-ApyG87~sWg9$eK*!Drp6X5Fb~-HH9Wi2XX1;Ilu(ex2&UXMc$O zI@L4(><`tfJJqZ^v45xY%wMiqcdFSRs@Wf^S$C>gcdFSRs#$lcS$C@0AF5e*s#$lc z*&nJ|cdFSRs#$-k*&nJ|f2vu3s@Wf^n`r$#>_NP&E9_}wcc^Cdsb=-5W_PG&^{Hm{ zsb+VmX7#B--5b=sLERhFy+Pd@)V)F78`Ql)-5b=sLERhFy+Pd@)V)F78`Ql)-5b=s zLERhFy+Pd@)V)E?8`Qi(%^TFbLCqV~yg|Ji)Vo2w8`Qf&y&KfKLA@K)yFtAh)Vo2w z8`Qf&y&KfKLA@K)yFtAh)Vo2w8`Qf&y&KfKLA@K)yFtAh)Vo2w8`Qf&y&KfKLA@KI zkp}f{Q11ryZcy(A^=?q_2DNTb>jt%MQ0oS@Zcyt6b#74S26b*w=LU6dQ0E49Zcyh2 zRc=t@1{H2l-v;$DYS`6jSOsZVg=$y@X^46n z)V5()r$KES)V5(2q(N;P)V5)jszGfV)V4uw8`QQzZ5!0KVHKod6{JCJ8&*LY)V4uw z8&*LY)V4uw8=|uYwQW$_2DNR7&Kjb#2DNQa+lJ_@L2VnU(_1{G~k(FPT5P|*ezZBWq$6>U(_1{G~k(FPT5P|*ez zZBWsMU7Lnon+Dx%(8~tBY|zUFy=>6SW<@XAt!db;X>7MfiHo{)87jL~J(Zo8qO$W6 zRd!yQ%Fats*?DOyJ1YB>VOH|o;Nh&)pMP=uusq9wu;u+IXmC>dP z7IrT$&F@l{rPf7I6_8QBb$Q>e`6VSM(#Ja%x88qQ@2R)>5tk0m@8Vo87biz4Q=*b3 zC|BS7oW69_p-cAnOgq~|Qdyd4yosc28gC*go5q_+%BJxqlFIHBt>3$_uT2GbQ28tN z(H4_NeMhQFbrzOdMr1ewkeN7>+; zC@QNM<7c8MoAEPIl+FB^D9UF3OcZ4^e!^9OYn)%46{PgfeP+ohC>>+0pD{hYzQVS=YKxHnAjlp&42nc&H$ zZzg!M0XCtNOq5NHH} zMi6KOfkqIh{vNUGxBlLc(%PrLzr(ivN72lYr(Cc!chRBcCHuf4kT|jkM5WzS5i}av z`l9NK&6mt81&C6K&K*3+cWqE2i%N9P_{g)adhu$@0VNIDxwLTL(1P;Rwges7N@E;Z ztfJC}F0vR!HZ8CiMYi465%e2DzY+8sLBA378$rKOTy2~`cu4tO4?*`41RPnE5=R!L zsI&(wvM5D%s`MQ52TV{Rh&Y0XBZxS%7)58>V-;D9BAY3ImLq66f|m6sgEkJ(vi_tF z+ipSq$r-lYg8CCIY`dlOCrQ|hKD4Yq<*<6_SAUv-9Us{~V0#vpc5Rfbbq7o!46I#e zP6Yx6)~+q}qm8=6{KD=%hxTkav`0B@?fBs43l=V3Io@(m+PaSq_#Ss1j2ywp5sVzc z$l6`C^DV52wY!QL@h zOdi4H5lkMzH49>L@hOiq&5ID*L|m^^~XBbYpb$)j?1jx179$z5xaifo3^A{E(8;i%X`2X^h-ad6(|72b|4T+zv}mxU{`VK3M_g1sZy zJA%C<*gJx~qj7qal7%iU%#(~&R?lj0kyWF;OKkXU?J$lIs|c}*EZR{$kC{a~vY8%4EV8Ia^|Z^Po;b3o zMX8k;MLn{?x2Q)pw}(YNvY8iKJB%ay+j5o6i$y`Q znHO6y|^SJ?y4P4$F}Tmv@S8=T<5Vn_R9jxm;s% zxT>@rahz{j+`oHa=Ukird?SbZm$oeIQ+pKpv>*cw z9F9SZaL2?=F>zCjY{$rUjBLlqcAO*|m3C@r{sIMh)-qzK80n6Y?ilHgk?t7jjwRg( z7xpeJ&MonMyTM|lJ4U)=q&r5sW28GKqKeaWqXN3!OUtYCh+R56Mkqv$a)Rg9v?D0)mx6%$j%#8fdcRZL73XU*pM z`!6qCEPrU=nb#<{UEG8n0ZMiaziOk5-D%%Jy zCNfi&)=h0Ddik!4%jV)KQR`7wwRd^((87Vm%d9211LSMfm5l1h*D4#j!5tu9tLlwv zW88rlcOb?cAYW^&e63s6@dNYi4<9VAK36yeWNp=1j0gSzSzA>PAK?#>wN>>-xH0}f zj6V?L50JGrCTpuoBYd*9$~M9$YpZPVaRkWPs(SEo1jyQ|dhl@s$l5C6f)Q~9$l9v< zbc0rr)_503AjT0Oi>uBv!6S>SZ18ae$l|Jc@NGF$L}{S}SzKjvKiG1nZ2FJjlf_l_ zCb%(zA0zlNf*&LJF@hf>_+)+6_qhuYe6qfZu)#<0$@;2#6Z{y#j}iPB!6)mh&f_jb z@W}$JdiWT@j}iPB!H*IA7{Mn?tb~L9Blt0buk5k*6d?F9f*&LJF@hf>_%VVXBlxlX zvAP7w#y^>f27C4|&-F;-8m2G!lLJXg<2ui+LeQwVn`DSI?>?_~w zRxM+qr(&V5FJzsSIC53Aovg8{w=pDZtn6k}E5!0d77ra*J>0?7&~nnmN@{64K{#n* zRd07HX<;R_=7x}dRJP4L=|*MK3f8C+){+v!kObaI;GKjpB!PDlcqf5(5_l&e3`qz> z62g!K#z}0$M1s%gz&HsJNCM*|Fit`QlE63#PhP^4M_Nfa2=tFTnb+^+7}z-Q+E&bK zTPp1d;k7Nw;QXUqi5|BGZ#jXxbBW&#yGWHnHWBSfBKYMSFr4=~qvn39KRM z9gFq^rK(&#fi|#!q?at(6UyERw88#LO;AQWyk#LT$cUG0PqMov%&uJkUzFD@bONKm zA`-7zsNR@IuUWJ$$a@!{1vsM_&M5C$$p4}RIHP*cqP;+|b^&}*5=rR`v;bd}7cEpz z3zDt{dJkr0kXT3JO$(jK7~qWZ#)UjUrU7S^H!f7qEZ~ar#)azb*5Zu|*)#x`ls7I^ z&j{d>>WvHaQ15-}KOM*?wgbI+!$Efk=%b{YhxVM6ns(jOzSZ?lA9ZDW9VHO7kCJZs z+i$HNbfekUbn~JEWku%Xm5-GF(`urX>vR=e3s;(|9F2C4UMA#Vr9FDBlbV$)cin2g zt~XokBTYHy^zLAMSkh0)?PhZDVM#wFH=N0VA4xx@dL{=Sme)H}&*b34@_L8rjc-Xm zC7a2?hwai&9oVtBf9FNU3amKsdWXV*WP{dWdY^;O-ht9j4f>z*eB!6JFMljv(El8C z{M7d4kEKKQ`Qjn{?8&bALHF;5T^Cu|E&%iDhf1(ns>)cZ%J5AymZ~y*(+rKs(1;9; z$k2#vw%K^Wf>uU$?bv_m`jd6jtqe9I!8;_1eT;N@heWoKF7J@YX7ER|>rd92T$*%6 z*6ua7mtRuoYNLxv@Dho>$n4{nw#m8S=)d((e09 z;K?uTY!>-Q)}`~!Gay-)f;mJ2zm#NMs!#0uix*gXSfC<#mp+G^SfI)#v+FH*;B<3H zGS;QCN!<+$%jTr~QV0np;*FB>OZ5yp-Y6-*R9|dd*mv=cWsU8j_NWhuM@rf+ol6t& zNO@gH^^80oDX;G+jODq*BPH#Z>a$G>=Jpw=^$w?$%wIZ%n;)k%!zs;Jh01VBGn~>4 zr<4p~ikuk+oKiA`sh(lLDa~+7Gn~>4r!>PU&2UOHoYD-ZG{Y&)a7r_rQW03!pPa3w zoUNrCw=~Bs&2bROR3>h<$Y0Ar0ursMp6=rokZ3I?(V9xT{YkVY8+=x(NVKMUhM$$HoRzAal`0aj z={$y?m8zVTs+^UoJZlCA4@hzB=4Yo#Y1-E9TwLBkWjfe1KPNKXwJ@hm=Uvp&aa?U> zXxqGZ`Cz+j*)GZ+)jMAKiwlyqY2k}8UQ9n4)q{`vljKbcaNwifMdLBVM0A4OsJyU@Pz{`cIr~h~Wyj-YyaET5{?xqDt za1nlzyQv;rqC=9qsUBQ}pX6?;2bbuO$Fd zP$Fd>^Brpc>$FdPO1r%OD;RV560fiU*tyTer7wj_>P#C8RLB2e%r0tNhCz~2R-T|sD9u+LBs+7*O$1sq-w+7*O$1sq<$;RU-51sq<$ z;RW1Xu)9#OyHLQ{1-lCc++4uP1)N;4dr-j11)N;K$pxHTz`_Nq9tArF1*;wf`~3y0 z9tArH1yo$HgHW)8P_Tngu!B&*zXkkT5PcQ!Zvp=nL|+B`Tfn~s(N_Wg7VvKY{}x1F z1^ipUzXdx81^ipUzXdx81^ipUzXkjK1^ipUzXkjK1^ipUzXgA}RlvUm@mB%=7VvMu zK0yKh7VHxg>=P95Zvp=n>=P95ZvoF1L|O$rTfnmgkyb&ZRlv6ed|R*@SHQOgd|R*@ zSHQOgd|MD`74U5V-xjRK74U5V-xjRK74U5V-xkDK1w32ucK`)ETfnmg@l^rO7VvCA zd{waSU+@P21?&C=>;46Q08kKLk%UqkNXFYFZ`20TifuYluo_qpLKK7$Bx}@0lkq#r z7-gG_N-{>-rc)$il+E=C5lGIcdei5E5TPJMCLWF`4p&&%i@<-?Tl;}r^ew64( ziGGxX2qijFq9Y|bQlcXzAwo%rP@*d(x>BMmCAw0gDBMmCAw0g zDBMmCAw0gDBMmCAv})B9!P$iN2KRONqXe z=u3&dl;}%|zLe-oiN2KRONqXe=u3&dl;}%|zLe-oiN2KRONqXe=u3&dl;}%|zLe-o ziN2KRONqXe=u3&dl;}%|zLe-oiN2KROUZ6_iO!V72qk(`qBkXaQ=&H|F@ly{?EYjo zyF`adVuTVsD$%17Ju1qY^zT`3vEazeg+4rxJZC(Wer9D$%DBeJatX5`8Ms zrxJZC(Wer9D$%DBeJatX5`8MsrxJZC(Wer9D$%DBeJatX5`8MsrxJZC(Wer9D$%DB zeJatX5`8MsrxJZC(Wer9D$%DBeJatX5`8MsrxJZC(Wer9D$%DBeJatX5`8MsrxJZC z(Wer9Dv1wDRtQVtgA&~;(XA5QD$%VH-73+o65T4%trFcT(XA5QD$%VH-73+o65T4% ztrFcT(XA5QD$%VH-73+o65T4%t&%vQM88V(t3*2 zSt2aa|B^pcE7AXwCBhQ@FVX*!CBhQ@FVX)J{V&n~68$gH{}TP5MbBr^^I7zJ7X6+@ zA7;^qS$JU|na^3~bC&s>Wt?Vd_blxm;g^i?OGfx5Bm9XG z{=^7>VuU|2!k-x7PmJ&=Dr+rUyivi$K01= z?#nUv2ra#vLpZkGiWU4p6x8i;vDVgfQ=YAk5nd)tQlB8s^ZEJ+2WU_62 zl%!<(^DvA1NJ=K#;!cv1$p)XehvZ`VQE=KzoI-Lj)pI^^3dzM(PkV_|NG_&&+Dn{5 zaxv92Uc@OR7t^1w(Oz_jcQuEBDt9A!6!~3xtQv?Ux-skE~a|si#UZOW2)zV;W;JAnErf^`QkYx z$(ZW7UwOVr0;NC8v+Iz=Nq?Sa?IwwnZ2KNbn3R*Y>#xdgzEyU3D!cEg?B-i#_dS(e ze^qw#t+E?$m0f>TcH^zGdp=Zl{Z-kGx61DRRN1vtxp2WZKSDP6=10f|-~0&K;G174 z8+`K%WrJ^ip=|KYFO&_w`GxvTLGaBllnuW5g|fjnzfd;#<`>Ea-~2+^;G4fA8+`K% zWrJ^ip>qF%Z+@X{@Xarj4Zit>vcWgMP&W9cgR;Rl|4KIa<`>Ea-~7T6Nsm=_^RKeQ zSJ}tVN9?t#wDICctYq8xu(vAP#)luR8u6o5 zDy={KXq9a6Oy^}=fB4ZV+14L^v`RMZF`buf{b9dw#D1Yl&NDxC#QvX3+G&2KY@0Xs zsYdKmsWjZebXK-qk9{lIhReRyhw&UuUfFMw z&G?zl%BDZI9w?jsn9jTkIL1f+0~t)uY+%(K@}YwzpmWRkE&54pny3qOzNImEE)`*^_Ll*^r-BnO=WjmtL&ynWk)kAyT?XlH$5u5$3{t} zz&Dza4ZhKgZ19a{WP@++yln6-yps*S(U5HLjfP}{Z#1OjR^VIsCmVco^JIf>Zk}xL zjgDl~Z=)mG^xL*CWYcfkzK~77ZTmuh18nocZ@$Q8UW~3}Gww!LvKe;^3uJ?DbR`>n zqbu3q8(ql;-{?xoybM1#cT~xYY&;0>Wn2FV?`2#63BQ%}h|ifH!f(}EJI9Rsm~kiU zR+1!e7UR?G2=gG{KqC2 zO4yUWrJ_ald{1#w^26uwmc~t zd|RHB4ZbZ;Drp(`Ci}9%H`$lX^=w;5HrKOl9VIv8d9$!cHqX0-MY4G=EG&}Eb75hT zY@Q1Xi)7P23x||6js9D>Bb)wPxFehXTezd-YP8S73)x)H!V4uOv;LCXTDJ9<+}5%U zkKER>4UgQ`vJH>i*0ODU$Zaj#@X2j0+wjS4tt4pR!<#HisNTkr+`meKX1q@BU)d|4 z(>|U9a{lUb+QoA~&R^Br_>%Kiw($-*e`Oo*)I10LrlpdvF)lm@{I;d)ZGOr5E8FIm zoWHVde#!YO8+@Jva{j8Gap5^2=dY5z(Qlpua{j8Ge)AlV^H=rY^BnNonW|@Acn?Ip~TlSI-zAbyn2H%#wWP@+ZUb4ZrWiQ#_+p?FE`GIfCF0#RgUZdY()8g`B^t z2OoYR=dbD+e_M8u&HV?@kn>kb1i`mu7un#$L*)EbJ^1htIe%3TzAd}RrvJ9=BAfo( zvWsl`Z_6%9UI@M|yT}ILmR)2s{>?X{c(H~D$#&R~?NFti*KnS) z9j;$iCa#$oD5r`D+()0D`u@2SuwU^Vg(4bSxr|&h8G!LWO$L`MTQp{ zUSxQY;YEfQ8D3;~k>N##7a3k;c(DP+1{51m+yZIEzx_-_`$@!LUu<}>^)EKO*!mY6UTpn~4KKF-#fBGK z|6;?74KK0&Dd%I`+r;{p7+zv{iQy%Nml$4Rc!}X9hL;##Vt9$+C5ESWKU##;@KVD| z4Nq^db)OktYIv#PrG}RpUTS!$;iZO`8eVF6so|xDml6*H?t(3`HEWnIzxt!8zXZePZ0zn6BG)~|?})iqbY6=s%Q%$l27bM@A0 z_n9?UzY=DaUCi2gF}#W4>21Zfe-p!-7~aJ2CWfas7Q0gnuQI&K@G8Tr zIz0WZdwZsS*4>Kkn)*?9v$|{QC*95JuBjh%@2tAJrhd-dtnQlnF?X}NJ`{D=2mR5o zRd#*QpA4I2c(vixEuQ4Ly{3xxnkw3Bs%WpNqP>`k_F^j9i>YWYrlP%=iuPhExLDhG zS#9H0w2fEMHeUK~Kph#&GN6u(Wwni0VR#)G%QC!KN5--YuOnkwhS!m?EW_(q zSU)jfKpp+cvTJrsE6W<+F|90Xe8;r1tnnSw%2~0-cT6kGuGvwnENgW~v9b)Iqaaz< z&hAN-Yh&N*9#L6#X7^Cavi@}sr7UYpZXL_5ExC29u(lL7V})I_un{b*ErrckVF-m? zy0G3B)}+D^iuTfN<^?O-YszY`siM6LRJ4~?(Oz0bdubKzrB$>ygNhbGMT?-KMNrWq zsA&730-&}JZJq`z46joXRT^GLzp@OkV_{i_*C~lC!|Rkpmf>|uBFpePYL;bq9X0Eh z7YwgsXjz8WF|;hh>lj*=;dKlx%kVPmU&qj@wElGrEzA1XF?5^cq3d5a@3L$>JBF5J zS55FK83PslR7!ur?U8mhGZb+?8r z>tA7b-5sJz!|U!4S%z2GcygoYr)=8!D-Ex-@hlCmG`!OKR~lYv{VNTxG`!NrvoyTY z=C3ro((p>_Uuk%=hBs??vxYZoc%5?T*D~z0PPt_9nYE=;E>&7vI^~jOZRwOtmbIl* zE?L%=PPt@RTRN@M&v6)fr&Y2Hq0=f^hR|u1EJNtDN|qsXS|!U6I<1mr2%S3VhdvCU zQzu!5(5aIwL+I2=mLYWNB+C#wb&_QWojS<^!N#+bCH<0!0d=w@%YZ7IxXOSko48K0 zbc#(}Wk8)|snUQt$&zJ2on+~^I=Wdg-&VVAmmem27@7-<>wD*~Q5xy%r z-qjj?_XWL#-d5@zbSt_(=nZtUx<2UrbF;cW=~ECCl(S<&tH1opQ-Cye^E;&vO`F zCt$J+uZtgKS;sm#lVu(2R85w3tWz~vhF}A!_sF{*8QteLta^#uDhd)z)VAfx--=w7dl+h;a{UErW!7O@#ila0F;Pd}tIs{8jHSt;T3{K18TgV9c< zs~?=Ru&9h4CoRogJilq8Jafx?+kAI}Nqg>=cAHYS()OKm2j>UlHqZXXYX70Roja8a zam&fe`*zPQE$>~NTRyb8+P}PGu}wUs%gim!?O51(Uu8*Cp3?3x*73m=BWaDJ_@p!L zJGWwXC*F5%<0M05+Qnq7U9713r&ayas@`d3zXvkbt|nvc zYGvHJnvC9QasRZee_GZ*E$f{&?vICzwX4Z!L02Yd_uK{d-7&Ycq29N9yON9@-ECy& z<#tWf;{3sbX3thD_U7&ml58n)8pqnnohdoi%gw9x?Kx5J{F&bQEA{PnSL(HZah2~* zIKQo5x&B1QMX!G4I{ICORjl@S<(lnr!uevhs~oRv1!NB zT$fvB|DnOD?O@s6GHWvuXk@fnYIpT^Wz64wQ2FvXXWXAP8NKP~&)TH_OvqR}O~%@- zGHbWWU~#Pxl>r>vPET5t+m$(CzaHKLd#IURt2uh1%@#X<(aMyzQ>=`%SDA9zv;EH_ z{S@R??a6q>c6RODKRBgrisd@qzC+~FjiLVJNlx0hcWiU2x*2`etFL>?s^YX|+)X8Wh?>8B})B5*Y-M`;t^iJ#FZ~7yxUKjfJoBn8PjT~h3PV3)qb^m^o(L2rVw~c4X zf0)~`|Kg7O+6+T2CAM6qvF#_TqpvJ8{Y&?roA#65X}yOimpjwDTJMQcuA2VUde2S! zN$+Y`8J*s9lh5{^C}q0oT}{RssEoC%je1W{Hoc`5%4@CP(?`plxr`g1CycUpf=NBudK(L1d_r=$Ly%IKZe zpDa0Ey>aVL*0?_lGS*I$v39j_e->o)PV3LYxPQmX=$+PMA|(y#O_q$_X+4U`$NgE5 z(L1d_3*-JQ$mpHcpM`P%F_Y0dttV;uxIerydZ+b=ciewuWb{t6N9JgL;!M9^(FK&p z-ha9MEg$9VTB&K7N-wyS_OXmE>}xNyz0J3Mw34;&THbj`M0W7N;@oAOC#v+LEB3Jm zm-p&CU2&!6n3ZhED;4&Ycycvc&T9XXam$rGh`70`Ev~-G=i=+D+VR!wi6}k?bSZNYJc_W?bSZNYJc@A?o~h6ia@`=dNucI z_SM=OrC0kryPBO}%`UEHE7<7GhhFKunmxLjtzhFS=dbqvJx4tB#Q*+#>Kyi*e{FOA z89V>D!uk7A&fliaU#HGrwmE-3%K6h3&L6iqe|Y?8@Q11M`zxH^UE%!pu=AV4&abD= zuWsx7a_szK>ij%*e)iL|2S2;Q`KeC)>DkUtetet3PYydjzK!#vsq@3B^Mly=zUO@J z3g^31=R3za-@e@W)|JjTr_R@P`qwXazV_9V24B0}`RYl|SH67h!B?ivm#^)7Y3h7& z>U?49e17VD?h5C#pE+Uh*{SoH6P!=S&Znl%Cm(g};FH&JJ`p${pE@6#Iv<@nADKEI zo;n|zIv<=mADBAtpE_4go%bE2L6-n7ek<5A8VUcYVdhF#9^n!Y$4Vo`3w{1-qQ*Kkt~q^N)9)cZ~DgP0n+s&apX1gJapM6$zPIaAAuDf+`$}!H#Tb+}pPIHA*U*S|!XL7`?2b06j_)2GVnsegRxzp6S;~kD0 z+|hIHaQmYNcR0?u{q436Zhzu`k9Kai)w%7|nVmXi;SI{ePLUroC_E?6HV^V+oNTj` zp5P=~ojBS&h^J1p*_pY`=E2NX=Qf+2?YG`I*nW(2>y6H>V&|5(*fzN3F6S0EKW=b~ zZO+Y)b8dFiZG)SQoSSZQZnACj;3h{q+crBlo;o+0IyXGpxq-8Oq((|0bP%K$v2hU* zl?z-%K`$aA0&2j9hyqqX)RCX^5bj@*o|L>)f5Y}l?qd!)TtCE{n^ z5km(Jah#+(iPSuT-g!9S31bk=o)fq8n;uPtg)8p`cI(~kA)X?!`+q^LPb|Q^w zPe{6Z^td|)B|j3JPNd~X-~7;+K~qZHxzbc39jJrnBVz}R8T#eMhpxeQmVR2vxCs-_ zO*;Q1kt+8SGIwXm_@O1M`rg}`$ahX5M4p9b828AbYaf4yg-2hhuU!e*FAl&;vKqcX zd_)ws5-L0;ybSCW;T7Po3I~C|CcF;(4dHFzhlO{59}$iMKPDUp{=RS;_-DeGz$=CC zfS;A`CRCm$FCwD6SUpLodP+S-MDjeLX-Z#SUdflze@70z0Jf<7>Z|u-hVWbI76V+M^MMUuvh8Z(MS;`Fjw*49-dW>6d?CjMKH z*NqC7&@xCB)d{8Ts002;qT=~>q%-MG`jA^mF&RcilksE26 zqqO8PcvdH-dE7_NI*pfwG>P~X8W1lK`65gI&-f+@TrmsVguM{lZ-j4%CY%w@5Eqv@ zH1cS@@JB`JSLM&aC{A5JzyhXE8tnc zE&!e;<$yN<==bD(zzM+TfUf{&0T%$j#043xNmYOw5Co(F>Hx9%ly*vIrMuEcxm77vhAE?! z@yZlsrZP`itgKWXRMskwE1Q+4l^x1%WxsMzIi$R&e5jmKDwJ=ObIL`f+ANsOW{25p zPBKT#ndTgGp1GyDjk&$Ki@B${uerZ@uz7@eta+k&s(H5g9`jQ3D)VadI`ane7V~!V zPV*k~E9TeDht0>#ADd5`E6r!j=gpVQx<#^BEt?%7W*u!EZ=GVD z34ES)v2`WF2d!(Zk0aa+cp9(+up6)+aL{_l`X0g$0jI1L)^8A=vtG1T+XS20=CFAQ zu_XZ_woF@&Ezj1{*2dP}*2UJ-*4NhGHrO`8Hr6%~zfZNzw%ub}YFlMnZChvCVB2Eb zZrf?wV|&H+y6v#-nC)ZRXa*%Db+1~kzNsEj-&aqlpQ~S~XVnYp zFLq)#*;TvS9<-;~>)5mHx%TGv0((1qXM1;hAN#HLV*4=rX#05k6#Gp3Jo{q%O8bNM zwf4vDo9$2Aci4B^_uCKJ582_aX zXya(_=;G+<=7vC6U9vCgr(v#GPSv(VYm+0EI@S>zn(9O4}5EOC}P zr#t657dn?a?{}_ou6J&9ZgoEE+~wTsEO)-?JmP%cdBXX*^DF0B=LP348qrLes=2kG zmZsIwvb9{TxmKXH(>iP2wLaRdTCp}v8?BAkrf4&@d9ZU910WyTgMhUF$cP3R(I6ul zWJKGo?bi+>JOqG@Xpj-@6rchCInmAmE&{3vaR~r3zyW~FxF9pG2p|)X1IPok1hfIP z2Xq1S1oQ>;2Mh*`0E`7do?KG_vjO)2mI77*Rs+@nHUPE&wgYy$_PAbgz3w^;{21V4 z*J)R!>x}EX>yk@%OKz)MbNk&X?x;J<-N@b4-P&E~?&$93?&U6W4|ET4k93!~OWo7m zbKMKw%iZ_8*SOcaH@dgFpLOqY?{$~E-*g{wzwbWb{@neQ`>gwd`xg)Km^`Y-?Fo9) zJas(To?K6JPl2bMr?aQKr;q1WPqAm1XS8R$XNqU0XP#%VXQk&s&sxvpp3R=8Jv%(R zJ^MWeJ%>E+c|P=<@>F=f@tpHq^i+EVui5MHdc8^Bh&R)lRshs?Oo^H;N9Zg?%nC#<9)^Zy7#d6nD=AvX>X_3iTQ^_BbH^d0fN?>pi9-1n95tnY&F7eDcv{HovW5Bk&mb^O`>Tz_+a zfxn%p`s+ez8BiD#^aZ9{7sI($#DAVuW9Ia z9B-xfB2*&s4(9)wMy$(lses4BwgEj4@q8ZQ_zaHE;5erIUvV}4!bdE<@DWc> z`Lo<&7PoU8x&Y~uI1b%|a_2BynnUEP zc|3Fv@;S=m|Hk8&@c1Phzn;ZYUKe2_kKf3663^#HmWIwSHK|QQCor6R^1C#=Tq7T; z6U#^H#Pc_3;Q0t!dH!2@{CS4c87v>+F`hocah^U-PdlEz9j9SBr@tY8_EjGLD#r(M zd|>P{>NtbpqEVN4d}11&e@C9aBa0WQG6r<+XE{szc}yOQ5oZ`ai#Ku;xAB;Fc>Zi2 zN)ziMCd!z>e}ixHd0BA|u+rr&EKeCD2ui$`{VH9{aGF@VgciA~HBWMScwZ2Bo<<)L zJ~QY{pw`B58?TYk7xH;bek=)P#d`sZG2}L$8<*a=ta8qaaarZht~7jJ_fO>g;DEv5 zL^(8g>*BTE#Of<1^u$Ez5{;h1`6jGjISW{^fIpn$!#U;g-pyMrF0l-c^Td$k1nG+N zkoQp9meF9;)ac6wuS7$iB=);Ros*zXgoHkls4GM_=YyM5Tf}S1`(0xG20!C6oY-gM zGRbRH&7Xz+fm$c#Q^MlK5+2VbK`{DqXP&+@k2hLB-lI966MMJ86W;3$N`(IzFW@3<64DN9`Ct4-l(0S8R9sLze?Ni z9&K=v-p}%+_j7tiG8*U;JkPeAo+&&&u7y}WQjn#Wf-F8!Bh}(OO{+ZJp$Bp=3%Zh74PQ%qWdzKcn z^ujcr&!Zf_%<;<{f0W~KZOQVaQ&?H#1ka!IHje*E8hVK5GhXkBXXCnvKbxp&U*Kt8 z;AJJoC*b0{ERBfON>KYMZx=&LU*OMPh+%z1h-+Y;|DWLz&Z`*Dhu1~8li@VRmt;@Wc^K_=&|!6Iq`098Y8L`7!pa z#78K4j??fv+DuzmdK0XB<{LQ6ClQwxvuDM#EMEQ#OCw)kc%rP*RkDWCSJ4-dGKZ(n zVR?#nmR_`T{1lHjWGS&O306&Fe-!zsQLFit2FNFi=iuT#*L!jyvv$NoOt(u`j&uDi za@#4+;Xyq8eC8V#h{SChk++wW#(#a0|60VT6$W#k>r%T6IBwVj+!7F0@tDcX zmz*3Q#C-86_fvR`(K{B&pCM<1l6zQuyj-5rsD)8hA})<*R7nm_;U(@H^dvJpp3=i} z<8@E8e`Gu3hRiu{)JWtbtdJo2Qkc_e*an==xYkXyc@ps{k_WZmTFbD1D3`{>R!)>a zF>zc-Xz@flMBv&?Fzl{*oMZEtv;nI^lehuTTu!c!^+* zc>ETQx8wP=<9LaD1nEnJwea(Jd>)VIG*h0xkU&3uiQ}RRdC#p;YBP)!z?k=U;%^ZHKa^}WDkhF;+9%XPKzk=_@aiQDZQH{!W%5FaCW z>2f8fhqtV$5y!bsmJKax=vzKU$b5{D+Hjd^!+9PbJ6L@ab*V9@klS%S7&9+!C5m>I zzcIQAk1|}~R*g82<3nQDcN4p_{DnvOvwspVBOTG{V01@;IkE>Ezc)$Eh0g#&@O0e zse-33;<8=D)2oagNoBa`<#c)(F8^7(#QPDa;S@`6SeqiBA<~09-WX?%x%o6+UvAmb z_LA*N`f)t|$zRipbs7F=`4|?6$k%YLvP=@zRpRm7(!NUO?~vaEm$)t!w#rx)5w>z2 z6}R3wPZn`{jPdsghD*j8&ZB${hszTERJjTCf6DoDn5P-W)>tSXALBl^A>(rpKHeq% zYODe!&U(a`dAXcF*fnNyYm7nh{>WzBSB+DN{j?94=RTbNdpLd%!xL=)VVBejIqc%` zhU^(SEn&tdg?L>;JfDp`A2vG^$S6{Wu=R!ZWB^Gb_rNtJ3*nlPWpK^0YrdMaz&_y` z(vqx$YlR)x&7?JUTz8Xeuow6>DIh<>wV`%uCvB;h`bZ(IPwSI*^g8VKT~9mHE~EqP zM!S*Dv?uLFZlE{QzN8y1qWwsBT1*F#p7c)a_x7S==~&W-me3M%Go3&ukiK*hb^~vr z)9G|_8{J4Zk^Xcm-AV@1U33?@omS84c-(+LD#<}j1k6=L&77%Bjj!2g76bLEL;+PCP##r5F_u2jl@Res5nj> zM~;biiQ~y}ahf=dd?3z*eej_;Tbx5a67Lr8CZCA&#rfofxKLa~PKry!CFE0axwxF1 z79S8FB%g_^#fQlk;yQ61sS?+VkCCs$C&VYnH{xH#zmjjozlnb%XT&GPC&_o>)8fb}fbcR}`@1^rpm41?Rs>w`zlVnk@N0a6Hazk1ux0l<~4ss{C z6YVJXmHX08azA+h?JQ51r_&ovnWlQQyJ?j+QF={_`2rW^|)Qxnyx<%bV zx8NMaLi(KjQTt}P2Rl>G(1Vad3qOxR_(=>OKZ6nCr!Ug@xr-=2anX?PVK?J@*w^qq z>;eKi_CMTfCKPrbg$+y>16BeaL~n=nL>~ui20RUbZBKUt_5%(A4gsKV=!bw)fC|7j z05;ZQ{z|LS9|eFJfYS_u7r=UX1ds{H0ptO2jzGXzD6|K30rUi5&q#o+A`AwM0E`7p z1WX0Y2B7~7O99w3V)F-K9bf}s3t&57CtwfYmD-(bV?X=9?dwEDt*{G@)zMm?X&W8h zzwhJJ%YT@|KT|r>hqW{(;FRH4P6J%Q6FG^~0LHWbnJ@U-u#+&F*X<|uNgiX_|C^5v z3nn%ywh|EjZ&46KFGK!o-~-qvcESib{!|R^*Y8(jpll6(XV2PsYVPy1EbMD0$1)S2 zNz6Mj{(r--evW;+I_7_S{$Kw+_B_c1{OxwuV~*|cOtfs;|a21Jj0*+6JGtA;y=fj6vhVqxh($YCGeUsTa7@b-{7I)LkgHA z#N*fuApVJQJO+Jld}2r8PQ+sq($)U{XJM=#cx#VMLK)1O{e#aRN#fPXbf4}c#PC@z zu^IpSGgs&EuYH3&b^Rlm`mGH8Yq?&XTE7%qsXqnT<*jn{Z|oau5u;W9=(pG#hBJQq zZ94r@Jbn%85!Zab&*i@t!};_)la<)Zr12k>6W2727Y$<$aD}@Q4 z+BU{hCjifbEC02Rz9u#bkXK7XoYPmz<8(9H z_iGkc)#KM_l*To1tu9OaWaMYyX(m znDdY0|Gn7%3mXoj2Ddm_JN!puZ2|NyMq>C}Q`C-gwKe;#eCn08zx=adO#Ek4)~5Si z8LhB-$=siQ{`E2vvtr}0eiS36`!^#rPxLR3Hv+C-;m|X7x9Y$Nq%NGuXSfon2Pcy( zI1|f^3fHk_F>gtL%Fa8}rGjfsskfm5-fkVot!AI^dG-KNBe_1$L3yE&W->$@$8 z8|%9*iHGyYi}l^sxG8lFoFD7E1tfqq;%l)&aRXcsH*&g@5a|V%1R8I~os)jJp_4*x zgG(j-;nK($xGE<)~uQ_1~UXSVZoW;=Np>&zbV zC|rQ7hfBx3lxI=a4%|&i;w#TdWFK4_*$)>bFTmN(%a zf|JN$I0-jej(~gb!O7$VoQa%IkwPo~MhBQ!!>G)klJGifHNOLJ*1ZuGULtx15krEQ5M%n)XfOl6ibi%?~O zvHl=+_-Di8GL7L>?Pgce541Q zpSQG!w{!rv(fUFHZh`ZXBDfIRz8_lUR=8x|_9?vWQ+eB`^0t?G+o$oiH}RIX@Rqjl zmR5O7Yq-nxIArAsI2YMKHUVcl4H5D;I5*h>mrkC9^Wdh^Q^0q?Magq;9XO&sGP&HC$Qy8OE(mr{EMWRSND*eGa?=PN5FmZBwX|I!Pwg zs0Q3cUBKPc4ctRLz?qy`s1N%n^|+h`Xo!YL7MHUCmoqC(r|BffrOiq+Xa?{)v<|6H z>(aW!%jM5T>(P3^8_))Lwjpf@zcFnLpUI_4o6sgCMDuArVw%xr@SEe7RyJ)xTaX5{ zC2dJ^Xe-(ZIFn$F{+0d}w6Wb6g+4`}0{%2s#ud7aZUcUToicI&4*;0Y$h9U)uaN)g%;orlLNTZP!H`a?eST{HBE z7U&7B&;zc4207bun2&mO@Xhf_}Q6Jb?R# z4?#z*fy}NYk3d5`3eEH^Wbipi-}8{a-H@~Wkg``GR|g?OZ$fGgL({wm4RacrB?g@$ zLYH` zZin7If%AK$b30wv4LH9|x`y8-oy%{N&gHjB*YMk#DA$x$v9vcG-&ih((;Z$6NZvDcib_igtQ+r z`p)5`3q0&Na=!>Skw=q(+#fQ5r60+;sBquPeRwGE*PPimW{Ger;0qi#VcZBpS7ni0 zNLeBDXiwHix=JTFvrMS>=F+r8tKLD@$h3Kl*}mC zi~N~G4#Gs?9^@ln9PLJK1oi`*AY=>KxTk!(fScERe@(_M((g!;I8Yo&(!`f>7Bwus zD!xi0d}l3PeqMf_)HB^|x*7ElKtJ0V7WsG}@>!b5bEkY%5Yby! zilh>^dqr{vPQ*Rbv+&QsiM-B~oQD%}H}wMii*O>j1SgWqR3=GM z$RgLpBGbTVXua+xr0whLS)D3=K)@myLFVs8^tX5lfcKXB?0PN=4w*k->jgacsrv&>#`+CGYmhik1e_o|!JP!f3kkvjFw=k#SR`yK$EbLUzwok_! z04TAd7e0;q+}nk}3p<48gk8c5*r^pjosIlRf40vR((QNK=i?*;`*x3g0iLn(KKFxQ zL(AQcR(e@{Rel~ll=XGd{x|y;`;$l|k~_#8cDod3S{5Kr=qov2{tLdQ8KL+A878=) zB1~pcHpzk@eMwBQUgYP?Rtdbpr{}N^Lx4qMF_TyK(lyH6^owFb9{uzK_A5S22Z{i8 z(ipl?-h*=SanwO9!yxVPcuB@;EiFc*0{ukDOyORXyBzj~Q^Os?H->Ks_Y2<^9uS@p-VrVj9}RyJ{yuymtVc2< zts`wB?IJfsdPI6hZix(u+!a|KSsU3H*&5jvc_#8)WOwAHbTQqM?o9WmN7L)2H%QM* zZ=c>Zy?grn^hHq;m7$PhAY8Lo^_MtVlw zjI4|q8Bb@tQzrwPkp-EZGKXi5tM^#FgY{0=`?~(D`rl?dvKwcQYLJvOC}&8{gq+Da zvl`ZI6l@f3RJT#1jh!~$wDFdWf7#^U6y9{*rcRr>ZtAh=&P~%dE!ebd)4iLvY>HjT zypVPAz{Phj9=mMPov|8>hwKv}>v3KJ@2CKW&vFivIERbH5^<_H4;)^O6Da4T!O}>n zL|Q7Xl-5aGq}|ef={4ys=^a^k+hp*!t?wP(QePvXLnxeL-Ga86yL|aDNfx|tc#nBb_~jrfHjIZ(6cxB{+NuLq{!#iB4iaGY-eTh`kcqAKM#yDfVJ) zcWhVeIqcVMi9H&72)k%=W3yw^W7A?&V`bQZxD)#gcf@+cZj5z}-4N>(yFPYZtZnSt zSiM+AOp7_N=cZmP`tj0_XRE%gI$d?D>Z7Xis{K`qtL~|qS2ee4cGb+P8CBD(rd3U? zno>2nsg z{3K?aCE}lF;YR>C_r&%2dW;Hh1IHeja0GyrKmqz&cpq>IfSHJJ8t^sX5x|)^Y(jVz za1O8;@M9dFMEH+5Y&D7y+u}R+mBi}}fE7x_9tN;H#3BQP?-3RQ1|dE26-xk z6)+3_C`&?FBI++8PiYzOPY|vIJPiCKLY%ddP;U{ukhTDx2abA6y8$l(C}2OJ901~^ z*8r~rK##=AI|RVkDY5c?2B;WEH2`)Dq!tL10I9&QMc4#@vtZJ72(cR{gMMi^!a;xq zz{et71OOkT2QUtU#|)&0aE@p_-~e!(57UkSs)6q%SULiX0{$&R)HQVqp1%d*G{7w2 zC?|Ek0rEhEsCVi<#NUn(bxOrru?*g)A)mC%z)^M>co_7XFarq(0Vu?@7GVU?0r=wx zI|FV64qk+N0#K&uFhZ2azyw(f-)4a61B3$%Fnx#+Wrt@V%|{4l19kwvgb?T4!k|fM zi0~i)?WPnUWO;o8ybHpU0LYZW%7hGsF95#-;YENBe3;P{BALL)0nY}s2L2#IJ}DilBZBlot?!{>TvEkdeqpz+J%iBb*3; zyei;HWF-JRQ@%jB(E#Nf!mS1ivKIkABF_Ow-6FdHC`Up4A}<-B z{7gtXWRZdL3qp$l3feE-0dOL|8lm3+1$;@58lc1wf;Z{)m}!p?{7G*BTtJv-fLTHa z8BGUY%rZjogn^lr)7=2G6(QOn9kOM%AzWmDnLUTPqc~y)kE6&piu}xfM`!{d4>R}~ z#WPX#6*KA<1-((on|Tky03ZVVRfHJ;$b$K8ge?Kr0*CxWS-#f;KZOu-$iRFWA?S(T z4E!^M#ehM;s}Q2CqCA+c7*8octVU__F)FT7(&cIR!;lqHZfwQ#l7+`5dNFAhO zU};UT@Wqb40lx+z>YiBu6CIG*7SIX!bqI?A!-01}cqd>S@Lq(}d&~f9KZFMW2N8cO z!qWy=`y>1c0Df2pAUsD%{aL^v*Y&{{2Da}B$wvERL!NB-J-e|1Hppi-WIlTo;vt_6 zKx2a>;E>N8(33L=@52F)a_#_t@2UqOXv~=a9K6k$1egpQ{K+W;pj;J|MKhCz2-O?{ z%gz8c`)L$5K<$kX&op9b`XFp%fLerCU9|^*Co1Z@5i+pxCg7;oMx@(_KC7Z$8&?DV z0vvR0LYr@Bk58%ja(+B|g zuA)6RO#@5^{s_X^fCa!GMYsg84EQF5D*^Wc--0j>>Q>;F05QZrWxRsx0_aem0}lCN zpq?P);sFEjRyuO=T?6oLI&$%t0eC|l79;^Cq(6;aE8w~lcol5jp8++%|3N5{(_{I- zs|kG{XQAK!j1YS+p&z^ncnkPQgogni03VI_j#-frq5O|wS3Z;oalAIX^3!h+Jd6)p z#gsAsXDfRa%qne|?b@+-i9SU2vCchcRA zose6wO2+oS*b4FhGLYO(ipe0*e-BxJ733vYIbMyG;wP}Pu>tQ``zzUmy^JTZlfiZ{ zwvngFcJd7QJ64c)@Ewfjv1joj!JdV(f{Z6a$WU^paxdO^_ZTT5qo9jR$w+9Zg=CHS zO)|m!wfP$|oJ=N12 zmE>NsoUeovtcbH!@Q2Bd!a`vUxl4FZcnGJ_rwR`Xe-Rde0|F7~H0HwYi~pyIf6e!QtZxFyBD5N4r;Y|t3Z3w=&;yro2);I=!k_vDZ? zXe;7IHW$vgTe;k=kY&Y%>O;AOmSR`2R_rKtN_0Torp@#7{64QKBbw<+7kwGcs5Gv7 z_mYzC-Ny-Z^5i9tGrUJhi9EM@=Uy(J0(n*3B91`02JJSPO6aVTi3n!x*ok9@P|$^f zLwUK>l>zEATy*<)!e76WkJlU*8Zh9Tz&K#4FYiSjVbZ5CKh+|WfX68$+lV5C?1Ik~ zPD@En3V<{!*n%FH=CG?4v%(sbIM}=NBHFP-<*q<0{52ts3(E8IjxkR=Y(Or>Vwa7k zd-BC}A5cIBDhOzn&OsvjrH<6xpqQEmog74c`pynCMc-b0vRJ=V{AscNHSOF{|C079 z7Dwo30kh~Feb#aP><9WBI*Y*v)O{S?7&X|bAD6030chj~SYI7cc#RTjkSBTRWZ648 zNOI)TNVslrlEb6fqv4W#Im_g;dTfo&DLLVw=(MsiCCgkl-7aDSF<-k-9>_z>YkHvI z*x3tb;|<3uSAHf>J9swlpjM$(pw_u9T={AA8tQ69n>B5YFRnBiaAh>2^;{BbQ3b4T zM6vwu^ZEmxHb|6N@6{=;M^-w1{_v+0WhHM9D%1Wlo!URH`KD~_=d|a|cienO+0YS# zMqfiS?wLm)XtDQ+9q*W^UH{S4pq_qi;v7L=dd+J)UU=K2pNFc*krvtdT-&j4N{z#I zOP4OiFd8sjB28jnC?n)^kV%{$AoZ{}c}rnVQgSdk7)lCdXU#}4&2W<$YNHt`(WH`~ zI<08~bKNH9P}pL2hDCF-uc>yd{0usPR(|&VvrJ_3E)X!08j`bBBf zgqr2&HD?v8m0@rqJ)dUKgztfbbaZ!N%a(=LwQPCazPH}mxA(2Lr0!d*Z`~?gv+vC} z+1I_-wQR+HmX|EqxM}IqO&gaCefPkD_ue~j;N72p6dS%k!3x`7G$bu)QVGi{)Y?zbRM99zeMA9QIS_+Ll>FJT|#yRPt&^)(}E#k|n zQ<~Xa_JwA8UDFzt$|ad3H$Rf%Pp@0YTdzK`eG4-nh_yP9GOtofokCrnZcSM;1AF?v_(d7j!5mYwv%cGqoP4sWSxU-MRrmd7`A zzwhpzJt;Z;L{;zWr?%R#GZ@fcpFY00SO1+}i=Nj0*}JApS@roXAB+~dUH?$;zkS?< zwH0N$i*Dz-%e9lTkxr1HNSMkx#1$5lm7%GS_GSHLaiGZyyDYWPf%hCMrKZ^q6R~k`b>gLa|gtM`Zj$Bwzh6ww`9pW{pA_-J$ftLp?On^^#%GS zeY3uxnA0)>`I~C@rOIZ}N*&YOX4NWM zOfH-Nk_FR1AVaoG2(~MHiF12(&Hi>hwaVNtB31y4z|YGIo)yOug{2u=+%~t5vJqKpSjJru)2t zl00HCO?_eifj&nT_cfo?UmnBTXdGQ9d?8@hk!a81DjYW9v=dBRjvdPNU|b$2OsrWd zY}I*PHex)^pv9<5%N=$MzNkQTdDrdEn~HW=G4@LZ1zJHtzb0e{AE>npXN=!uD%up| zx7-#96lQac8-x{+8rytROhy}?3cIzbXdM2d~ewf($WOpKub1;?qHA5O~0 ze;xz=f3d9lz#oDFwl?dub?kCz&>yr{gtAR>GO2QsgL!}`VAjX!rtv-MHdR9%~z}rzEH* z>AQYW*5-0EW()anTqvl&fkLjKp)zULt_ME{s2YO#5} zc*mz|!$vC!O%mj3>U@oT^#{y$x6?(e)RcniW5hXj24ym(dFBGB&>$VII0^C_hLU81 zPKK)u&5uZIe5L8l@*#>X>bNlifnC%wW$O1+X)0~I{gPfRyt?S}_LDSu&x%E*%cUW- zUER80AG!bA$Mo;F>s>DVH_HlsjY;tp9LrXZ-6G$x$2PV0l^)n|C zahkIv+#r&gk!DFU)whzgjDRUApn9#u?Q39+0+2F|8467ELo~}Pcm)n(#6&IJt#UE= z$4O3uF(9Q!GwU(a!5BIl&}IgL8Ymgt=Upm=T29}%{?+~ZbNa#3%ZtivYZtHH_|&8A zn%;N+lDVae9cA*CjEt9eEZCeHKJ?7HpJrvz&dVS8+ry7R+F`pr6airXBBFdEm|04zz72e8G4)2Xc`C9)>W} zZ(f*2B9p|VNfr;2jA@pVOt00Qlo~J*e?~y{WkQHS=>;}u80`!=+`RC1am`^wjX8qT$1Dxka4Rw5fjK zn}6s((!1y|`Vf6IEls~P?9L09-`)1%huau{$CztMRp7Civ?xqs+vWnLBwH5eD-$TD zVp#TY6O+lp1Q(qO%?y@aZP!^~zQIIrrAt*ctKSleYQl@84$=pg>r1=eq+c?7dt{t1 z0jz>FCf66%RY|=`=?y2DlA+d2CAp-IPt8sXID-LSa-D#f9dIUFe7QzbG7ZOj3g;6y zprC1R=!JERNS!ODOFHl4BJbFSndwQu#a++7pueGi0{8mvXC7ILqccu)o76`tyY(N? zwd;TZUF!`PKcWAC@plarlT#1<_3()khyQvgwa(w>?tT6By>tIY>#dtSdEFzECe<8& ze(~bxUs#4EF@cOjy)?A94eec6n4vmN;SAFxXU3$oWG9*Ao0eRX8L_6ReoFwFJ1xo0 z8l8zJYGKqSK{#_0&0`PDvM9{5xFS!GRADz-b~sviO1sAfR_K>$9aJQb7U+-bn+*vV zw|%`lt+s)yziifw+P=L+ReibsuzsgLC@qbehu!%T@AG>w{LpGlj?@z(O+~~5Rd9l>)XGlM?R>4=?)1}lR#S(5`KKjI=T4nG zcCw|c*20ndMWZL*e)DVh+q6N0U29g}H*YRzT%fO2#>wlkLfV~-ENt1dM-!SNHEW{b z-$IBfQj#>$Z(W=+@kVWFlf^ClOY1If*}g~9X5kw{u-lWBR8Mlai>E=CHa)Zx=Ush240 zuL#pi-R3;9?f(0>KYUN;Zs(33{k~hL88u1uzS+Lwo8s<$I(8VT?>g|X{@u{YQ`=uZ zuuq?vGpEz8`}fl>GiFX7G|vO?K~Fo0n=CE7$sN|*k}SExvSb68 zErK;H%aWERh=M!K!PFORe(sS3J3rkB%|?}Jj7O? zX_dP(oY^z8BvYgV^3m{^zZ&MBF=U8_@zj92YLVi7`@~5wOWZH*{_LFo4AkV^TQ@%T z%-t*Y?b`j)iZbbovRyCCU+eW9+;HkWaoCJGb4xF$u6}g=I;c?e>An~PE}}msk+y{) zvNAAJw6Ac@R4voQl4OTlO(Nd9CVMgnK6WT>vU34GlUr!dO&OOmNthOzmh@G~d2Cj_!+-nu*s&82ywrehsX9#`qeC&s zKUR31e(#d;s&@M)vz{zWY43Qs@0HTWAqoy~gUtqz1WI0hytT&?1)1}}F<&(OFK2#q&7qY-u| zr*5I@X3`oqf zWH!_Lu!nPZXpx=~5P}oH8PQtN;%tHr{8jSgISt?2_ti&7PV`xM%?=@Hv;NFBG$0ea z+3RWZMccJ|>fam?NzUEO*)z zQ7pB~6GA>LVU%LOOl@dQGBq^29a(;Bh6Y9k&1hyeyW`Ca537mz13wGS2J$X4Pd}Sm zXbY$IOr4$jXzI39`3hT^0m|xvkn19R~d;Z znJ}bgEtt&ZQY;3&HjJuXg{dG5+S+Q$GH386R#_8Qb7Hun$t`p`@efZ5?LF~tPD*f~ zR!JnKwrHa`6vTKE8>#md*$$1D%qdJYJ1z78C zg6j*&I98GM3O2tkwBrUHZS2kU7y5hpnab$8-O&e&5`Av#x?N+QtIuskFA-mCKWgNI zD{Sd%(XwBm>vp`G45#kJe$wsU68wh<^8m3~d`1~(n%4v?^TD~M8@mm<@gp&L-%t8a zy*KM?z8zTfz}s?zva(;^{_%7)pL%>3(*kf6kusVMP7X1Ig^LAV+=m{dS&dvQi8%(6xgVP zwNAyt);iZ%tg=m3t=@o5vjy;vZ31@99&l(5uQRCmTt2tY6Y%C(Gi{k_wmsWX-&tR) z=W3;Owsx=;+B)DL-&}9M-f_LNvv!@U&|T;$^xk6aZR@S}w)b}QcHXL$S!da1sSB*z zT-)5+Jlni`TzlMmJa4()a=+!dRoT2kPflTul}YCliv+YTnt{z z6Cf{ka&2Lf&0g`%q4G_tpoVnis-S5rDim# zrm`;VAYPA2kpgCCmMf6u!!Y8?uz@O{(YkyvVLe!@xU%bG^yA+(F>7YDFQd?&WpPCvP0rOO}Lf^f6g%NZM-6{;f5iU0%l4sBrGYq=PRWGPAIulQF^+)-)$pMW|{PORe$*h_{1SvJ`igW`l1B12ize z8*diWp0`W^#st8x3uT5e#e`a6LY-3R>**U|D)B8a&GtR&QxwIoWGXF`7TO5S^s885 zla4DnE>XAc+eeRz^Y;qYeflreV|Xnte=2T9cikYhsBXvQ2>aWyFHGzCoFPCO7rMzz z)il#sPps>+)OFf_vz}OJ85f!r+825!#5NrHsBerHiHnQkw0*mG@7w$0i+iy~-Uk=( zreI-vtKPdmZAZJ}-HPq>7xm}$UHXekwt^?AontVaG)9!0*ab~;h85XX8lYSjy*|M2o^08qy@BOp6J9{RVB+pL%)#Bsx znG7}rT(S3<*!TLGX6heZ($D_zL&@BS*W6vgn*F=iUjJTqmI+@xb??e2IcZ_}LaU|maEUD2M-M_W#hvEo#Y$?{>j|bXIg< z^qnZUDDxeMbUw(+Y!lFj$%&Z2T5%GLyW6g?@p=77v(;t!seAQqaWG6+XVA zPi)*O@8Sl$*YY>~j@x_Bg15}7zQ3@u&1w-SU%9Q|Q)woDd@gEOR|j=A3o$BeY|n9@RT;+efWn2Q zG*eiKW?8drS!$L&%aIk%ie#l{MYA$mTU*;&tF7&=9j(KyBdyb0M_XqUS&M8%YLUIj zQ4}tU6r~qMi!w%8N7+WHqwJ#`qr#&iqtZu3M`diWZn14qx7fEhwuHAtwxn-~ZpnDb z`jYJ>^(FgDj+eqO;c~-E(U&sX<~HcrV10w_4J2qqz6EVCxG~H?Z26^Q!pkTV!ezS1A+h z=;@N%WObV%W*H>QBxqQ!;ARh7t$+y)Q<_;lFmhBFIgYrI1N%nf_6>UzLEOFxz`}7A zyH-dm!=BHat?s3uQ{Sp4W6A}MtW6Z|Df`1b>#)vaZ@X_lNp8bK$WsW=9b;eU)-b8UXkp3`sm@O_6p{G`iJ@x`d{_; z<^E-5H9r08Pd}qR;alKD-=d$Sne=ApCmOpDpEo`q-!QtablKX;zA@qFW|!H*1UX5xQJdLnG1)wJk3-Wk z>=}-TR;cyVY!EG|Gz_qDizlO*sn?8}x6jtRy-e7HYk~AZ=pzhDtYXy};z`DE8as>g zpf^F!dM%&Qv3SmgbJM$+-9HVd>wWJP%GYC4svXv-JJXD3>EXXfZ7;vfIX6mjT>ebl zSQeL8oNdIs5PF{0FB~Rf3pEYK8c9nguu@1EGXTFYlq4jF{eGXpMIYy)4?)=JfQe^Q z1$!7)UbfGHO>F9Ms-n5nWu1`ZXA4C^F*$@y9_e zZ{xsDv&Puj)Z|Da9!EB*>yTwZ7EKOSv%Ab*ym7$fv8Oqcy~)X1Y9?*qY@ju8)pytT zH1K+vn04K5gEml^Hn1}fdI2gSg{y=V9J-9F1TS7>d#&wS^&0IOSJP0_q_(L&Qq6dt zTjS00<9xpo@G(f(slTzS=Ik!INN>1EsN8^dN_$!`@6uP$ZB*93EGOOa!(LjFEVbO>3X@L_y?;C_ z>}kSgD*lP8pZ#l4%%CFvE=m{kZOU!teriAa?f$XKSn~+WICUKUiSH~r1+N2`VxDE5 zVwtMWQm5h?X^E!3P;mypV z)vc)bpH?_E2S#D3qa4c{Y{lyAMVy_f-JXiiDB>)!GFh=PuU2#*H4ZK{4s3cFQnMrM z>;dy6AG?)oJ;BF!jJt``YML#QwdxhZ`9;g5eV@`1%PwE{uD+4!FiQ0EVhfxJ;kyD- zsYxg$v#}{4YHU{^@yrR!5mJT?wid^a*Sul!PWk!p6l8(%@v1Wd{E&-C1>veQ0v_nr zlY;Y9&3Sf4z;qvf?q2=8fQ*c?#ZoG>vWbQb1(cm|9tFFed>u@9qvrMFI0$;_)D%-* zyzF+GrM5I*z>x8}ucnVfsQk1vYLU*mmqq$EkQTz|z7dX_+Lf`5kg5 z4L|P4e9Y{B)Uzmaic zS?RN)Hq6rud$xA>;hOlSgp$#WZ|c)#S9U0*5vz4m$E{uoH{;w=+Ggfnd}IpG5F9C zGT_B2^G=0z(tWZkn6|+j+~A;(i?hk&juP`acXAtBfk$ZObD{bzqp(^~Z>*Lxol?P8 zP+66kO`H2{TW}gyBDyZyC(mPRyqKxfLp##ivwN%pudSxPG+bTs1-76se^Ii!p>CqT z+&*$lzkXvz-d-kWOsQG=|B?14a8XwK|M+vB=a~&=m|=!t--ks&ML{4HVO&E6Tu?+r zBsEi9z*J;JMO-pmTHPx&^176jw`}uTXjy5cX6f~It!(e!u5I_)rp)92{W;GJ3})Zo zy{~_Sz~OM7^PKZJpYvJYpOP^MI=P0Ab7M-@y4Z}vdk%j2<Ughb%#oNJ+_qk0rBARLNme=Xs-%CtrF@~B z0HT4l=Ceh$mgRSDTDD}}?4|%#`24ewpZuAgbxTcEK3Kcz#4kUdSwV+DrKOozLnGD@ zu@6iLjPjFrhx_dgif)ZvVQPh0p~W1N6$ObiAj_1i%L~b%MTCtmHXXu&Qei{GB?cgtdFl0P>}W;QU7lw>#st$aV*4k>0Y?)EuPWBQ=M& z1l2}a0);$%UxPM3BqNG;o;(p`8E%Igny8(VdM{^uRdi$Y&ge7IGH|T4ZMrsno1x7Q z$U@pPx-@Xe23Q|^a<)l&KMR(%TbqnqZ+LLx&_zK>u z51?q6u}zK-UczY*n{JE3u68mP(S$rrqD@J8~nzB$43cH z43FIL>qogU^iVID#-M_#I!_chJ(&qq2AS#c7fP3E^&^LlE@1x3Pp91dwzjlb(ouVyCpAqF`e3!Oa?y&mZ>vy|O7p*2cxY}Uz+ z-C-?Jd*yw$+8%~XU36A#q}d|mCgtgZGkOps;s;Xo(Q=Olg@EEra8;{eezRWoHA?1BX7EViEph-+2J8e9A|a!G_BaXD4`y)!$FXj zLmu^$S~V*otN{{h^=m-eI@5o%chKb|CIYYWV)=ivmSXuagY!qBFjb)FW`OrVL|Wj43!=?SX+bk zf(!$Mky=xh(PA@a1rFp+AJs|^(X9RQhw$n7$AqMZRE6A&g2-@9_=ZfV9#UegM{386 zs;L<@rk1VR=C0YgmHnm}H>Rcrjq|Q8;)I3AU!SS;6$kJ+L=#U6j<7|;&izCLCKJ(Y zGhi-zZTn(EGlMauV6z3Y8VJwN7wsetk+UCc{T-qqgr0K=ve%)Q*mUy#_)f}3e4ghL zBxi+0^8mAFYQ|DH$C%m_Ozi7VbMGELU+h0 zM(d?{3Udt;UUC+gSHmBm4_Iw5=z|MGFg3F+NVM7u$!HpE)x9H^; zYz_|A#{|p%HnYtXWDfGT8-gPZVSZu$QMMRcR8UM%ggwR{6&w?h9+4km<7ycfk>Whk zt47@~rVGpzUw1drF0m&Y*_W>?-?5_$+%3zL52oEkG}Io(nq01*-N)eDZYWcb-p1F1 z(~m*u?X+rJ^($ndRb)Un)Ja*IfqrDkZ0Gj|UWjPMKi$8||6c!t$jpG7)x(S8#C#8V zxLougbzD7FRP7}!b>_#0+f1@HO^__Q#x`Ely3*4O7kQ^)HeJKB; zhtnoB8@0{W7K-)Fv-daTnKQ^ZuSN+H_4j(-66QpCbMVBDuwU?XU(39BpmtLE{P`6X z^OdiaV@N9t16)*|a%|jD1{!KRrYNt@huKvN=&2Otg7O<;8qb66ku0f{YKCDhM#SmO zccyveAXBS39)b|Wp%(Mr*v;X!2{C;H2tm1V0bC|B-ICBDQmU3Peu)tYdF4tk|4!ii z8|a}P{_Qka67v%q6Q53$VLR`NMi8Iv^qO*m^?)DQ}gESDB!*^R+g7!+A{c02BJ|Q-(#V%$E zxlwth40=U6w_M`0eDS+CJXC|%Am_U0?)krO;H~ayyZ`v&y#`+(d-4CcaxII^~^?FXza+y)<~H%5senRdK^ev~~SYLsBkP;KyPI2-0n8VeU>VU_<1_new! z<;j(yo55p-cbfbS{(k=cFfW??kq$E1xuP*`XIfjDlwwHnOY!e+>~2a4$aV~MOf*dN zo9Iu*kBI>jVS3<9^frfc>%iyOit7kJCPm zVJVB3jU4727M_^0edUw;W8#!6bLR~ml$~c!%zAkJp(mp0d^5`Rg0?>&oTkzl6#^+z zXq82-E!1koy`dIeATwwzl5h)n4njZQId_~i1*ikSkz!=KW@b9PQ0z}CYZr-Ao1l#u*Ry*Yl(krUm%B>b1*7~})rP4v zV%|Z*$IeOqtC5ef+TcfN8HN6Sn6rWAi+DkCH5FT1EihP)ZG{NZGZ`&`qP5UuGNvMh zL(&JDw2{`98( z%zkNpISdIYh>Jiph@Uakm?StPhn#9j4eS<_!?LApxv%k7;Z`wF=s^Q!$_ z&&}?wq0(M%h;I$_iwFy```F(N)^vm_laxlX@PDy#H?h7G9{>lG1+!?D{QS*WIkVAh zG6v|)8aOk6_XF$#yAT6EON=QpATTU2JTM|rNb^rKrh!eyhsB4-M*xOb%oqCtG_SAe zmVhivR$z8mc6cEhE)JIpwL{E9g34K?SSgij;rEEBH`m*5F$Y+MNFej224o3+19YUc zlJ;STX^~63{^}QxL0&v_WEE@KsQk!sH@YjDPn^708L^FpD`ysq%}wGSriTH3jWP_^ zWT^5F{JW9CBu{+f`j<*y`2zMNQuxtXLAr<#X*7ues|6u4EL03x4HrT1YI`t#?9@&? z%1M+(>n{ZW0@=S=iUX3$qmg7%MH(s$c-EpIxMWjRuWViS{fE@4zdS>drj7G*5y^@@|lOo)pZsd zd)>XDcm1vNrNM&-j_zhrdM^-fZ)5@O8`-^0KP;LWZ2y=QjktAMq709Qd%Xx%lxU>@ zG(HULX%f~r6Tksrk1yo*_@@l=Q(~)juc4J&i~<|e7v6thU+Ba?ywbUju^Aiq)&|u6T^k1 z7>miOPeVXH0y>XpkXe*81T_co@;ORRKt83{FGH3l4Fz)~_hbWLW>K@V@LhIJo*L$% zwL`FgDs79D0Z>!bZcBh9kLAw>(VDbcNw5E)mb#Y9HwLt(M9%0d+4 zY%PMn%ohMmhI~-81bAhVwvgYdt^hUj$ZMkW5DFDYc+ zNzx1|7=C)fn7%qmI|L_#FddZjVx!yu{Vh;P;g}A_1%?(C)4?LcV=Zb`Ob5;?u!0a$ zuvOgXqFNiJI9nD3-A1Ha@EjUZX62y}E2Y_CC*Obn#QTptYf~<>04+;|o~dOH9q9Hya#Eu5!SW{m_BECzfEQuF zq7meF9@kvx?4}C^d0OgvYoWNYO6AA@Zeq?X+==X0R${7u(%@ZR@dP$KV*S z4t%a&e&)70o1a#$xZLl5p*-jk2e_F1Z|AIm(2Wa)0rDC7b?kGxu*jKbO6k=jG&nX| zj?kud3oyz(5J?{wkqozcNQBTs8_~-YVmHfHLrh9?oScwqh)xJK8l-Nq35MVRYcKN7 zLAuU(hir57(UG%_EP;1afB=5Dczz4dalvc(BmkOOGSAr0&PmQeMl^X7azcp7;FN{v zD62m{N!&PTMPtRJ70Z{N-Fh}ADmwb?_A|Za49J<=_tdtt(NR&+XCFS(vpT=u9JZ&i zqN1^}V)F7GXLg+JIop{(r{}4b_o>VKJ5JqFlbt^+{rw&InNX0EVe6xujRc|HS%EZc zTXbkxC}o_4;fu{?g_dQuh=Ddlaim%!^mSo=M`ESAXbl3sERwO#9Ik<6nx!bwDEkAzEhaqJ5f>F|kWD~BNhAh*{5WwS{7Wa892I;iuv*x6k2f6MzC)+) z;HUh9+ZJX)RKS4DSRh($)*)7jFq>q=HCQZ`z#uE+TYGm?_W%ddGBb@?<}6EQV1HXa zTz=k7$ zdvJjAIE%j2EI$7@Zco4+9a{SlOHf*~*n>GEHP`b3`bV^vs4K1DuZRzDfRiqtnqND!K3AT!-E&P81vy>`@aV^b>m7ofCvo zbFaP=B1%(-Sk8TcTy1=ETQn>Vp39VTL-h(#)8Hvl4cTdg5aZPb)L=R< z5VOXwSdS^YBUf6DaZ|x9NWX?(1V*M97%&lGm@G9d*3P_b%%tq{wJ&eH^Uji@zn>_1 zZbqGcX7z;0S(DZtxo5$Gl2Ly@KH@3m&h9-Y&D+-_eR8elwI51LM+{CMlwL4(--4N& z($X8to417o|4=@5#87ITw(rh69_Ze)Y2w}6L)-`AihB&0(XY%gDm8ZoEH>DSjhYl` zfwq|2`$jsAb_3F>ty7HJDU@%Cs}(l{B@>cE+(_;#=+-!O(6K$faI|wz$c?(UuUX$W zyKiGzZtnQf{<&qWZ9;Bt8Gf3eIg(piimzq;>1+S8GPvGY1Y(K41lANHgn=%GeCu9M z+Ru= z|N8My$hiIz_88>8Fg2YH`I=qR?11qCX@Cru(d2c2tVA2qlkvn?Z`NMt7IWvWu#M^F z&Yd%7?p)=!ZQGRJ)bE*Yo9o>PrS#ufSHo%kUD=tRBVKsu$UnY0K5Ey5S;kpoW-hLq zHGFD_=F~j=@zovMlxuV6&cpXivuy{{;`<#FCX}9OSaJrVjK_CN8qSoKmAV7hZ}|5c zZ~d5f*|>V-z`VgDK(j5v zDmh>70lC9O5|=^rmm%mIj2aR6A2T(6x|v1-Sjok^@N?~k=Hl(uJ?EH(I|NZw$ zn&F2Zlr*`=gUTyP+a}huk2Q6C3rhch#Xz=O3uGd~%h1Ye1uXzGw3c(+YfZ^%gyVtW z9Aw9SdF^v)EmEh};P3u>+z&($duW~FqjQ0Lu)|rR#z^<3D6QU|G1C5rA`$<2$gCai zupNqg);=I{cyC!V3U7^2>0mWbda^OLGAz?@7+JP0ziwD7Jr2ID*|vOWEu7>!v9$3?4Op{HVbd|HDBO|LvcZa$&48Bu)8p z>{xNt*m+YX&PXU2Jh*^u`=1UQ$%C(JMoayHg(eBzyx1-3uJiaH&h`)zgpLRe@>uRD z+4^;{&b>}+Ji__wGtzXaP@_N^j}juCrck3*SP~sEUTYt3j>e8~273iU7FZK89}<{U zgI}L)U*IWG7__8UdhgyndMzEYvF+H#g6Y$S7fiohqhzM{svk1g)uUHt+s2K@TBjEj zOr3UX0VOpcM;x*dVdId5k>a!=R$;Ad(grE)rWe!BdCKnLSaS9_94#F~c%f~8S3Ty*he8f7KDcTLrN0k{vKRQxb zuyyovC+0qKg|$p~7c~84ddcwo2^#seAp43t$2OI(ue!QsT*HRRiukBFgqe$O)jLJdt$5d>SfiLkBDhP*G-U!)F_V1S=FxXeLlAJGh`aNj^&+ z&Y=RLaFSO=TvE5a>1$q<8}|pMY-TlOo1KRa&ze!c^qrHVca5&nm5wT#Jatk^%$Nzq z1%pOwsx&8UL9?b8R9Y-Glb1BugHMbeRXTR`gmFWh`NM`g5sb%Jx%?gOXf;laFogcw zI5otJI>ZVVqO9LdacX=``oiC|1@@B|VTH4IWUy5e<}b0ZN8yuc4TwTm0hzM{Hp8H8 z%JPpch*WLbh%d1FTLK5hl3xu@OR{UL>0OucEpZd#80wvXpe-TpMgw;&KPIuLouLN3 zJmZY>x~5bcQKwgj%o3!%!^o{5AT4L^h?xFy+uZZE!M`@7ta{oiZs}fDa%+%Xc?~%h zmt5`xAI{tP1h;lWhr8ZirrcsP-3)%RR=l6E1ucLG0!$SW&UF;k{~^-&$&di*J5E{J zV01jf5)~@&!SfKr8ZmADw#T+zxXfhD`DMR-u8sQbbE!D+qZeQK2hr9Z(NANI!`zNj zpkw;EFFyRDR~+XDpssYVU0M{#B;we-{!Ol4Gy1R|;}df8<1(Fd`;?ZOOb-WI(|be? z8bWisS3DqI0p0r{2RueTL3l|g2rmiA^@NyoLO88$fSG!>FJ_YEN;}rUE9!bdlVAx5 z)Me#lVtZP5zwq(ryTt>Q;_bK30%SlXoiSDRc#X>*W}N?2ryT@(!s2%#xFJ%o0pV*# zm}!O$haQ}cqy{?g`1V7r60sQ{6*9z&j)0@jB?WeG;o!lAMS}(vo%{a#bNGa0!1C{T zZ1+7I_WWr>>ANqyaQ5sAFT4vrcMS5t)J}Q8sPU5^4;b|fs>w`09h-?jP;k2TbGaQz zmL$_c98}wl3w|H$}Z~}8lZ`J%@ zN!Fc^FSw^$l7t4J$m?`ZNKM|~>%PSA)%Iky-J%l~Qg8fy`S1RIQRm;|ohp=havHbe z!~+Q@D$awfLBJZD4J`z$0f4Z$NF(4i0IPu|r4Otwa)5Bjpa1J=#2x!ub+{x8c$$P9 zY$+FYNr=sq%DrYDj;aA|hI=iSS8=t=|amn8&x<)M8w{Ovix^CA3;g9G1$>G>jsZ3@xV32_g`*dDZp)J1HHy2t*2S*#0O04`b=^8jqJa0S7S zCg>6nryUt;4NnQOT4Sy8ayMgiutUodaOPkdgNvVVrO0N2wWH-aewk9MFK6K1zigoo zc*}2CHE%L1Z1ZN==4})xuci$kR0AvyAsZeA6hK}MB##FY)&~Rz@-c8ulZe)ZC9H8_41 zUGk3KrM>V!a2FkW}4`{&%`LxiPWHmfs@!uGvs|#KbAk>)xqfOwS zQd9_FIl}FxZhFCCL+}l^d3vBZyoZ1yd13gvoYCuZ6xON%LiFyu5`A!C%0WNprvNec65J)z#90SuF zPzucj8dA=jVLKBJ8TMx_m?!AqS3V!E5s zlHl$Z88p3P4$8>k328CE+qaD19E+xbg<TFB(nETwbn}D0jCgzf`|deqs_& zI^MoqrTZ|Krad##SMSZ${qK>!HTotMkUua__Kb3}ZA^2aIa zc!Dz}vd&kKsi-fy~QsqGT5dMOE=k4?SXm+<>iZXlsHlgQB(mLtx7Lqd7__sn{;oO0LW-@f_g_h($}*a4nYj+juy zvz&r2u8*+5*)Kg*E6D!Vz_6Yg$?=3Cxj8;YY7STtQ)_6|rT0v*Tf+iFQ-y@gczXa+ zFjCEt8CpxEKFe=l7N&o^{T<79=WzK!r6Z#dkqxo=@4i4p7ff%2H4RdC8q%{Hv+vD* zI-3$uiDKZ1B<2h+NUR(}KYF8#w4_jVAWSAh{Nme}yxq=C02^#`1bHa|Hyq-SP&cCwHk642k0?#LKO$~KIA$Dyu~ z9WR5t7{rcvXfrfiB12Eg5pAPNk@&Zl)7h_kevkZ~`RVz+@_UaSbY_q~tw(y#^z_Dr z#>B>?#^lDF##tEgVZt=wTm!AGBY6we*H#XdH zCr@5#KHj)yZuO{%rInss4>r2ESb0l%A1NQMMbAB6G^)7x$v++XbN<4+CftTxrXqLA z2lM97ud4Ozy;4JYLD(Bal+&G$dKDg;kPmZODd6pc|De6@=I!F$UD}Bz)N!gk?N$hB z7x?)9QE73FIv>nJ{MDzu&eLv0yVBV%-tB3paemd(`06;-)Q&r!$N0i$AB~D1)y|yg z$WS|gQ^L&gK>Udci*Gh|Yu45p!&daPrUlu6D*;o9WVT02X`x|>hO|7Xe`1~?qbKf+ zE0;Ni;8x&L0B2p6%LF+ARB_JDNTWRDOs`6(P*n9!h$SFN@(==PUXz@Ne{%r8kQn0O z399$7?Ks-(P-d4a=ocHH59-DYk3@|#zVT%O(5v7X2i zp|d)M_7PP&P^HDg3uL~YV?FzO;HK?$s8Vv1{@7pNb{e0LgYkXaX4i=PT$Q5}J-GY-7WEY$0b z=z<#pkn-gbEa(?pucF_8UQ()3KrgCXp|#t%@nZlZEC|RExEml-;6@;sg8aK~B_Alk5i4%`p-pKR? zZFLlE-aF$!>0=|0EusMQjNbc7`JVE4Pfi11$T<6hh1YJ{UU&0$(134$+G};3YPDUB z3*+Ng@H1XF4$F_T$z$GeFZ;y2%ZPMG`X9x-BlLYmcx~+FpqBqv%==|n=CHiP=smfV z|8vYcSEazJJF+fSBi(-;^DY95txMh|k9voFfXBpfzNEf;HNAU>-C(pOS(wj)*bu9h z>6*sg`|7DhBaSOAsf*bm0S(3ypiv{2QN)2IFqW^YwQJ~*=#)oc) z++E&2nz!Q#crM3Myt4i$EM`dgAs4b&6k<0dF-5q|*;~!ONQ!Ok7OiHZ#^?kdHuh|?9?UeXZWK9bWf_LpNmIw(p=1)a;}Gz108^A*(OaXnDh ztpp+iE|-dnr=m1^aTN>#oQ-ghUkrfB))19wR3=ZwTw58yC zJJt}DK9j>Pa$A%qPZ7Q;Z=T}b|KE9vNdBSsQA&0F zajy9M0(C+U=_n?mcS`Z`;T9OpiMED;rYCBH&EACfe^j%V>e5}p`bh$t`2lP@eXR|uY13O9YjQ#vl zZI@o*?W4T&CY(o_pZqJIpQpc)L3yOqF17G>Pk$Pp`kU&sFJG_m9bb;4{-!$3?CVdV zy|e!Y>MyvYS;E0C{bQ&UlM!k7|SvV7Jn(rpc#l76#y6|J^QG4I+UwTb8$u@`@s^SkFllO%Jo&Y zgEqYu_#j9_wgd-ipgwz2aUuNDd;<|+DGu3+jO~Z=KG@2xKIFWJ#9T3`YStRDsowqm zZL`+U9;>!ezW15~e2+c!jx}x7W@D`(yy`1o`OVO*a(2yR2&v*Z^Om!CzuFh&eiXoD>(lg%I*K3sRkSfe^Rczaa^Pz(Kds z-E!qLQ1GCY@*Sq#&CY>y#To{e7%320nI11QSsHZOFgRR>y|PsdA3gK-M?d@eXTqAD zu%-miv(uJr9Z}FmNPatROG|||`d#~U0MEvA99n7KZ?85kQU2lXAJiuN1(uOKV;4gE=$eWCE`PCZyT@7^?xs_5ETEA#Vez<1Ku?>FU$BlofvTAsarSU#71k$YYbc zg#Go0&AkueCo-)@Ey~aZ8MP_#g0VF`erJr(oD{yttZmK+Y3ZI+o0Sq2Va=D~9mYJV zZ@3{pJp%?)dOw87lJSlFwcKX{m#xPF46#`KAN-&(n*qnl+GcADZ;NP)JYzg#J!3l) zekS5fB!Qp!CS2cOTGRZ_w5BdtMsP8Sjc@uSYK!uF`**)?Wur$IVzK_l^k<%5vhb-t7mX?!1@nzW8gvnk0_d#@ zeBl}OZqd|nzLUk<`y$)LPq;ycnayxr5Ek57CD z{W#xIbsKej;ydW)!*@9LW|#S7^XKxM;4bq~^?$WLmzCuYIQ{$fp8@`L_aHhg=*`Dj z3)vmmo_H?$aa-Z%>iCe|k#mf8^n_-$WRRyn?C*tKw&wj!OXbfw-&E&A^XL7=W>5bR zj1SC8@arWc*Wt-IeEi;!y^uo-xn&7&o#2^I2>9g7Zt?{wlxxkn>C5{wlxx{rD=s{QdZItVyOP zX^EOmEc{040kTZ(n)=WW*+RauoS3OuI;{>#WAv3P z4G?E=cC%r&%R#_cGLqjqDvPif@p)!n(_A^_@Tc|l`}QgiwZGbSeCV(@9ulvuI#5zF zX0*4;h~|97l;_tJ7YEf(+x_vZS-2l3PCCqHG`a83%WqoQzrUyA2-dv{bGjXTNh{F; zb>hF7;guE`igwaGER^4dc`>Y4Tp$<4LV_(i2t`PJqo@tEiz(^7s^|9T-o3}%>R##M z1eX%uqp+~{^HnQXuKK*Tun?GT2m!owxTK`4jJ-9Xtfb_y^p5h$K1D7`>9%FF`{ylN zQn~@!ji6~T5Bx{-g3bUN9~J}EL-_~Xrrrv0SeG2kg%&9}K8=aHR==~+B9~g0i4g522emrg|O?OkW8u5l0a+k8q#8VMP7@; zQ}3m}Gh8k8!mGZZ`cg!s3B_J`(U%CsB~loU8X<;4ASlDp%c*M+7lkbfhwaUloe-x= z2oR`L&npFQz$vRM?T>n1B3r^S9E zi>NYlU%vr?++#PNosZ^ zR5qXjP37cAce!UMM@vffVNN6?zpr_cpCRPHdxXi1kgPtWBy`Rw3iRrk5|y9jlaf~@ zEvoAP^_y3_aKRlkq1Y%)sMeC7f9IyW`~d^j&2|nXjKA`UUXVX~pU1|PcxSW#fPaGq zPM`kEk9W*;I=L+pw}Y?km7jHrW3>yhSUJ8qT@QGUdc*^9wgBrLBj!`Fo4=k{YTP0} z(&2{P=g$bX@8LLVrYiQpVM%0ZN+Dkr+5)@mMaYimEIH1FM4FN*iQ?q}Ng&g%OtuT7j> zp`HAerV#9ziWtnA_3`d$Ok5N%GOODrE*dpz*Umy2x5Dpu2AxOZ78Z5IQ8AOm585Yi z3(yWf%c_7P74}N6p4%N&lIK>Z_hP?oMZhp;8Y@?=TB$OPc$YkyY{6g~6O`;Sunl?f z)sKK)*ezwt7SV6>mToCPFWkpsF^_Qy>Q6zwr>fIJB3P=KsVpihlk<+ujC_`z+{b|> z!92ix1Hr>+H854;a!Jya$JQ8|KA7H6zpz$g@#`^YcD*w$S-(a3vSsJ4onpq$UAv|z zdux04=+Wb|o;@p4D(~ArRGV)yg`Q8a2gLR472zn`y?L`}+`MJWW_YH4+cZ%+R8^%2 zQ>vfdiB(khQCk~>Izu5fB*VT%)ied z<)2S(Kf3BU-J3m?FTYh*D@Wdban~M1&6ltKRr9!VzM*ODs#SN_A8gsVe_h|2lb6mu zaUncv-`YdR93Wd@JB&sw{ew88v5;}&$)=!EY%;&)=%!O|wm4eJ*48%cC?8J(TCe%ik!Q_(%(td)nFJE%%g^ zbmk1zdpdIlI{~_OTpyyL8Y#d)#tc!tG%h2nj|a)a^98MzxY(>d;xDLHy7)6?qmsH` z{Avw|y2s0+zExSw_PJklelqYBfYoF zJ*_LQqKe<<@mX;DuaADgX&&qBflMSL-?nW~DA){_G^D5p@8OvDt(t!d@E57N ziKGU8vn)+AqqB~y1whR>Vj&qJF!zSY?=&d$*MF^?ZgRIQV!tSp^4Ni4gSwXubi_;Z zNAw-q?X&ggoF6i>0}pn;3a=b-)d7)EdM16{+stMsgjpD4Oi{6w)z#ZMILw*oVm<0n2v@QKm~AgT(2Pn3@M zeg|jw?>h2ciZH# zer}6j$>f*B@BGG0ZhK|&m?-wJc&@Wbk?YZWm{s}lzE<%wX58@TV;i7*jfX$?S4hi< zN4`!rffL#?_LSPL`B|Qd_OWoDpj`u768PeHVoEeoX1x-u>>@RR>s3q?5nC#N=d z*BMTu;vyn;UB9aiq|U2USAo5WAwM>=sXGyjSRaqklFt#D5qzhRGh|7M*II%#<5^VH z@P5(jlwbd5qNFbClq$|vY`6Z66_miX(wHX|0}_~pD<^GY`w>L+P_3l5Cl1K%$*U+8 zb?aR^K&CoMY>iq!iE4^GP8E~FO+9;*@j^-u#w1@CuuJto+uDMMcGOUUJd{YnovNKc zE>~|}QXF)AgEBqF5vr^0l?F9go0U%O z2RBWec=3}1^I9Kj`&(3FL1xCHy3dZyo7X3&Pp{sSXY|Ru6+!qTx(5d@ZTR@bY141X zNt<4NXB}5`JFz97n%xXC>P6vIoUGa&qP} zW1QJCZv2xg3yV?`!yOLiu*9Ui{KaH@ms;1T86xLldlCox-9({or(Hi@2gNI2q;=?PWrq%}Kj_|TF1+TzynXO^uC`%7X{ zey^>pqG|pJgepcFl?lw&D=KRBi-DT|OdOM+Bg+oTzL$~k#y&qz^M)<}0#Z>2BCg-qyTJI2KX<1uEIE`M%Ab0P_ z>>ZA@g6Jqw?26}j*5gV`@3E}PgX@TA@LEuItVYM-&W!Wi;5hPP-*@{Cdjr~ZowSF_ zG{NevDI8ALFsRUKwI-^|$L-x%SDXy8cXyf+x=QToI(r0nnz-kTic!&nhQ&s8TENSS zk6tV;h5`JsPAMdH#baJrO6S0#Ltdcs5Q2M+YFR~q?tHBF9h5~|3St!ddQJC9$za;a z+LaUbl_)|@^`GN)a9D4 zY(rIy*Is?~7|Z>R&798V%~seh=(&&~_@#vdrwZV>f=Slx5N&V=-*@!QF~f4}f7!A` z$ye?n;g`$7NiNvuP?v4n%IW2AJo+I@WiJaY{uh^u!>AV82$`hIVOu>QPNf2OxR6Zh7sXy8Nua1HVZoi7?!E&>W;)8w(D<;m zBadhZvhoB2hs+11tAHe(*m)2INp{9#ljKq*D7Leq)SM`sE+pELsO;n)+;xRUhlXUb1N$ZHpwVaxZZSwon4*TpiIvJF^yBqpvZ|2Wvz%#C@&oD4(XSm%kcb=5O zStD|0xg{YdTf9TrB)#9PjAe#FJD=RMZQaBEuI6iX@P|IPt46w2)HEyGE-5QNIoSHp z{>}F&FLvv8>Fj&1aX23hN|k(-=iUpf*MISlj~6RhH+u*>jzO2}Lo9ph8r?WwciT`|e+05qAQ?~war55?Zln#JN7@=a*W_aWlnnQrKfWTyPKRUQqe#eDX+tUj)25=EA0iV^Q8H5 zL{#~VurOKf88dw0Ll0@@EBjm<^+n^q{p4_L{2OEA9qGNkc|R@sAK;6sOt}a-BXbV=LJv{x?>%)8D|J zKpX}zeqb*2p`whiYGofQ`x`5xFUr2Z(HE(tm3^Ybww`TOV%aBP^7Kxf&(`6c?2as( zI)lH_f3o}9MHzBC=20aG#$^<%$MUs5U?y6!Ypd=4+2sFZ&Mxin2^cQ^?O?#M>m?cLxROP zANfnA&$z$T zM8Y7CVgC<}E;iq#_E+PIs6QlG>aSVO`>Sz9%+&_FGBmqoAjT|+i%|gfj{W~ zfN=Bv_xwTsd%E<;I`CL*tqAVw-4DD^l4)H5`Ntto@_EyG3F`Y?f0)0|kQ@4M_=El% zy7WJWc)5#e?5_AKk6XCgJHBq78l%o$<8ce>T}k_Q75o`7Xgr}x3?y$_c1{)!9mPCz zmP3jYRej>cRy9pM-fR2E2k(;c6_o|&9}>ggd~=`jXP97>;p_#rVi(JPL>Auov+_Eb zU6t38lD60XrCvEzf4sPmwUFVJwKxYV_v6K->pxL!IAj>YrOd$`_$|N-jl%!wxhS+6 zlHoC#KAy@zo`f(+i5%zzo+jTAf41#F!i-(KM9^!}lCEV6t{tPIK|_)(qHEW^Ta~(P z_wHi%wO0iMxE2>LVW<}53JCaMg z&Q@Ii0n~vrh4yjE6!J4syYLKe$C>i>SNEFnm_DYSG2Y*kkHBO47-CG&pUXw+bL9u< zxeRQd+UYLy%pY9LsulVB@;GIjdGO}nx}5nc;+32?4d}uX(e9tk+b4HvS9vAH=}zv# zE7kQ>V@>bjF<&>X=Tn{Qsov{`M%)SL#Z+$rFGj06o}eahYV}-FB;L_w1wms$M)|gb zPrky2Z`uZue0$YA_fu*$W|)q7(7rfdc_FU`wQ5;jy^^Kuz4!hH8`$h7gz9q(vf{^n zhWQpgPuL!vI4k^qM0N`g2BrJaiu;jURMh)%muOhJzHY;AHuaOk?NwyrX%n4*Xsu=E zmsY>J@0@aFlak^7Ua54sv?$Wf_Wm8UT zb1*9-MTxUGN#vGTnv*R~$Atta>#Bj6BZFXJ$Vkkcvuqzb^M&Yv30coVen?3ui3tc| zr#m1*lp#ylnIKc_xWN-ehuELZn^ROYr-+rDw>!oy8qq%@BZL#;l4k@*c)q6!Z2cDEL+BhqqEDVM9zpO25M4Vpji z59*-)j*kAXNXxH(hyJQOq0av(AK$|RaX%u*7Vmq+vz{Iv2%fylvp&oC_`dzs`K#-% z_Q(4o&Qu+L7at#yLsz}rL6pT~iseN|cdu%DX;TXuSX(e4^~Jx;F3RthC!JcgmL-orapyZL z%r8}bd1ZL~{P^NW9-1!Jg8kXy>&>qJQ{fu)~(vWU|8+ zvmr3VMo46ceTr&*D%l73U|mFEEY52m&`}t9by<)mJImt8iiJcc2U_A{1N$T_DRkmUrguo`s9Sbgs#bE8R|2o+6}cI ze9HQ{KXqScAs5~)TgWD~DZl)t9QvpH?wwovx4gIhp;wf{$}G0^qZjYFa`u^?QyyIU z*o<~NOWnVz?oH*dwEu!1;ma9_Yv=JtsHvcJaznjfx!*!qe zcyM_}LnfV%kUPrd&E@A@jbG-uNn9os8z(=!uKap+#HK^b%%pU-oJ5GCSG zBl_#|oXcmU^x5I5pLOH1c&G6( z+oM*~Qp(G3SRL5Ac}-E++^pPZa%1Jsi+HCFXU_0`aD{ZO^QmIwe1|>shYW&SwrjL4 z?6qC_Dn~Lp5OeAMSe)r-j^%H^{(PbMjDUYQx_ zs5DXK8q-kqYc^Hsoa$8XyGc)Bs>(T(z`m;dbxes;P|wt=erci~>prF=z@@}_IR4>T zRJV{qk3l2y_0NGdHWpZ8A+RV%(|HL=28v`3%e1i&Pq`#1jx-bKME%8NmgC4PH&%5Q zW53_YPM0f2R>jp21X-Qgr(6;nUH=?==wv@7GtjTN+*FmO>8&j1{o40WALKbAB_JR2dzFkRqIL`)3Dn~_t18|k$&+)FmniZ~wG*zzk|V8B4+ zaP`6m%C|R~!`-^jZU|v~Iql8<%4tPWHh=Jzc-~?~81cE{@++^hWHy668XKB|-pYF; za^2c-xx4o4`}&iktWi8?3!JR9DbIqnV@|(Z-y#14I4Q9_W`Udo$%J&qK}pn^*?ndv zku4n8xXdepE8AWj_v#ErMZxEfELu@|cda%+2{K0^Y`7`FnOC!E8Ke5(u2)N6U56Yi z*3BLe(pb9o^Q=_GKDzhJ74_oqyc55~@ZdIBCMfq-_1%!V%2if=ZK?5x-bgJ<*`Z8U z6;6*wYaQiTtm5_8vA&sD-(a#6^XPaigz8M>D-6p`CXChcs`ek=bbnbcE>fni)zlT1 zk84)$c%w;q$;HO;X7O#f649)DyH2^o-A#U9c?oL;-?%cE*ImR-LvC+!$))r7CUW9n z??OCbbG%XYoQ7auU`^U;axwLACq>H24f~x%_TsSH;#4$UHusL;2zh?q zx}mID#ez#FlLcd%l@IM$rKyOF8~a9l?6=CD-zxzK6q~Ig!qL0n8FEkXC-QH4kaDg0WK2 z7;P$@s=*`Nr$cm?g}aU%M?N_#cDM*eo@@}%78mY;h6^pg~};{F*Jm2{}) zDvv84VXw!}Zf-79{&@*jC@zO^`3>@zR>IG)t5jXzpgE{5fL$dPx&ZE8O)}hO{P5z; z(N~A0{!6WK$u>u#F4KWiC|+=dy1xNL#U+O;^y0dPvYO@*FNspKdqnw5FR`_;v446{ zS-&uG6q&jfBo~!8YG!klawj)r2(Ck;;vo_ zea9j6!n%-mnOm^b7@63l0i2OhEQ2?jC6F#=HFqUDP)-$#-a>R-ktfdLr<^Gj8qm1`T2Q&}dJ3JOl$L~KJTOR2}%$$dT zbtw!rtRo0)Ly;Thh(t%dSm2`Y zFl>lw-@!V>cJLBaQpUy6lR`4II`S>5DtrG{Z(-nmyjJkwTMSH8+NokNyX0b}yg;y? zN(Rpuzuu*gA#?`bQPn9xb96=pI)>!oPUbu#Brb=4h37j?!egYk5LR{ax}d!N&Fn_; zojtSJiP@V08ZrAD<@L`_E>}L-gh&teY$tk*(uC*_IqE7shwCV~LlS8%LEXQ5gyVP7 zASuii@kR+%OdQK*7g5Q?BFHm)ZU_f?QFJN4-HpyAjwne@zLl1vJzlU_(jrG^k7RFM)O^hoc7V!{9c0wP5~dguZo z3Zi0#RRml>#RbcXxVoSspzDgP1q+aS^7}mJ-kD4Q_kG{*A3s5oxifRlbIy65=REyk z9@ndRR5r6wY=>|XHi@;t5t+-9zJHH3V=ehv zeq-9#4_8{|&)85ihlE$@pRc{lKj*)q?7~0N(pd7+#qa+9(+3No1rwh2gs$mST0+-^ zha(ZZLg?HDg|P?=i%)iG+;C~Qd8#Y@&K@e+hVH08+wkC;o6HaWM>-r?sHxz7zWev@ zXZGFY<%3296&20g*t3_!zNka8`z%Qc>MH%~oi3U_?&H0c8ydE34zp+TA6FI^&z$}B zaKa1T%G6Pu6?t7ea1b2Cbe9H+>#mU&5O?T-keNN2n&OxTr)|W|U42rT+TU(nuOV)G znV7A%M>sE2XTam2j7gCd(1Zr=!5@bw%*sc1lZ6Kgu|}oP*_1wxPHjY>vHXQ5c0b`~ z0REVTR2Zji5DPNI>9~=QtaUd-dB)G!fCq;R@5SeOvk2Vj{;WCwj9>qz!H~W$c=gN` z^Q7{ill%7b!ywE;NKt>F!o;Nmug;ot_J(B2v&zzAW@c2)s0r(Bbp|UAwNdB%3KHogx^M3*{{StP-C*d`L>NPhet^2NdgV z)^jgpwP-WStLgzcYs9Q!eS8n)YJjbJAn7G(`01y+m-Xv=iY1OH9752o3ks={(}$;J z#BN3++ps>f$IjYG&~6*BXdK{WhEWaQD$@XO3sh*ghNPRk9J!`MOOYY6$dHa+P1NxL z&3n#XY>UbvAnA}S`<~$7WW|vfvpgA9$CoSUVV^?@bEi(>gVgvW>pF7gC|3p%zNDv$ zR1;dQ=#FYeJTU!%{VO*_A$2X8e0a^-wkgm|`MFBp?p=n>?a;PW=Y=KhHV*E@SkVJp z>dI2g)A+CNe6{TuD<8LN#R|Fg%+9?fmitNP)MVBovwi30E$d42%O5Q0$XK7Gw*PL` zuE)Veo%#esLhvF!^{rB&6o@=GbU?uDA!64PMIkIV$bdeb z79H%-uGPQWCiP*gW5I*v`K8NSHt*a%vjs~|?fgkDlX`d7;4*grTpa=A{}n8Iq5WKJVL7TG?f|J0R9Nh>M`Oh#GVLQq)iv#-5&mS5xN)OKYfD!R>| z{bPab00-zO7q5 z7}ds&u=R1ITN77KIz6S7X;*UOpc3lDk*o0LJ3)FQ3J+MUNO@M*mj@6@P zsp_~~`QFq)gQrZWfT8Ry5mz#uqDYo%0>vYXzGitI;K)cugcs!X3ID49;;UZ~QZixe zj(41zeDTfZjZb>;PVGK{Ohf&?Axmj`g9ul=7cTT0*TtQDsjZw*cVAGX5T?VbBs=RX z?lV7+V-*^Jf>#i~-itFP;}8kLu%HlkBcV=&uxepHp?0ehV41&$uI=osMeJ(zwO#xw zf0t}htG<3%I-_0OF)U>^Z&R4(N@7n-AI^I89Q%P?bcFdKms$#RP8$Y&Z#-h$+T1WRDJ#}44@+;pvdW5Qz9Ja{Ru&Bg> zL<{)@^yCs&5L*_AIuAX<$s+wO7cw4C}<~p7CO5Z1`ithQ&6~bud~JtUyXAw zTiaLz4g1lkyIV~Di)otxFLYDsN9=uouFW)Ny^wH#T+FqFvY=xe5cQ}6*Jb- zBw_Nicl_AY+4aB9X2x#*mJt;u8eg2R{4&NzY%X$F;%uI#4v^6$4CE=#8n@5B?%maUsgVih75R%ZN-@ zYeb?mw^eKKL*Mdhfpdd*z$2Hnk<8#(BDr0pfwog z7lKz811CXKdK?Kt<%PMyGC8ZeyORcA0W+D8|GjT z;olVXNGPWm(Z-6Va?!s==qrVBpffoR7aXiz@@ZENEOf6T^_j}%DGHycq&6SIHW`yq zZ6qbimHWy+<+u3l*DD5m^6|ijB;`!M5fAkxm?Z;X_VQ6OAY0+2-~-0Ef^~{Ai=qWf zAygxKi^Z>^ z1pu_-SZXApR1WA~T*1G1|1AG^MRE55PqM3Q6Bc-J_2T08vsUwqNL*Si1*Lp+`s+=% zSOEX|)~2sdeUt)OyD+pe9SEM6p9 zNpgd~1vX##wNvV}ll)sP1No%1_9UYtKvMFh=|hLIxky8v9>u=~WkkD9fDMFTvtK(0 z{>gNC0i_?GKpHZ5G5iy#cbJU++6KET!%0URmq9G4jj&=q`j+&1-5#|*@7Z}VgZx;# z{{7Cr%Jz!O>CI`Ayj^3NLddJ9kA=JF{)2Vwc+Y5K$G|STM5!Kg#~)nOqMsG|Gb!tOaeYFRf8Vp z=d7#*2bo0rl|5PW=Cp!*bg-ql%c%||tp$Wck}WPg{eawrt@PCFyB*22>$3SOxx%tL z{^j~t5RT4XfDVW z@=Jgp-XHx2_f$%-7O6b}-N=HYqUg@9)ny-4pdcD;I*wMeK=aTM_ZQp2{(`J{x0Sz6 zEyWON+B(PYZx?&>T*(zhyMZ5{7?xTHsB%Q|&# zU7d97eWsk?(ay2e^B!`dms$G`{Em}#n3c?Ti1K+Gmrq+#Jgum7?0x4%CYOWE>Kb3f z4PA(yKn^*8f$}u}qx{Ia6TJR?wFGl5scV_pqvSAqqi$_N6=JO)npZtmiVavYZTUtD zX>sx$$+K7oC%@C7{XqWo*wUhDv|r^oW7OZI_z;_RCqnDh9nC^3Y^)p<<4P_|Zi)l> z71IMY)lQz-Z2~!#f4n{XliKOt%NI_bR@`sxN4t0f|0yHO11DJ5>Y5&<#ibF#i~QXF zm^glO%GAy!1w|o=`P(0R;s#(}(!R?RMjX~8h(xJ1uJ%s%r zf0w8y1Z$_QiC#?PL`HY=qcu*b6%?{Uddt1^wQJfwjs)p0jlIrjRFd7r65A|5Y{h}Q z6?iyWG#rM_6&6& z5=3gUJ4ruH^=lRs-mLrdTE4d~yM0pP8{sQzKG090TY5pz8CH>yrG96%9(g1xD%-{f zJ($tV-nnHzCV#Ox(N|K=Jv~N0l+szZKT?#!_I*Bsaf-Rg0A^%@|(a@yWkI$ z>_Ay^LO3!zG~rHmY~9Lt^3PauM(g4tX`}a!6aPd?1AmQmcS?`B>Z1bZDSv(!V!CjL zc+_5PYP)vl%zI!i7#8c!U(;QHE~B*O&!89w&1X*JD)7X8ABifi;xT;Yny4+Wtz}nN zuB_vx_Isgj*49$}%kg}3N%^EvQaLaI_8mCdgb+8BTyS`u`1Yio2Wc;0!w~ljai%y5 z4$ym|qv<@A^1{YYc6o59;1S3zH%9Z95A7H=wBKiQ=3XcpK61y4{AI}y^-#@wH6x-W zgZzB)AFC(7-K}Gn(oQ}8I(79Q#ZIS<-`V+>ufF=r?%R=sl;Eoks`a0Fz`<7`Eb71UFm;-vL9yX=_VDbRwd|2IC3+wgG$N2QQ z%qhBDJEdJ}1sLh`8r^_rn@8jOh$oHn)k_8CMzUP(S6?PeKfn7JpIO5?+`N3{XI3&x z-B)w!hICrR#V0?nj+M4Tb8$-TRRrR=;Av^RAuRg>#q1&2Vh~>-(?{3zUukDkzasl! zrn+qb|G9n@pI&RUdw3M@KrxmcRv)8FhLJpJ9HyiSc*%Pz_`J(F1QcR3&+wDvE&b>N z@|E(-Nd1nGaPRT$Y&8D0^OwXph_NxgfptUsrTlcTD3O~Et(w$250RrWpjH~yD7#;( zp5+d$i4opy5-$2aRYNjo*M@E)MmJK}M1x@|0>vBMv*d#OQGN-=XxxK^1!j}9dO3sh zxVSrcMo?m7K@mbV<57QgA_jQ*qJt=N|? zdam*Rdp zFnd41!t)t|X8XqxJwta1HieQMSlEvcuiePvQF{m%4zS^H-K-m{$Lo${*5a2-OE){E zcUbm;Rl;-p>b0*9s~ZG9c#J2a)yULGSHz;xZKctI$CxC9SRqciS_MRls3Z%5k{pMnwKOFE zz;j4bs8ikGF(}KH-VyZmKJJ~rW8J`8)UXQ84b^cq7Iwf!fX(qHDQj5!AuZ+UMN7N# zbEi(R+^$QDrpqmdj4e=x-r2*$^k9UyOfRds5#{ishD`BcAXL6kh*Oi z<(urW?rK!hAIV7t`N>H=CnqH@){8bBa}{?&KxD8UN)M^h`=p_upNy$=S3n z73ElvEZI&;Mr_j#fqTTi8~qG$B*Y0bwD2vFJsL>@$QfuX%P8%EL-5Sx0hKF~l2%Ub zKSesJw&UmcHS#sE)@AH3?CstodOkXPez%Gd@{j5n^}>^@#w`g8dwlGw^~7grzMr7C zK`;3)HgA-7^$@Kf?fj_)yi-x57~cBNY~jYo+-6C&0n66+VUyU0XGCn#ew=@_7F*;% zjo{%-FYW=HjTRwbaoiy`nzoo#40hvLL@9+KEZa*7J_p9Kef0| zg2Ti2Eof4NUmy&RAo7SLf;UiWE$AjO58wAgo7GW!8-p<(=#lBUeO+BqL-jvG2R_mm zmywd%ho!jpQ0$A&C*oc3!^V9dUO_Fu1iz@emt&yJ9?Qcm(u8dsck;7TqsK+#7+>%W zIG^(G&pT)W!9O~PcxGb&0>vnH6Z`24uVrho29G^6qP!5iz_`v><3(-M14)ecYV0w`aRx*A-RJ|s&v;;(D^j(?(_YZl_t)R4`J8c7rdfh{db>D z{lDnU1nswg43`1f91hZ^vU#=$-_;|dbyjtcZkoz~uWr#jqqQ`0^2~&UiGQY-s!2`i zU&SPq7CYi2|3oicJ^3(bqy;ZDB>`V>ExO=?Qs%)p@28W>x`n+k7iKv(gucQ5gQ~iR zf9;{k=-S7UKUH6qO!Z;6mP5GRqTy3>4)(IS(C$Iaq&0jrpf=!4@I<52LEmA>#v`FNxtsm-);rVJh#fFDiK5j zmwiGZtmGUew`pbH;(xtU`|zB%J2VSLZG<`K1&@7gk9r=y*jI`p&>%t*3T&yGYUk3q zbqhtKEt#HjH|+-8h-6E2W2n%>H1^@zTrD);u2tY9!v=mRX5V8I^T40OB%$*Aq8I?jJxKJ(E#0qxU{a6$PQoTkTo?$ z5kEYipseA}MMId>=cAqLHthI&54N<=f^Ez1eT1UHGgL6b^VkJp5=3%l`e{Z`|&3~`a-83s!S<L^9^8Zz8u^qrrX5*g9?`MmqWAsGg<=cN`nmw>yp1Uex*u#EyZ zM3k52H3V|7?8Jop+=PU#x%ml+kt12x9z8sPF+S5gCMG5}GovUb=0qoT+|cG(MiW61 z5lJyq*juT=7%1wGHLlgg!t{h{gmgA^SMpl)OwX9lTaTQ;9*l2U*e5tD%4m8iyLqct zW3FR6&vu$v6df(et~1+%ag8)C_LI~yDgmjH3yrS(8wPh%@^X4)q}Q;wM@{61;tN~$ z#<0?}?qPYJYgoX_o|Bg<4dgq%XH3n*0sDd&m=NCuSLHhFFPM|6LfC(oF{PR&fT`gn(Wd3z;Qv!tPuA`=t%j}OKL2gYP( zhDT(}7bV#zCZXtoG{2~9_Dgp5KPAae@$$-jC3~ME$kyyTqcJ$DRm+$_Vnl#%BI?z> zN%oujamf4fL*-4dxo(|0sXqzk7ucy2nBSxN`LRjzT8u~fu(qXl*EiJpu;dG`Q1{qg z&AUk%>1lZpzWn1JY<1rk_6}ik8M|5%+04(chNY*av<#MpV1`|+qEjc#0Ud0YGK(EU zT?Ui%o9nf_K-6jYjlCtR-rzabwo2Y7^+kS%mm#XyUq&*F*IaKegFM0LZ8=|miaN91 zJayKJD9HeLx8n+e3Z=dk<@^_~XAT<2cYO1)!Vg$vMbmK%h?qvorb#ldswijv_@vTY ze3DL`ulAJ!d^<^* zrF~_q___w)VCgyVi4d}c*+g?3gi7f*NTGIvh&UEpGwD+Pi5}U((XwQ*mb3{9lDkPM z1G4rPw{PD%Xw;~0MoK@j)mD4;;>gHgTk~nQpuD{KOS009{Hcx_Uv$rd8wq3OS}~Dk zjfWgT26}OCAq=$*V}Bbt@|#hkf?5?9x8I*NAVumX2L-h$u~;Q3DkQ6i^ds2B5Mz4Q zlKFXgLAGhlZNZU|i>vLhvoT*KUi#H^++f0fCx-a5kVGV?{+qwSI`dHDd%y1A`_!|) z;;km13o+(rvV7@R9*R%BVSMl9U!Q$y?|yB6UQ*`Cy-Z`pcs3FhY|`MH&+OfI;HGIT zKHmw=r~ggo?D;vF474)ar+rSDhxuP;r96~Aw^X?yJH>cyi@g=2WH2j|$&6M#gnsAG z=CsN-^f=^Q*5gn>*^u%#4d#Xu_>viaid$L>TMl}q+%OOg3x>e}>_|2{*wUYwVecoO z>;*8(E?;gkeqOORaGaTu=ja~4?b-ifd>E(gg}IC^Z4kpY7?c}q_LVDq8Nq&1iDV0K ze}XjwSF9(G;`JXKf!v_M)5hL%8sA&%FB!hXKOypLd>%R#GGTI256K~g@b{{_ zonQsk-I$*=ncvjT>`Q5~{#gUE*c@Nd`|@nO3pj2_Ns_Npf$BC34F=v*z}EPzR(l5c zrm?=#IJrCWImli^<_Cm8AlcLZ=+2!-cS;B8-sBn%EJZCy-z)Azn_ z+JweKM&kISLe0A*OjTE&uBv*P{@>~<5M|`rgQu&gU{nDLKrz-1J*ck(Qkx;BIKbze z+4mg0FIFF>Sc5I+FP=I@5%XE+uU@>G9S7xAmqg8)8k<+Zj@GnDZk3nZCy5nQq)o3W z>d-wZHPaHf-Pd<$S>6=v;8tU#Jk~S-ciN2N*sxH89AOLaHp{-~scEzLDiOZ6=rC_{ zXjDL8lp$K2w3Yha(_vgZj|)aL`?TW+bpX!rll>Gwqo2vg?BnI*?c)>Z8|WA49~clA z7-$K!dWZOg`q+KKe8PiGMEApV4{nS^@-syl(jH|=;GU+CtrN#4c4^*4&PYgYF;)ED zm1W|0W0YlGU3hes6jh2CL;8(HmqKF4SzZR6{sDeObk(}p&m`eiRLo2^n!PON&z~22 zWkI5Wz|sKsX?ls%^ukrDpCsp%Uy0b4dZGX<4WWz8#LEAN`{w8Rmz7)Z_lE0alMZl zL3yPI5}Qzsi!`8w1BGD~%?XZ0(MselE~3=_3y z2Bx+FEC$mL6f40X6APgfH!`#&U0^LN;HC>P#^{XlqrE${@HR#y6m}cN#+!b4G&nS` zuutODsi^DIGCnZOu{{E?e63t=Xl9PYyqn$geuX)H7wg^Te%81L%iXZ-!u}04)r$Kb zxS;Mj3MpwlpF(TAiZQ}WwWz`LdIQuGjQ_ephzSO%J`V4-)OoD|sUR%Q=8#qY%X|5! z(%?7ukVGk1CWB|kyoAi`^J4UFxOt<&Nj%fEmH7|xHF%Hfbh01A1K@{AE+zj zef$sQKZx@*2=zvbR_A~8>VBcL^BU!uwDFzOm1z(5F2r6>Q9k2)Om7Q|o5>}l>wBJD zzzs!d>FLQGIWMg#nVFuxFz2O$5G~n=Hl{d_6sKT=QNeiH3Ukp zKnER3_nVwgCJJ)7Q;wu|IsepDfRJK05!& zya!+3P)WQ5=MRk*c(RoDXqZCJz)Nn^GlQhscrgF2;op#75t(Tef~775!i!FE1v#O7 zKyG4EPEJx{?)qhKFT>x4gxnlF$xRrweEF!6%Sazmi+CzO*f2xT6ft!zSigQEM44$3luhIIkhto0cSnv@ID&I z*>4>fYziLSYp^K<&JMhjZpzH*mMNnP*BSJd@)^A+s31^c~B;2Rhxd8D|I zdidF+bWE7-sb+;$y9&kDgL`@jnLOolq+z> zMoC#w0e*Gs%*HVZF-dk-5TD;{Q2)`1F_CdnetfeCya28ml-5H2N!H{aO%CEe5YD>p zM%XJ)(CY?fOf@8HGLX7~whHv}xXC0>H%}4r4wd1EPfQT@F%cnaR0vh&>ASAY8;HDz zw?|z#nw+`)@+0NU$XbrN^P-P=%8TPV7xx?!(row0(&C;oz%ejprO8`fgE7IEt(5Fe z$`jn9)crMq9scM-PQsZdUQT^ZhW5pP*4Zk z`SyFf#uUYC-h0QXe2iv~2h;&l+?`B{-zdSH|G*q%6Lo(>8==IwiSIl@{W6b8X-wrZ zdqneP@_7Ic-_`BHL>pv~+tuOHR9K3C<^l8`AHa^GLp|P0lpQv93~zSj0qkeI2huT* z1gs>rBT|Q4Y(RgsJ|R^}+A4QGbzEh~>$mJVE*p$P>*vS~w~>P(ue9$Uu2KviV>|gk=}g0;Vo%K`ai_Fr z(wVMTPUq(Gah9Af7d<}AAA& zLmJBL{4q^jBN$d0Q{^S*-z~}5@F2DpuuT-W|0D3CC-tO|A$3^HvVpqs;q&_>AITvv z!RN4{ppma|CJB(uljT4L<&Qqeq4~Jp^LkFaXJvEleNXvZyl1=jJ=EPef%lT+@L2bV z`dlxF??kh2?j6tci}+4_w#RpXD{=BFtS<>zh`28qHh{+U5zUYv4(qDGdi#pkiTD7c z*P_o2j?|UqNfIV}>3hDf|M^AZ_uT8^BMQtkS87+huXgc;j|2 zsp3ECG&z+|MQfJ5@-^3dq9&|~>I`B2p7`!6F+c8Q4IXo@c%6>18+%%kZ4XpV9w;6E z9rFF73l(4f6*AjmmoH!QC$WXss0sBkwH~1~C9DVQ$x8Se{4hVl-?%HVP=iNC|61%< z#$E71t$?g8@C9p!MGsCo(4{LBn%#a0_3ovd|46x1d6G}XrWuC{!n}rY2O!T4xQ%hM z#kinx>OqRvEyTKEX*N1AF8SlhR-)f`V*NM|lD9NA02lf*bMN$;D<7@zG*@|Pu39*E zF1vvLL)Y-ZY~LDr{~ETB59Y7wi#~?oX@1}u^@VWN*sTCQ=g|D}3fDTR2PNKz-VVIz z2YArKPw)?KNW#Df`~Xz_q|NGh{#S1HYZX?XR@p* zZE{kF4NJ|T3SK|$xNu?n&)|b6e%|)!g&jZf1vx3hho`V!K{+$7eKVs)(4AkM&Oyzl zZu*_ckBvzHdmNfD<}t4RzG-T+d2&Heiy7Zsn~?*ULG>mL58H_K<9y8>03*R?l_&u* z-cC&-;0hH$qV|BzT7a86E(baWE;GTLMHfipFmz2x7&}CIw2FUCK=RL~KFXvaV_Cvm zY3-CpFpKr z&ZAn(4g5jP7Bc?A`rP*YoZoI(uI99Am5pLIWKSvxvRWi6VQLAohlt>xB#A1EBKa^_ zQZBPz%T>qrUGJVa@y?#DYWyfLX+uYXpXZY6oi=`C9)8zV&Uzp+s|3Sx@3md zPW0=@kF@Wvo2T~i%m|k|us%z6?p(qP_>+q_Y*>sS zjpq^VKNs=-Wqb)=s_hTaq$Z~(f~G{OF!9quc@=;6^vjUs=gt`vS2=I`F_GtVNm07! z-$VZ+d=Gb}9#>700^wSS!>D;l7)n1N5vdk=oC_Ykx7|9*=*^yuD$unEYggYwocP>w zeo2ho4fhrGNXH3kGLfrCI*5yi98DvjwA&TiUA@sq4b2?b!POR>1#O^YTD>RwSLvaW zl$?n2y3`Qeo0?2ALo4tp#O{mxUfi(vtr?Mwyz!lgdhS#{anxdV4T5{&l)F+;0|8yeDs-|C;U~mSZ7m2(wz@N&5@DFe`PY{YU2p&MRiNqpJSL!ynGPNkp z-nRC9$MHFB`^@RQ4Y*O?Ct~1~Ue3d<9_wk(Xw!dDpX!GydrZ%$@WAO*Ook0X&tBSl z6xRp|j)S8bZcdHSZPB{OjwbeY6>R6gvXV@*d;UR)24q z?eR7EbqxsWYVk4U2UxsH6+^zoB6SG1+`N7@hyJ%*wOpmk@P<8y-^j_yF7`Hr#Drvp z6o)8mn6T9nI_ZKJ-HGVZZtT5zv$}XOe{zwQ*dWTXK=0G|v8ZY3N0_7gb+yQGW#S@q34#@6%8H z0P8ISPpJT{gQp~d&AGN&BTW%tuSdv+N>>CUnS9B`NScHi&na{5Ecl3LIPp&(fDW|# zZyxs5ha@`G?gA$fNi{4~FjknZ(4;~-s4+{)_L|N=A7h!)hh--aPjvjP>-?|s;0@|c ztquCWcRofsHSS^2lN9=`O&|^ot+=u1PR+L-cPg*&fpaTI5&6r{6DFt!FOPg(V*wiN z|DW?FN0t<&<;B##Fn)Ya_m2H0Pw~i`e7N$;iBa>zGde6@bZC3|!h((tC`yv(({&MR z_oF!PL_t4V>O1P4#L0pY5axrfgj7IsDG7me&K?A&JQy1^s<0?6r(`nD`F!hn9Xoc< z89)Ao+L*kwqSk}b1DO25&UYUqsMMNr!GW2#PO3R)qlyKoE+fsSinhjj+0@>EQB9s@i_02KGkewbstYuZ1YI( z!)wNkUjKB(BTMf(=Z7Io59NFLZ$QvD$9f=WRw4SdZFpRklMBHMBZN%b%aUZ6xauC6 z7B;qOjAvMM-XZ;L}x zS#JHm@m_y+eV3H4?hRb3%OVnF5pS##)&tTLHEcF2qzfYZUsMbS3uNd5reQO}9Zc-2 z3A0~H&6_x-MVjQtf=sJcLnXPscNc5VP?~7TBkK0{V+UMM)oi;sR!PmdNu>h z)N3jPw^K5JB}1?1!47&BZP*J7V2jk%6O<(qmUrm%v)GDtyLRp1wdnF)yJOd`b!-J2 zy{z}3;lrx<_NrmS2lX}u^USqRsQmV(^?W*eeElY7QlD7MKEB;^`ux+sJT$z1t2}o2 zL%*D!4^s-AlWHCb+O-q^MueR+Vq(%wiMO49QsT;e8mD&CZk9&xS+TZ9jB3kt?}6<=zx~xtAu4cA0FRgE9Xth;ouk~u1tfh?qnLvib_~7S zVmFD8XPZ+tzb~C5ZQCbm z908S+c1Vn!&maZ)=0vX&j^!cKCi7Z#jZCokB|VwbQv>@e@f&rO^-UMf+KNjEQTxU)^Uu=|ap=w(>UM{uLM zn~!kr+JU)-!j^Lr_zUa>T%=Zs*^-mu@)L}TEr>Y^Yz6Y;{O`5wG5=Uwy82tK)Utos zM*ai)fyJ1tHD^4h+!dRaxzbl;pw zYs>21n?1a#2G*#5)h~Ond-;QHrHmu3gHn5D%EP-qD@k{&WB^T*tH>7y-&Lu&JE_M< zJLKV;3=iM~jeZR?yr1BPOcN4<-10e##T#)#g;Q>-mSIDW9e-W1bQ|*4v7y~8Z023n z60Y&T?A{I5_`!Dzckf>KU42#40tq@i3ke=sv$+n`e06wzM8I1Cnuom~@a{6u-cs#Y zt$#qH?utrt@g}fpkQcgmx?2W#_6vYr!n0kOwsq9R>rR=uv`Jqwzyu!z(G%uO!kplz znok5F*)F~V7lT{h1{FZ#9&qfKG@cz`2c>bxkE@1byoJhhj|nE!ioN5!gUT(+Oe8}5 z`wn$MW`?z>FR%mmKJ542Fnz*U52FxyhcU)({0WEsgumC3zsK<@Zeu^dZ~|A}S~l&@ z0OLmh3wzNYd(jfK7)Bh;2J86U+It8xe)F3kVE@rgafrbI1dY$#2#vq(l*ZhRIsAyT zvQl0|ea}eS7w6*MBQH(dqf8D~bBN-u^6`Q9; zJQ0x{ArE8JwoNl-8m$f~E>^~o2F1nNjK67J+8z~s=;uHRC|=MD9q1?8r7bGFE4x_o z0CwQpuYgKp*%-FF7N}FLzO!3B&Uz6aSPC5PDg12Uu6T`om+$f?>b?(%slxa1rSN@F zuOZDph?4*gW;R^$Zi{mf9x`;ki-rw5NYscT0wFyflB)tvCk_SzKg2(G*}P1g$o9T1 zjvM}pB=Q;4Njx*o3B>R0qZZYuL)z~-q*ZFbsZ<9q(^pdtwhJ0cOfg@@M|OK}cdr@pl;G1B8VZWPJ)`?dP-J zZ=I>iYoH`01;vDhN5&$qq}9dity*0@fBou3N`)&92=J*gtn-^`F-KMfMTBOZ&AFZfoALFW zY?M_(A-LQ)Yod09ILbW95f?8B!VI!XT?R)<-EoS>&By=cOc;IVsnH3}$=mxGAK>4s z+xTCNcIA8^og;Dy(46=!iM!qnY{T(TDm*f=WCX6pUU7EUHH9W&7%_lmX&nG?OlpPE^rS6jkyZ+_H^_Sqa zKuOD%AdGjN>T%qv0*}Gh${-6T8zvXqq9VN`l-Q&oLu{hSp6DB7u%=kfp8_Aok=?M` zY5#2A5%Zf_L*_RNNP66XHtur+DpPc3{PvBrmK(@&J>=7)to%44B2yYp;FK6_JSifA z$KG;~;gw*EVv&Z4jzI7$n9ze6%@$(O`UM&U#W%+xLO7Ow5d!)4NB(Hd_OiHryWnbm z6)v-@{LSX?9NoHg-*f!Cl04?S;j#VuR|GhX2ON&KURnA~R1x3v(U)&dcya#|I}goX zws`T<+PT^~!CQ!4BMtM5!-D-XeIiVmUO@pVp*{|aVn_+|a!8GI1Y&{@9e=Ulh%_H> zDa^+=EIiQHEX!5?#+l)0`@pJfa4=lA=iH=!ET=4|us_*7`jsEHBiYJ3%pRtgEobm| zP_b`JRPU&TQBOp@5M}gqKQJv2LO)@|IFd%z$RQA5pgw;>aESWPq@$Ari*U*v)lN(4 z>@>Ur&r$_%I>R2enPl)TL+&AKkFrD2JwyUrfuR_W3Je8E%?rwFIp~lLXY(e#)E?tp z=rLXpPjm0llf@xMFOv+?W&R;7#U=+v+B0-UQ{VP#4$`+;Y4~7Ivjr(~n72`qBdW}X znZXu6Ijkzc-ncjVz7!+l-00=)6KJsp_*-NAf?|AZzP^6GK|VJ8i8k7z6(hDx`N6_e zvv=FJW{cMk7UK`>!?qUJ-$BLRF;Pff3k-~cA4il{yFax!Drg&blWmg6OVxrrIxnP< zjpN2ooa%OoDgySb{|FS(?qQ+`Vq}c&W)t0oQHIXNLEu_Znap92NC^*4k%PR0DC6$> z)$><#>aSXm09kDF@iqs{H%sfnf~!Jinkyrss+whi4}%vY9s_ifod~H)T;RIg<#Ex2 zsFiBEwa=X!hD*IyFYQ(b1vg&O4|rbVBoNb?>#{ZU4=wgVKQp`rH=*}EU2ANmvXN(|Msg9!Z0jbO*o>IT){- z#25j7asLAT2*d1RKWnI;5+wPAhWYuR?bxY{rz{t*i@2BK&@`)$pIMg7^9_}ecAG`^ zs|pU7i5_HvI&Y#w89pcIvpC!bxokncF&2w8DBAAr6Yb+`jka1qv4nD7KeRGhelS~p z@Ur|c=%tuuxQ&eQZn@coZsLYOzKi7Z=<_(fiBIL))#p%s@)_hF&)bChlj`>9Wl^0V z7mv(PW`GIDOJ9I~N4DNAbVkrwd#owk5DUIrQ5=$(X!e&*(18uJcM<#5%ju7B3?Y<5$HFBGld< zluf!<2;K39I6cTIma3h{xo z(X1B#;!u0@E4Od+D~Mnk!=i5AW>LM*?7ekspWL?o)ZROH_TUWfj=3UPX$aP9CkZAX z)7PBoV+gTXQhXgjCYIt8D94BCf&pF9n)9~UV)gTuja31rnL$#O&$rI1!V@CBT~G=1yuy5 z1UvkjL531^j_oHuaRBCRi0|nF+pLWdfa_)llnt_A8bk6cA`a5%CEk-T`32 zE=CZVt_sTFeZJxUIWB#-zh*S+M68Gignj$|lMGc((a5G&T1U91)p@KwpAV+*ZJktMvryP2uq_wwa~KP@6HJN(r;fGz72n_N(#*th8@L z2-(H92cu$?7}*kG36BYju@mUtY*?{(Y(#8$R#=u@nV#t0Q9<2J#98CV>?lbd$A4QY z1b*$+N^N%qkvp}$2CdUN_;K$)u#OnRoZ=`Svr)lXVltx~Qi`H2rJ25zSnc@5QfMU> zO;!t*qqWr_|9fV$;B`j7Dw8kJpvijTVhk}c|7AUz?h((iR?~+Xri}i5gvXk& zGI2aYmh(g+pc2+5QND?}3)bdEQBjQ&wa}$=W6?@k)1^Y>=ch)J#!b5;Ov$vzqg zN@`OaXiD)4PV_Uf6gvqoL_$Id5Gd>KU1eYGrBoSbT3D4qNY@C3$Yw7EhI@)2xv?aD zC6nZ3iE~tl4fsZSg+TlUaP#HWV(r|AKk(JZkpCHs7ma#*IIJkSq+BC;O@gZJuqMPo zO++*$1iAt541kSbSD-ph+7iwu^FzHBunx#0a9+H4@pFU;sr-F*fWIVl*e$i<%h|Nr z5C5*_fAG;|lIx)(e=O{?DTY49!Lf;f!AXh!k<2HvnXkjFq!{B#%J4wz+^G5XO1arOKVLHx3qMAh1(7Pa1669tN zZ-%tXrPy8%J$6C!T=0H;8DDzgSjcrHK=*BEDv4A5ie(*sU$`zd@zL)eX1VHHcRM+- zHbgRtUbkN0{o{%Q>?wwDaETO~F)PL|&T{_5b-Ih&3a*ppZK#T1)+*q>Eyi9I9g531 zhm@5Zs5B&M7F(j4WkyEFq{K$Vct^&>#(JAA><7ya4A&n14tm8BWobs73GFw8kHJXA z))Elh;kvsWnwp`^_-SQb-AcZH@((D^qsV_1-0CcU<0&;|iySO<-Lz`e=FX0_$BtbP zEK72z&?W?~Qp^tez2g4vByhAdUMr>o&^$c5)D;kY0gG5+E)2|Om^JXHKx}ucK~7Fm zM2S$ibUODOw`~#Yux$`qaP|87a|DUREc*^yM=YUId&d<=2dX z_&oJ9rwS;*L6>S^b&y#Bp^rAwOwmPLDlM7RzfS30_f)k7HigX57R&KCHi`eW7r(|f zuyHKzIcnR%C$Cz)dKKFfAm=~Nzu`~1&Wd_s;;gy~I6=SWJD69~Lz$-c0$GINM-z2C zLhc0BkRBR7lbzrdqSeO~+bwS8WNuR)g~3zu{h6 zz!o(P$eY*&-XAA(bm5z@e(V=)*k@puC5+KaaiJj<^uQdH3c_#k0SqPl;hAYXpV-oU zAcM+SuSgO&(ke#1Qc| zsak8|#V+!V(}j#q_;$dw9D3Q8hCdoUg3So~ui=F&w$WoQvE82gtk3malU@C66ehe+ zU?a&3381;*SOaWP_V_H3Z&)OG7BeFgFESoawjXTj(Y`3QTV`OLGP^tKwRoj3MJkB@BJho)kOPA#>=_YA%|tA+Ev?+CV!DOdPR2vr_ztiBafFfoKK~s~8p8 zjS8bNeO+1SIy&?JrFeKg5H26KlPGO=iGO$q$F)v255HGf+QZZI4w43*vvDLka^3@tpwu;@dDA$$i6aU5qmPvd075y7{#e~EkP z^s*7-fHrM{Ck3O1m(HH_qeNP@wG>#=rprHRs5*7dD&FwU?}&4C^1pY=(R&yGS>RZ1RNqI?jp&bU(2zYIvWcxZ%M-oQbXs>XcDs7) z#~+VW-`xH4h$Bq;P<8@R*yoy>99SpNRH+B0UFQM}t>Q1#f~Q!lp?`xJPuCF`8E!laX6Qc3P`uR zK7T`%2`LPdeRA3_n^fz&uN$9^1AkWvLIh??SN_?us$+kxKG$!2?L@wpC1LJz%z^$+ zm=zkh28-wkYNG5$ybc0V{6YI<_(i}r5iTS=r#?s?F#!_Y$7bSN4w9@~H!=iaj|!t6 zB#euh^hcsL_XTYa^)k~H-GroZRm<@j(P>xqlqr4t(xvUDZtdRQL=-OMO3?UOz+t9{ zJ=Yw%tP6^EbEZaMfM5#Ornu8p6L|!SZiHjLh>&k2aP2bIAz=D_jPrslC;H3Y2X~ru zm384x%BzjIqqmf;ICT%+e0)wwj+sj*=osTVCnM^)0DqqncwJ?9-u?W5cn_zxS;U*&*Wqwnyg-h8;s5X z{M+=buoGZM+;#$3hV?8+90Fx_c`$WKbds%_)j;)UGsxh=W;kWb;FX^CgO!6VvalfB zWO0L#3(3C>Tgb0#b_BK%;+jN$CSxzqZzOSe+@L}Wfidvz`OL0cB$)ZoDfmnd7}>n> z$S2k{9~mGSG_;WOe->o?^BjIyyHg70sGSJon9$o(p|?~12>Ibjp?0w>aer&tc2mc^ zcdG7~zj!j_x?AD4zjYB&R)6NC|L89)TfL|moYc!K{m6$+-1T&p#JdN8Ur_u52=7lB z2BMyt!YF50mu~&9EM)u2fVbU2k+I1|k<5s&i-QPS9H_Vynm*0U9p-h7G6rcqpe>CH)Og>Yuf&nR)w zUO7rK`21(!^6tx?zx|4nsVl~^uUD<&(PLL|8QzVgBJ#USql?tf^?MjngwXk9IwfzG z?G1kLzW@hKr7~IO?FlnkRNp4lgOWOLIwes^7|dMfv5<|PWD#BmLSgHPxd6vZNIbO zOyjJk*JzLG2k5&qe8l~?=k^I<*TPOV&rp|Zo5drveag+fccXNZmuh2b+jMn1$QZCr zhR?Pucxe+@3;y@88lR*MR3Fs0u20{B`|jPHy#lSU%_cJ+rR^kGtk^^rAAj#QYJ$aE z+=or^^5TmDDnrRls9rdinXqn<49B22xI#ElcR~}QC4ato6Gx6Hd)FW5NWo^lm@gcCi{ApDiNoZ& zTu#;lZQ8phHF{G=W?sRUKFnTZ&e4dM-^Dt$-#%>n^p@SaSo!kb`3D%c1*NL>MWy}Q zW8}=8$>&Nsq}30^9qjgRDT(4-NG|vWbZ&;V1^iBl6_S}Hk>Y9!6Cn>GR#kply-xR( z6vB$?Zz}^&4p6AfjIr#LXk{z;Q#`Qx%SRs{dF>PDnU~PQR`}@Qqxq4>6uO6Z)2(B{ z($TeSJzLddoxWGpJ`Py&4R&L5QV=1QL?aKuBT>B(cTbdt=NpV1p4GJn`OkV#g!HadzT3 zd)V2|PMW0a?aXM?Bpn$|TN_=z=bS4sY^VM5Yt68H?>W!>KkFPnY94^f4iv;Vn#mKc z-7_AB{lhcdOm01XQtw&Te~4l5UB94x8hQun=_j9Ot%aj7*ol@5kzb$XY3XRvn}bvYN+!q1$(1)e|6z6M;@qn;9v0I2re+*7jF^M;m^ zzrAyFO9^B{oQGgAkHPReoxdfbs8Yl$gg^UP)hly%@OjV_@cN8L?2Zp{-iOdql_+_K zYc(v?%fVU3$RPR{C)27>`Q~L7>Rci2qpHx!8{}8>#9z>v8I6VRI}?}J zN8MET_^F%Q09T#}g1f3b!Yjv@8vvu(&;Uq&ha3CwVIjD`>K3hkcpq61GkC#10CB>Z z?km?2D1z=bSOg?0pb-&YK}xqFk{KiddXUt#L$#i0d7`F%#bIul6~vbm+&PcZtgzWX zboahR9Yv?Rp^Hcx$BE$`>k<8esLR;LLRB2-_ftJJPzFNzRB(C!pN%zkXxyTR|BvPx zC=@|E*NqJ}$Tog~{jD0W>!)(8bei{uW&M{tE6V*+xBbstE6O3*iPh;=up!OV2GsKd z^`)^jpP)#FF-PNNY6Hr{!pzxNEBY<;<6)kSMvk#E)&?ShiShU zh|MjEo|pD>!^_Ye^~5^rwwnjqVbc9HOTTXP_w$a~)l=<&fOYdUkDwmuo(}th95Cb~ ze{xeIju*4#)~zwrdS){*e9P__RF{PPpjc)TQ%1jowe*gaxoZT`L>xY&?-qmkun$N} zEf7jLQ}BlId{A%05A)G`Ht2xOaNLcjYEm$=HhL)8@ft@t5+?xY|MuE{%t~a?rGN;jkCtAHZ9vw-(atm9^e@e#Bz0d;bnt!Ks__gMPr}jCI-FupU+*2OqrfgWm-ATj@Rowx4hoC{o5w#kD3NESyY0f!C<5Tje$0cWxhf7MSfP_uayA(0&CRIY!ag z23CS?MEyu$8;K3rHtxZ{F9+L*&O}2t6S8Aq9UsK<#HB8yGm$X1bMkuGB7!P#Syy*Z z?X+}$@H({)=@@j-UXP`%A1?XBcGsAxFK|5}5bH1Y`|uo>G&*Yvv=lb1tSevvgwg`a zL;Vfx&GLCn#Nrn)d-MySCtyYfA`=`9jK4TZNQ9Fe(?rO3dx`zC)GtsmfI@eX*&T;S zIOGaWV&wx4zrZk}eY&2F@^AQAE)UB=HqFTrJq%9-z!khiF;R#w;cbNzG5&>yQ>lN! zcUFXs9dD3{@TYmtJcaIX^qa3HzeCS=t=08>*IJQ*rqhdHLE#k0Rcn6tU9{?V-(9`# z*X?)N;eUHz<-{6K=n>ur{1GC21~hQ4d!^rk?~zD(5T4|ppw*P%KTNtmP3ajZi#W&g z@r!A;qHC8=5Mk8&a8%#y3(s#MPVc3@B~oZ6o93^+x!+FgB`(1G{!O^UpWCP`+T1{r zANw+pJ-2sZZ>TpKg;{GVOHx6sEh&w!3%@?^voAX;x^{4MiiG z;OlNtIC7HPRXYNI&i)-u?-lm_ra1)@Kp^e6H_Ru{Nd@0klL;uNj4;BTc%CdD+nHw2 zua$Eppu3(hI|K3Flv^I2$DYNXgLupBI-GefC6AHNZwL119Hs$^0nq$Dvgx}20{UG_ zi!B1g==dw++;$l3xdr^E{*6pJLUsvc25PJQuQvTN5LRE}Tj&tMHl0=KwCQ+)x`-ad581R{m7g>Of$uV*9F7f5`m=*>!U`U_&|LWn+zfe)6}<-HET}%B+|hA%(C9T_F-uJ6L)ZgulSmJs@{@IA^k*?ckree*XN+thV|2l7tZ3})A_5v`>~0V<a4`18y50j(G4QoibZ2WRXDd9*j3=ZZXn zo{yqG_m(ID|D0sBBR2=F=JA@sn6q2I5$ z2KN%-9vJj~{CT`y)~9&AD?d-~?;3vpUHCbeUe5JS;?G0C1J6PHVmj9gNDQ8T-}2{C zKRKgm`E!WA(4m6<-A&I!dBj^ij~VUyJnKj2``z*TPvGC<`ZJQ3HQhK?etHf32hXAB z#b|nbjOhV&Aox99bE4_-CZ-3}oR&UM)8ps(`71w9(*q31OgYF%)(w7xilQA zU=)Py+!r{9<$u$^g?|dM3GRKv-qxATq5IVEdO$8bO!PY0C-FL=mk#Jt0rK(`y-wnf zc%4tup9lU|qM2i&6tBA!FCs zCW-mUF=%-I;oP~}ntO-tB>5YNEdy{seTMlk^>YtTt@ST(qS=mtTEs$M51J(bn@`YFw~*|@;~+vj zO|W9>GYF05lIQWXZ2^@~YNR^ROo(&P^Pki|%Pvs2!zjVgcUj@i`OQ_MA=lph{tuDp zof7afs1FQQ;Ho+=_krE$cy7@Uw$kg|&=+?7lV!KVRIKO^L-VZHV4pbW2*WNn?rs@x z(-_+cwaJH8Z*vvy-s|A5#M}u`vZ2RTS8aP4g+mtiKqQf!_N9#cl z97F$G^vA7$wvPjb_Ucoiu{stbS$ceS!#u~JcDt@&8l;r~3@2~h4bKXah0#HuVwi)K`Ic@y!~ zLna@;V7Vy<{bjr+W24>92?;ZAduu178Oj=7+Gd;DSCD73MS6P?t=uGrTX;o#Pn@{8 zzP2cx1IIYp7tU53HN_z4iR?f7UekN1k@!#M*%Naz>O230oC-CnY}y&kEvGhD z7()$H?;VG_)OXn(8G@NTj)&ZWM4?wB4N?x&B!GHuyI>8HH9&WQeh;yfLBJ$*E{fxi z=oRS$1E72|nPAOk9^X3(?~B~rs6&hTX7=mHd$d*d19w$Ue|BFRH5HmrJJ)0q2uw{G z-;iPWrPgB1`ViyXFV6LxpQzM_B+a~gh{_<#vO3HHNy`H}X76bdBihJdUH8GdP@cm{ z44O7D@Oy;fg)x4hNrZRCXR08>;H!7pYj&hFV%u0n!xBX5YVJu`xgj_mJXDJR~Y zGK3l{HlJ!i5=6bd%p5G&#}<3p89)a;tOGZwqZ`Cfi+UOeG!r#q`!L;H6cMAM3apg~ zK%&DaK6e}GF-isdyZ6699Xw7jUhIu4ADEg;t#VREw6S(!n3yZGKQo!m%i6@UzwknY zIohskR__03w>9s?mv^jt>hzY*+#tLtb19MaLfbop;iK+*HY8yh^|D1kBVZU@gY66y zmPB=h)MlZu=rXZ!m4wc(SzP(^!t-?aw@iSAuWz757SICe3)s=9rPn|*^(VB3IkWQl z*I&M++RQJ#<0sT*0kSQiCs~W%cyz$K6HpI7LCXM%xR4CIpw+?|FvY`>0z%*nD6w;q zaG^X_8xW&V!fn3#$g90WSC3Xy9(iNvWa8kxzq@xZ5h;-UH_-90iO~JV?w`43qytoI ztxT5?&T)JZnXC*eFT9<2cl3$Tl(f-DhlU>whQx|hf$%8;IeecXk41ziLy^xP@UKtehV;7@>dgS3Nq!;0_3!Pv6uCCcn3rYw~*6zS~$fkg=OIc7RKwbOVU9<=Q9sQZx*+i-kk;^dk?g`?~h5w^_cBr@Qj%S_b z2>5q)@eB5ES>M9D!NZ{|$AW3)r)?3)A@q8H?x!(awgB-FOaT)~rxdQ1e6{o8oXbSy zD{VtBQF|JnxO;a;Vq{vif%*e6no;oJrKf64Gn>0&z?|=P7$hZIThd!HW5YZ(!>=CB zDPVP{Bzg}#_T~1o-<+~VTD$f<+MU0D=X4^``kTc1jL5YdVenOJ*}~s@F3x02t@XVl z$^Do2SFzVo|G@MITh2a$WzR)7Y|;I1;i3&jW*H(FB2(gc2BI0_H!|H*n;3{zC=*Q> z{CqIAE<%bC6Uh#D$}GOOILgX|_dpE*daKawqTn_OFiZq;{Amn{ks)Gb{z%>a)j+Ty zaxJ4^=K4R=s|{Ss#ACl97zY-KlT2yBTz7@gKRnQf0kaWx>xt~3SWSF{tfslWy+cDl zd;z>T2DknUfGtQ|laF};r!n1Zl}VVA;TpMwif};@+GGRlfO#6i=|@pdri#HBxY<%i zTU(N(1=8Y5>dllhb^U+pFSuJV z=g_{Lshrplc*vF5dSzOcAbYSZbyKauYVP>)&?lc=f82``;UBFE_lS)Rk@C=;m9oWN zx81Ne$N~M7-<1M1@&5^*>%Z(-9Op3G<~H8oaTgxNN@Xr4?MwcaM7>Sy$5>xMO&sA6 zXqaWYLBlr43Az*vq?@5);0p)ft|*XkD1E?Gl9XTr7zt*5Z_01~btl;+kn@5(uW9N_ zYfgPU{Q{4A>5gZ|Uj9f2j6J8)r1 zTJ@QOdr9Fk2I07jz-98iy)+hqT&>e!E`67qp2J|KMz_(Cu-R3Yngl$BFurCVw>X-yjq)b9eQmRRD9 zC*iLwHATxZ>*DndnK4v*&y(L1e*d`Gr7GW?TYcML#^i_8-;cDu_`sp=6yndlah;{{ z@eOlD6=OwFQ$$T^L~>nPN@Kbx($Z;ZKGYVcZQ5Ov++L*j^tPKb1@(y2;&cOcJVX#z1`?WQ51h#P zcaNj9=XPUa(f+=SsQjTDtWN12pC4?zYrZE}+Cd#ifMJWi0wF*~Ve?oHJoq>ygqrvvbElu{TMIvIp*Xcedf;>8;I%j(a}r zd(1bZVI(uHw^$YImtNM?SY?qhJ$yXPRvLO@R4tRlWbq1WwcOyyEPJc3z%OND)4Bp> zq@`oq`L?5rYu?%~VU%pRqg^G-X-hI1Wbu-4q1@7%&CwYQ!EKItunx2=xrYI5A!b?~ zPyUyf>4tcczih7}5vgfAb%Tigx0vZ7=$il*a|1qd{wAMKk~ z;x8B2LG4O<8csN8`jGEO&Xw_ZgdbdP31U^p{VrgEEQ}LaprV-*>{*NLzoF5ED`ZC4 z0x;y~Y#!KQ3+#;}2G5iRkU~YOX=nkj;xoA-mmP+83&49h4KT7aWPL7?&@h&1>dK9Y zubaxfJaBHEw&L!;-j;uCezZ1$Nwuo7%B8x>bcwchrttBp$2RD?p?UP|Y3kcI2Eq0g z_E`mt2wko^u_RT^O^U1^*pkzDejc=0*PjPhhfh#|UZXsfO1uFY2K zmMarZxnLK9T-;=1ZpbtIbaLSa0dP@f2I~HRhk}4Mi9nlV?4?BDx}(wXmF1iBn@{#7_g;a? zqnD{~Umqm?oh!(-UmD95Xp1_x4FAk7N-B*TVUWevs~mpdT9T_zwn%FCG{e&6v9~)2r?%H>7rK$HOe4uMl6vtRL0oESxkN3Vd(lC@c#c{`KM};a7rSDMd(zT+iW%n=g}oUW zy@k+lmFH=mdYSs>)!EFoaEed+TI=*{M99k%>1*@h=vwOT<`~HQv!ffR27)}-5Y1jV z$Cfv*As4%0oo*l}aceSRl_>Xv+%m+4bW{Q23~(Y^%5fndvGc4HXQ<)CA#4sY?S;(< z;RE{S$85X*i*3_?essOIa&+IBvdr;B!m(^+9E*sW#GEL-J|(%qBodkGQzj<}h+@H? zTZ_pN`=#Nel+mZgOLue^#fSR)KjK78r@_8Y*UuD7B;`BXTX$E>?s@{rB3(QB2b`}0 zaeLJANd>G(Ni-ZzY=F=_H~_3Wo9Q}unXo@k{rrOs8$KlD#0a5)?>?YDS#|cZW23$M zDjYQ1O$e`ccVDIc@WdtRk5_vzZ;Juf*nziF@UW?LEsoCse~NOC$de;X;|$dZCX#*$ z7zxBxj5I*(lZj%|BcG+Rb(_yuou4aG<~6VDQj(v&c!6L&-Z=UN^*B{YUHEQ(RjEth8xzrQ6laq~0T zpC4~jdn`Qd8P~#WbBjpqOpo$`eCyvBzb4nBnmRcP`ykeYGpr;D`|yB0;7ihhMsDF< ztpx9xD~u1zKiF02{#)v$KKxlW^BZy_yB#DOdgnz1lvp36{!B30?WkiMp2cK-#}KOuX{FT}eDmy13y$8z>1{ zQy9Ew$MSoKEd*43sRx(dk)n;Ndza4&J+$1!&S*(pop@PIBO^77aJ6*@u ziUM@C6FKynYMI|ME<)Xv1$06vg;V=9d6xMtA$}Uk1B#zMbC*M2T@KO*4oQfQt;CnY zz;aN0fQrqoZxPBtUMre$H1ENU)DbvQmM>z#MyEnSB{W8)fI8 zrTNef>(~M7fXd|ZUNO62do+4nv2*qg>f8y(wpn_Qt9MTCnDKkpF2SzoUAuRDf_Mt` zO4dH$IrMfSlngurSO|rC#7#s1bmb3XDf3^)DYh`Hq7&!|tYic)d?gZ9_q36pI*!8= zElQv#GsKSOJH1{v=ubaL_`+tEX&Qo~ZuCe9u>Utb7f${UVqS;4C5Zn@NqpZb^Gn#9 zH|!0zLcberjQORbmyEpr`3cwwQ;O3rtVme9Q_h~Gn2 z>lCtO7OWOB2TZn-dYS?urjzJ2XX*NpcqJ~ZeTD#FX(_nOra$( znNYEqh5>P!DuQ@0KqlhsGT1wRG+}$*%4TBc|AWiH*i@V#b-ppaAT9*RPW2xCWRJzN<6R>7-5nNU6A~VI^BrH_*Qe(d&O_H2yl71d z==}+JFX(+B%?<3amuE#k{RHdE$j@>aytNdziAa>9^XM6y54gA9r&;1!6 zEbKy0baM0B1^Tma2cdf}tOsRsR_cpODwfofUM7!lDG(fw_sOyf-AY-pKFz|-HemE< zg(4I#xv{U@#83@lR$j3QXv z`g6bd@t0>CGDpufP#^xgNlRp<_p1o6hYiMs-$W*tsFnE%f{^%%6mevln)KWA;jYY_ z-S2GaePUOg{|7{?CHJ$~dX2sE&9L9y)f*pMJy%h6c)Bkx9vlU_FR+r;2{b~n5={>r zzR+c*6+VLbIg;D7GyRI{lI(asC?j}=PVfxaOuo@j=_N{S+tb;~qni|T4qAssZzU0w7$gGG^fOj5+XA_jJg~qnqe$zY5qV$~OT+yg z&SERpU#<@WrKM1qB=v#lh&G$dXIw0=-P;ylx97e>j~HV*zal@?(A>!NoFLhH)V=E3b#EqwC?PNRHlC8mSfdeN)SI>{seT0mhYQ{)#XLmI~HENr6WNc zxwRWvPykO*4*xUOC|dzim;ss^A7F%TT^hH*ydj-xK=i>Uj?>mGil$L2L6huklA-0e ze#hCTJL4(~G9#xL5iz>3l=Zdw{H?z~S4XU*BQb($qbll7_74m2AhygB{5|&G=XaN| zyaK%cqDhzfoB9t|xD{@HW{9d?qTn5vAi0hES`*w&b2CW!F{WWX1M7x78qOg>dSsroC` zg{lZB)W>@x%cG5w05?H;f^T@B7j2k`&zCpqD~OXwEINM5;9cvM_Q=3Fc2f#*lTo=cgf7;U=tNE+!>4;UCD7#OwaRpyA(B82f#L0+{DSKqi&ZYo6klz~r!OZxC_xVUf?jmkRz z8{V_E7{4oj0^fsir`{*PjJI|DG{V0I{q4wp*uiJGGi`9HEn3pDG(up`rh8f-+;qwT zi7u9lh7Tdg0j3s$QaCi9?S|4;(Znd^`(<5^eL36q!r|KVo}K%5Z^&g6K`1ZUv5+KA zl85MPhtori#Mf+vwN|gqR&&>SEA&Y`Wmrgjy;VvOGG$pqZ)VBi0gJljaC`Bd>5V<> zI@cRn?jd1541177%a>&57AO=NT?cU{5kya@gGxcPIwLVjnvxk^q)@gEPGx(AxMb*U zkRh;=$k5>kg_a%{uyocmV0Xx3GJXir+H^hbPDCduC~wqPo*K@SnadPJsbfqL1sYHG zS_evEQP@HQdVOY$pK;U0@q#0BJyCXh_|QJw2L-3G+&QUuo3fIovGw zTG>~kkQIgsAO|fkY#aDr6fm(5aXty%PvW%6t_yFmD624#@bYTm}cXo0E z!WQriM4B|82hjSJ&OX!)r@h-Pow;x;dYmMGZ|@O>KP^0Uqsdum%pgZ` z{k#j90B=57{EYZL!w#oLK$ngYr~KF&Lemw$^lK zb?I3%{8@I!Onx2C3+GYapg+;RS!*1~3u*31d1LSsl$PfR#@BEv; zICg;g*ltgV%FB@?7i&w)v_;)y^m^i{>&4y7tZs+n+?|`|{R1A}|8jjb6J7huaPti_ zd>S9-ef?*?GBXhZJ_zUu_xiAovEPK)FB#Yda<0G-6dyOi1@y#42ouaEgYE!zS`|nn zi{LMC&4%%KkU+*s6qb42ukTO_nNaX*fVRL3UIZ5#*CVY7E2#0QBf`sXRUiIxz z%6FV&Jtxpd2S=N0;(nMI4^Tvf5vgsH=8@}Lzw!(Z3m7Al*ROZHCdz0^eQJc3Cs?Oj z9QzFvHAgKk1p^F*oQRG@-g)WxZuBkC7B7z#(a%>KL#PWA)ZU)6Jq8BDBSbBOwIrq! z8E8!%Q@isWUyWV}iZtu80I6BF0}c=*`v%}Az)7i-zKv#+9z z3Jb$}WEK!bXmsvsI|Tp7BSg##vs_`Id$_SIfqG|>8W?xn1tTyO6KO4Z@j_q9Cn-SR zT3p%^=QF|NIj+vmkcpF%*FRI*+6PAxeuef7yjfgi?}9yZai!128%R;0Og%D7BIJ4y z4In5>V;Ui22IsEQ%MnZUVt+o1!&1f6>~86;-rr$VG#=^dZr)uZC+b`8`*eo7I``Rq zt-9)w9S5^0$Lri8EoHvo({pbiGr2e@1(QfJ)rnY$f=Gk;uH=@%b>usP@AmY^x#6hCeJAue? z=fni7auQ@=GwUPAUuI`X?$p$^Sd_DefA21be-HUoS|TwRy75U!0CDWco??aSRF953 z?jEBCH#i;|x3kK|33JA9O?)L)&fr9*#)d6CKWc~9ytZ{GnJ_Ww_)=NaYoe~r(sZs^ zq}&d(Uk954GbiYc;wT0hMMNZqGYUmc>C9{-rkd78ans-=ab~tF(RPi#GrYG z3SoF<9Y4IaBboJ4@1?J%_ocLD%N7h{iR<<~@KVQzT`%9ayUjRGZA@5qsD}FY7+M># zreblC`6#Rn>^$N%CJ}0FK}|JKN5z`R5s}TK)W1y48Ig=(vTodyD-I?3Lqm>7N13%U zOQmWDIWe&@BrYZtY&{C|ow6Sc&-KRL8CcN*(F3EL87i##IN3{^A`Vj>C^IRjcZo24 zVLZVmx~Ac9V+U1jw?&SV4P(R?m9rH}##;$_$^fb_xlkQQQhD|rPF+n{S zU9r8{^=`M1LGOHUqzA==DAYj-@?gN?02UI$TafEzW5`0X$lj1_pkDP8s03FG$wWu~ z#Od-M8_rMVL7gambcFfVG{Nh6db^EbO0*D;PEo3wS5snX-dQQrHSVln{?0M5o%9-; zaQusi%p0vjG&r)j==MF9%UmEePBy#N;g47^mNJ&5GhQOn>1q0}B0sK>Vm%>WN-a?M z`3mF2Bf0tHZQeq?^gJWB>DYSeKkM%s%^aSiv)>wPOp%1+zLuSpQbo=7dZO*=E58X+)F zYw>CK%kXX9_QLxmPThkDom1b{v8 z7qQOiI&WXMEw#5UR~H_t&92{&-k;FhT%0Uo@Yku^POOiwDa;lt3yaFMuQWY%&z{ai zUBi~M&vblzf?!?gliPaIrQ=1~M4lVb#n^F>AYU3tYM;O3fr_4t&rW>2*j+`x| zjyzp5oG%DIbzSI5s!w0((r+-8J`ofvoa|dROwqm&{n@2!04AB^L z(ai??M0I`eg+9oaBS~FpxcCgLle8S6{eZBz*(S9;4~+eF>f)e&tEDj|!cNvr5MH5j z0kLD;BQ#e0SH2{O4Zf23vxU*JiL#jJ;?Xk3VQFS_!tmJGK$un*GQH3ro2TaqjD>2( z{3N{=_RH+oaDTkcKMLCgv=05UaezX^ih*x1V$8NN>iy=eC6bwpmUIy@M%GUV6l(wL zLdmDdQI9*$jgm%Em^WJUT(Gez@6plG_rfG0YpG$mkr6sJdQE|C2kk$4;N7Ut3|mN0 z6Q+w6C@+U>yFpK%u7CwlfUFAKq!=s-`wgMUZtfn4f28TcWbOpL(lJ`-XSW7_ZEZ_e;=Fqn`&Gb}(B}r6Ks;Ebc7e{=YK784SHvo$FTQ{cHrw=ye>B1kBIbT6X@nZLJCVrn%ehuU_~y&ycT@ z8KSrzjl1hry5`*vy|?M&Lj>=gf&RCtPanA`s;Ddu4~nx?Cq6X}F&khMw>sdfJ9HMr z!S4J(+2EY-0Boj#tvVx2FCYzcnKL-w7{_pk{;VHrd-w0@t6#WRpBoz(7@Mm<#gydl zdw<*hNynY`Q`A4GuQKP~+eIGU@&3MCrjl4oaIXKJAmFBl@Vq^5%}iY0Vu1z@#|Ov% zd`Ag&6{6|{LQfdaIv#1LfB5=Ymo0GD!TOLbpz*S+krC5$d9THcmE85u42RIs*VVQgts(qeT%5FN4t?ASAhcxnI~vpBCB7 z3i7X`qmDnR3%e5kJcZW-^n$$LYpBKqI00)Va%j#Y$NU0-=>g$L3mNy0d4lEn4__MM zL5w@@8UOZA*Z=P?1OqJpD08-DOQA?uu%+$#G}J+}!OgE(Tti*E{tQSQ)_b;o=^e-y zx^x^d#C2={ddEjoRKtdQ2NJNF1xt-WqH_X3+N9$&SeUfo3q8ay;R*z%xvx;kh#I9W z3juy)kF#^ZhhwU8(QBC~DReoWm|XM{Fi%SBmxTSchj2Wf6XWMpDq47oM3 zd~XjCG&V-P#?LXyNlzyhLo_zf`zL6>N()~g@~bSDAObccD~Y$ukMyO_I8IIyUVNRD z+6|N*8z<}71=Pmu;S(i}24LyO^6KnZV!;TG^pnw1U?b32xaeSi0N6Yq_y{F5FzLuu zVy%T}18A!U%eqR{x!^b9GqjMw2UtmxhS8(-DP31C>~1p*zE4D5s!H0mcWX!5G!+;W z5UNbF=1QupBkhF-URYdi!$5jn+F)~`k-v-70!(}JeU%OPrKp%X|K8Z|pC$OObP$4+ z>W%v=CMMu^mCT$hgDA0W+p#mY`%QcI&UIw4B#Bv>DFLr+*+Q|M0^! z1an)TkF5;|^`+jOaNIpQ$~aJPcql7l!_ji8gwQqLF_1=1InJ^^4UFOkQrbX~GHl`8 z!@hE91jF(8=qT&s@fdT7>~HESOU$bxQ$xzbG_FHB;Q{N*1mSRcS&W@X9mfa8&YTVo z{Yu3Iw1=eti3th|QKFdbk7cFP`7kY9!vuo$M?$gongtl(iZgbJv;qC6V1l%GCC3snC#ZD-_$5 z4OIn6BJ%v$*r%gJt>b6H)S99BBB!130zCnrjB*M1fI%0BonP?^of!ni^h92c!wV7^ z%~Qk&5X{ZYEZB%k_dhu0IJ5b-%(^M20F2#RB;5Alcx#+GG`;;XgmltOghd? zQ}1chBE82PcaIavn;ss9qvOlFjyA|oBkqs)57O#hQ}IZPucjll6eS7nVYfmEL3 zU!!)?J%6#!@?A4|vXG1*U{mI@7Z# z>jKsrwuNZtzGK?F=}hC-8!k;}!P)%blatK9PvjOp7^)NoOah8|&@o*;lr4~zPFAo8 zN6!Qq3f<1)ky&waaV%Lgfo%-fG1fnwcFdXUS(b7XbYn>eiZPB(dWFSAP>-Ro0N680 zD0f{R(Zf%S%CA#x3cu?^5cWfs>>X9};f}i}oV*GhrW9U3sNA#WP z(piU2loQWV?|#kK=gM<$J22gBBtExJ^j4c9CF#|jLrFx?_~c83a1-{lz< zVIHA!(HlZ}h$lg;C$nas*lL9!~G z8Vpw&!yQvxMVjmfSynt>lw2A|Oi*{1?CMC2t>4qM>A}n7wXqQMrg~lZP$rM~#}p-; zAnSotK&yT@`?ZYuDz2X(B#qP+X)dn$0@6SmL=uDqNJI+`gIQL6=!N#i2j>cf)?Qn+ zKrQf-no8t`Y-Ipnofs~*8bpLAPn9B6)aGl5%NsBB8w@>X)~5}&l=8oF4~f!=Q|eL# zsD8kk;4eKKDx(=~Fg7f2$bR%jb(}ExfS`p4<(Mx60 zLnfd30u)*6_cbsXL86!-ktIP) zBzpLByh7s@;RKnwr7w##H5~0qNLY8cuJllUdQ!_=j$-zc6XlI(KHQWxv8k&p$%`ZP zk18)OIM4EY3rcje`8 zeX6(Z(B}4dc~M?=WNM~&m^^~33fV5}Do$KitX38GnW~Pbu>y<7?xA@?rLrGj4gpV~ zJK-jt;EV?_$xWIDLkcX{@?kEM-o*;x(z(j#v>jp8&C+3c*D$NL5^?<#cXNO)wBBt|QW zH%I&ETlQ3?%#QVzXvjOvy#?~f@_oIv<2F%5)=2I3>$<+$L?2mJ;%nh@QHVR=QrtRz zys`D-_9B0tG+3LIGylfS@N;vSMcW^3iLWWKMSJ^sFw_k@t6T5fx?aYMPtIu?M*$$z zWp&K;%#HY53|encgAUWxD}mZpS1=p+xzyvrVXn8#y)s?0f3(RgjGQS2PtRE0`yU!hk2Y7gck9wSYz7I3+3p>iULL=9 z>+}S}Xv~cE4vI-v21i8-{eQ_RtZ$0$I#?T{htw@vE0V!sSSu`imBg*kaj;Zy3jp2V zv$Sp`hRe%_U7{{3=5WQ{Cz^-fK3nE6p?n%DjK&X05E|mF;MbmsfAE%Ns0D2@1`u?t=y+WS5~20Qziy z0s19fgB;7!;%Q595=1WY4 z8Hx?sOhr(DB2#B6SNjn;WWnxNM>k*Dk(*O!9jS`5^zF&(Jl3X(lTG-g)QlDBbK<3( zjLxhmmNv1@6d9ISZ?;y(1^TEe9U1xi2dtLi({=G99cEqguHK`++H1+$`S#Y`Z|C{u zuA8=QIN1_w9yn5Jm`L!>pJ_Dw(NAX9MwSd5rR^g7Gtj|6+^cfKZlkct2u6Fsh681a zR`Jvg`uAPgL57(ZpR3g7YD0olc}7*PF2bR#Jow`JrYEcdn=Lzh!!uj+Vscss$5Z1J zGP5R(6PvAd7saT8i2xV}w(WV%KJxM|8=2gHtWGAYJKEcKyiqQ1IJV)?C%Y_`qzIKL zz`Fb6gAJ#rn=?cgGy3-0&<7cCO=qMzr&Kfl$)Wt>BhUu!^g&}FpE2+QOh<_IX$Y;* zCLcRi)bfVi651AXZ2QhfA>j1(Qx>MiTV;N%V5Ri(_}1KQFAR^ov@4%1eT;g4V8TOa z-B7c0_;`&tqWskChMJLFsprHHA%D#N_Q8_E{cpm1<3NWS;k`vmdV!nlR{B1A9S{gY zV%51KH^vfqX@zL4Ir7SSqkW{ORFl7HcZL)aZ@ye@f!Ha4+xm>49ZwHxgO%3$O<7f&3+0}@993dgY)*~VUtYDN zUaM)gY0d|VwJ>@mjOF=waAkaA?OGoXhO%@dPgj9m-oy@5r71FLY7EESm*Yb{;|<+ZTy>@@E{X3!bPaO?NVD%2Iz@UrMy4zHc-&x+vcq8fwn7MM`WrCT`r( zy4w5aipU3Qj*mB(glFU0kF{6#BnEgyW~Ua%<|r&yMUJv8K3fqGpvaD|;7X&SgU!}p zDO}W95^st2O{yEve@wm57N|+J)%BR<*-5dHT(<~;ACurHEUGs&Dc-96cb&Jk-!_mJ z8_11G5D5Wcg(*`bGbVUK(A#Eq2$_8`P!8KAaxCdZ0Kel(1*opco>HN_kkAX-x|)9WRwD zYv&t4&&V|}RmgnQjRmzqjHy-)k}NG9XZAqN5Y9rH0Ope3qf_h|4Rw|3%8Y1KnrhfU zlvN#jbK~H1+wwF`dmGG+R!u-?VwyGD%1tY3NtBus)l!jnP(*+`!MB)GxLwcgDJ8R> zy-(hBtaoI8LFkEty?cLkv>?^~#3*BM-Ju3$C@(Z9QWaJ#uP@LB@g@AAKt+y$n&${O zo*7%-K(=C$#l9VMwi?Sf7z=1B0WSqM0|3*K$+%g}xk2>dRHz}?W%r$e%)ULgWY9uJ z8{^`H^QzL~(%7y(HZNH(&(uZmBd8}MIK235xh6>ns9bEw;O#4#?@YE#JwBkTEii?I zW|Xxhue;xFCJQo-?6XIChDF6o&Sy0xhi&J6_gBkkb-XaGF4-#2jWL^Ja^+SS?iXq* zhBJA4Se~r3{_5nllhUDM&kk&Q?La9{XO$u~DnIfjY!5In$=uKW7#wkKjqU9)3z&GHO^N(hlY& zrE$_nT0SLM;i4cnZme1H05ep~TN@Z2DGk2ttZ zuiL`d*8gq2C zSrd`7>-DLcC-2%Oy%bY>u&d&5Z(3MNdu~olfz)i3+GIKTZ7HG8zXU;p_L6w(Jx^C> zKlZ}&_iTycildYwPIhUz74_8Vx9&xLAvkOXxFD0r*_$B9n=i3g9F z%r7bqywKJA#Fkv9PYB01(35e5Z)(fT8!1(m)TI`x0t70fO2A3%EXgn=sSR)M^s6>gd22aWI)$_Xy>< zu<^BXlNHKC2c!jA2?3?L_P8)jNMwc}nG{ArmXH@;HCfd)Um}c@dh7FQv>6>)5;i|Q ziKxkvWOZa%d-9~_GNRJKtji^ zyPj@(lt?(?u1qwAQ_;bshp#=hwW}}j@LwLEDTyvV{q?Ed$Mc~+&JiROY6Ni!7PYF? z7+0Dk6eg9nuQLc*Ht*cM3(LBU(y?a2wr-&D5Xu+MDozT9wc&(P3dx6o*1s@Bj^q#+4OD4!D&9wij1tvbFzBl8PXCt9T?JKGWr zJW&B0ed1; zLVxd&NKQgkia?k%SaP~xQZACj*{W@Efx;M0aEu^OT7O~y`F={r-UjpuT(c2L7wv6a zIf*3=K~~N6%Ugt1P6&?VZwb<_G$#=0Gkql~DuYGp;~izu>r7%VMsL~C_a-iVz1tX( zKiA36XwOm?CJJ>K60s~H($`CvpbV2sr6HtuM6ge^xvpn(>cA7TSy@}28;&e0vhf8u zIhL^A$7gfYCHbmVwIUNB7u=d3n9j=_zqd7a zq&m@Seax0-YpaPT%jryG#0ry?VP2v{WnxbF)`*;fe7g_E~Z7xX? z28UA5gm6ORY&uPPw4Xe$HPLk7z_!jb>R*i5mgC(l50)?|UFY?w>OgD3p`pyY?N9f| zHjac$97Jp9E-^=>nX_Z&y#Re`9^+>E!u33w#D#``6`aIV8~|O zvZ+lX%gV3Wd`Gt*M?2D6?gtm?6~}#$Z1qx(9DcNO;^97RM4UNFk`c#G9zv-ephGe6 zZZpK0sfc%La2EeYckMc74E3QjJmA2(jD>oq6i{T$*)+7|^ zxYBir9hY_#Neb+he@`jbhxkVtqUD82V#1oRNL^D`3}+P%6sWxVy`;L-$cQ8rUz|~! zKum=Stiz{k3kDl2wYsMI2CZpBi8?GUTS@#J^fCzi)SWP|9=HaQnm6jD6WC(UgfRKC z{Q?JuRG5vj;VVVY8QwA4B4HZNm?`rOh%HFc<--|+Cfn{e#>@9_s!0}Fyiir!D9<^Yj&c3CaSqAt*R8BQvsiOM{NgtlU}?8&@||PQ`P-8+q>1>O^rMdp06FKTl}uN`o*Y zLBLB?hDX+&hE^cp(VoR0p^&%3aVr>>V=e{ySr+3)W&K{*BhDN2RFv?bW(13 zYHK$2jen@Wd-mMZgLQZ9>MxABJ-+wu0oz=YUX)a%cEqt_d(U?3WjU=$@pT1hT)b|u z3(y}wgz`1O9c&4~P=XYJFZRbC+6Wal2F#2too2F(aREPqgQgKKi#4dN+_ddb&c4si zR7lebq65jf>PaSX3tJ7;UW2$Wlp57u=N>K-jwNPIDC}5{RGy`x;nRGT$xI|Kq zGB%7dNqUW5o*eBTq_QQd@^le|*|z(Y5jZpNpSfEeUthRgCs67F%kG{nCf(*AY7r-| z+gH|kv_&l_+|oXFWqURuBO=yay`v=|mKez{k$LVkv>&KL>#3#W%-29iLg5aXmFodY zvfTidBKJibI~*2bc(>%>m@AUFZayqGF`9vb+<|P;c)ysrB!>{CFy#Fe}FnAM^QDJDoO!!rgBZ}hSBk%jwlab zH=^{&$%h!{iR8` zk^QGnY*)9{1;+%fd+`1z288Qp4j$N%F#Pbw#Gpt;aIiwe;U(9A+#(vrGcN*9gVsPF z$kGUyGgKy`5O73t>GJF@+(d&(fKp52b!jfofGiMzOnk174jc($LKGJ|oyiEr&>srA`u640E6)!ar0R z8x*Kj@&g5m5Ng-=dD$H$S;g&{GmrLbb7n5~So%xjJ)4443R_e3B}N{&VP*PIM@><- zV52%YvAi`#v##6_9Hj}%X|u)#O0skEWxVY22JP0c^!ll)*!-;Q^3IHig3?ma3qLQH z9fGrQP>k_+Y`alDg~sR0pn&BfCp~|KrP6?NoBoiJaylfGvh}X1us{kqSZR_gOVXps zPwP&;ziIG^DXVXI7-xQJe|mdn6gMH-P(P8CoKYSh;Zc5mTN&}}Ixqw`~#Zb3di+*h_wY~Yt48>Rjb!1X8|tw|4i#$T?Ak8sP|@haF~$RD!a z0IYCm3EwagGnZ4WdZVjgMSB{gV30_2rHsJqELC~9X;3ktGcqfGjU@tw#Y;{4QX`+i zdP7^@pQ@;;uBxnv5gAl5LE3>PQ$p{Vbt(Oox+gi2@}OvomKPGG3}iaOdT&+9BZB2*b_H?x=!K|7!k=~9p2C>}?^4e)q8Bq_Tb)DdAdke$PzkEj|& z3(CrH#i@3r*v+5oiqMz81?zshF(zefModsO z5m4sIiVt)z! z8JmF`v?TH1NY6lg*(DCnNe~^{#5k(}D0#*SV$wZc@d3+)IydmV&KMZ^0AIqNKEV)6 zu`fZk#>5GtIc~ryuI4a<#o#ETxS_G8=rUe$d6`+J$|{u*ysXri7*np05agNU)W4{| zjlF%iG`9J8x2`H(5?`~~;>-Vkq`e7zR7LU!KK#OJd#bWXemz5S0nVea(R#s0zND;w{7c1Bdx zjA!o~^T3*Uroue=rtk2)*yuv|0A3*>p&`i~YabZ3;LN>+rR!d>>OXw+9n!ZdbO+k% z0r#nA=u!7hv63k}6q=fLFx_QvN=AL*VF}CQCskz*iJUsEA#B{^OG}3MNBFX+=m>j% zQ}l@0ldAMzB#muPV-G!Cx%uVx)>pQTHaG8>Xz%BVF{NK0)bqX^(!gtu83%-E~u@A6k%8w)BxPB^~3k-I|@# zORIG0O+_$fqq06sYslB{r9N(*L!yuO_2m1Ft$a^lua;s^)!1TFJi9+8Wf+@nQBf~x zTj9NDJI3wqtj`ECP2JHDLkl+k=8>jUi_Vd`1;_dgae4K;yNh4AkE3bStk#*MOhxl{ zl$LFsTje7E66%&%16qePzdj0C=te_FoOHH$Cl9QLcyvh<)P(OJ}boJt~Y3{RK z^ci)z1&vvO@uS*u*pP(!{AkS>7M`y`u7aS_iJ3SxpFeZdn2vH?NcoC}WE_+C^fIJH zSgK8d0b$uwDiX()q`3K{jhv2wQ6yWZHp13L;}jmngAKTuF4@hX!#HJiIKm^$nlN2R zxUK5gY@FP)gO>~-ZD6w|7{*!CgvSEBpM2b(CAFPcy!^c#m8_AqVR|4xE5BH_>D7*V zU+hTsJuExT+E%L%NUk!6q{aBjYYp|=XTsTt9##-PRD3${T(W#*J)(RwGO*>Z_LZ~3 z%k0fpYPU>HF-_h&{=YlA76+z{%S;=Y;6>J3%B-50;LMu+!o2LfjFr^qWRuoMy$5{` zE^1GomDliu+tS}$s(9afO?jnaV*qU5xs3#(uJtZ zt5}{cts*)mCD;kuU6P(@zJF5e$aT+7&6pB7xh+07%1v*m4l1oEO$)ER|CO2YZ}QL5 zL^&z0Dl6A zR4Dt*bI@C4Q&X8d8NN+5R z99A(see9pIyhkrPNcz-02kR_z`RGOV-Ws>Ex|()A5sKT%gHwH$z@+VTOmCsRqgHUx zm*vJ)riZzOCghA9U!7-k!5u`dK}o~hQ_~V0{vN-$M97|O(N%1Eb-~QD+iQdyhUy9| z^LdAcMTI0xsWkd$H<#o$SEMBjODt(xQOW9Cv%IlOIHtW~Vo2PCdzO_q?{7&>p1Pg% zUsV06`?|D7dJyLTgXx5aJA8Ra8=T{~v(Wd8s_&OS1(^#f3loEb6ALQ~%t8IW2?hPW ziqBW%nF6t*ru^yB*uKIm4K%TDZC^PO@g%5p!rz1wS{I!2NkSKO4Mi3b6i!+|V?Fi& z)9`9A>B9_R@BsoDW+L9&L|vE{PopNBkpCi|j;~uj(jziF$lh*3d3#&IlQ>7EP}Z5%BDN}i)G7NiPh|AF z!G7%gBcEqH z@(^gT(=Z#-r4E&p$*|;yZZC%Nxeu0AlENuQM^>IrlZWbf$HTe|7rx#Dr_7Pp7BM^a zP{qV?BhQupwG}qtYkT3-B^&2#@;n@)>i&A|g^IBkm%RIrZn^99=Ec+I__j}ayM4pC z<@e5>IdW{xME2eXPab^LT|Qn>!ID^<-Mi0s&VTNy@{tu4N1u51ME1P3wUhrD7FI4C z{kY|;hBnSS$$mEEWd}+_=GI=c)>&5Rz^fw3A(3x^vDl*S7gzwaPb1 zyO?|HSM%Fkm(AHJI?8TutX?O7939Of1veHM9W8&nZZ)VWx)sUm=hdfnN~J;(ra}=y z_PD)Uw(K2;3hWkkvLF^OUy#4*JpYf&=fN_Pame4x-Lfoy*O#6v?e0~w7b&vgqzpFk zVHYfJW+Qp@5PV1}<%=&m*+lx!MwJ&NAK?1SXk3v_fL}xt`DuoM+KaZ3o*1C9hwji( zFpV|Nx4N&0uR*Rx&UNtD5DQ%MaWrTHsK50el)W|X>jGEK~K7xoWwdjh8KIJoMqLjCH^R&W3U*Dp_ z=FNLO+#^1m5H~E*G^s2rrz0z;xWQy(Z2XdylP4iAa#0>CG)Zw-%S4+net{a6;?OO6 z51+7`ZB-*%TSr#4$xHVL+jk?Usr+$OYb$^)(nfxx2bveU69nC%J9QB!Pd1zQ%*DD* z9BI9fJSa%H{_SJY|17`p&A=~wW4F-X?fCU>$ZJb|;Fx4^*NC)0z$jyd^gL*SJG8JF zt2Jatu<<+sH-o@}xUD$*VF3#~D}4D9n_D0kvi%*M|5!2qN%nGQ=glkg<=^G$H{^{V zrO?(K<+KlR+Emt~pzVQzl+Dthhf_+t05_(7bPwIH-F4~624U&_Labz1bo-q}iv*Xh zh2l}1IQg%zL7~gg;Tt`i)=y``(HUN&oFz;20SjdTX$B(S5U4 z!Xk^R#O}T7FzG(nV;4FPVGqxmNb-3jjzBPOTimetYe*(@j5^RJNv)8^R@B#>xgmc( zcI*o`HZi-RGYim`myE_EH3v#xUO?3{8V}b9SL7vbIV!G5lWr4I`E?m5{hKQ z^y9t z@g-Sx_GKLR>$?5HlaHUrcu-l!(hxqkd+*eudB;wak1QW4e;{{Zc=@m#HZlXcvrNi| z{SAdGd8*O8Ix{YE)Jk4(6xaH~y02W+ef-GNHySP!WZ)R`E zCwM-l-SW*--|TIYcMv_SkgLA|y$Ehu`4yP_R?H+@8U-3I!(eR@+{Ey2LUL?a5CTAg zu(`tasL=5vC#?4Lob#OU;ql{LMJH1HT!(}?`K3KCr$4$qx{V#^GzG;Q%sI);f>zSF zCy!{^Q0iolyijzL(G*FH z#p=)qpxot|gbcF^V}v1WuWpOp{wO>De43wA*brC0loP^Vz+%*Mb38p)PZ&8qly!DC zC+CW*1v*QA_UZTXNaMNtIcPib`G9uYsQ)0@^XHUX? z&9Jjaxy>4m{7vqVBgTE(IcpmId+oO_L~nk5kFa><)@d~lZe&|my)4TgU*or5IlVTk z^(y-iZrJ7L|9$ZtPH%Phbm?`R_aJ&?rV$r{qlII3lzM_}7JXce*LO`CeeoUS3lnC) zHf#S8IigdF-6LzVpIGwD^TF}wCuWLw7W@)xGD z(o-yEp}fJPIDh4l(#+KVWaumNk}K-W7*d(46gF(8`WC;(3#Q9tMA{~ej*OQ%1m-Nv zOJ`0pg}Aw@MJM^%w_mk)eA?B{CbB^OQP?UU5Q@I*-uhZD>-eqqi(h`iC;5K43#+)G z4g07W2AieB7o&<66-iAMCpFA~y4c3oWLoo3iRNu=(qnDq4cuKl)_Q5jj!Uh_xU1Sw z-u4)qAmzzlY&(9Xv0Cw2>+6-Vwz~1k@huF_ht+SOJAZLM@qXKPnj^Vugo>W;N?%H~ zeJ2~|u2GAeXPo=hJdVDF`6qZH!xV-XDY94bv%e;DxIvF~Y54h}M!;bm6c0p%{`~t5 zogbgySQIc+h(o8m=o~qP^A(Ohj=OWw=MRD}q5Egyi1@4eOJ%NyWeQwh$n7zo?_sXz zP|g~6u_^U(;rMcSe?^$~n*2i%o4;KApjKY<&6qLYunl9xkJ#&7&X$7*Ef(Q; z?#m10UuVeIXUNYid<8sDlM_s%Q!Wlr1`to zF4(gB;+Kz%x@Sgn>!|w9dGkB!sWQiNr#2VYI6BU*Uo?+>o?lorqN{S$aX1X3YB=f@ z0X??I=t6DBCls*NI>t-wDH(%_G7%#SCz{Y~ux~Mp9ZK(*muARaBR|l&b}i>2!^QpV zS#Qs>i(jsMFf}X@j0m^JjIECibdV?Kv2o?hGjH1Tl2}K(az~fs@kEd-f~6}9rbUutT8VS8#6LG^Epessw8;;f;L4`?_CNd)P$_`vN$ zwQTp#H8nrW^J~TOjt=3-D+^iq3>Gkh*)4cEx2uxs##BkdKJgo?&7dhJ?Wd%UZ6@7; zlNc86%BSpJVn^Vbj{7&PzRx@^qMePJd9mdO`CI1oL({n_($(3MkDmVPF)z=ra^>?e z(bw6g>$>o_IDL}c2GrTVPn{8lwz7%sg-WU{)SF%JWDDtcryS5JZ|GF&o=b(~f?bP+ zRm#UL#4~6LA)x8lmnM?5a~ihy@CIW1nzKH&V>sWuqIhA|G6P*0Mr0Y2eS(8prY>6V>w9WUMM{b(DIhqYbLP^O z0aRC}8UlOx43{0(J*)AeG0W>eOXTia@rg0=?PVmXL#y6-XI0l&tIozkPm1y0-F;?c z41OG%)-n6uRkJ%-QRnF^pS+-c7+rAc-bITaWDnuf*cTo-@Ha)~HC$KJ@S8AGrj8V< z+%5wN&@IKJg>-0*W~HJTU$})7zL_My5<5G8TFd@N=9gDlid+gaRun#a=<4V!OS~~L zTk?>vSW1S)HqI(DXlb3mT%$G0C&uC4~fZp_$841bre!>gDP!^-?WA7goRoFPy zz_MfSDQIrl|M2|sN=q@yTv_-GFEhbt$`-eqBElZ`ECS+na^b%-sBx)V$nGmNg0W4Qwdb`q*Pz3mO8& zXVRvPYq&jm96OvVXIM(&;+|k3kH-zO$M}shr1wz9fLJgV_{dERwMmrMwYaS^t=heCg$lUq&ZEwIa4Oa;HRM;qRA=!p9_#57bQf{TfB?iiho%xcpqSV8m z%O0IfIUy0q)S>q^3VxutBgR4Sh9O* zGw947;Un-p3|yo4A~0pOKyb?y6Qs<~(;2*Y`snRL(+`9b*jq0>yJGI#70(_^&d$m& z)>M?_MTE!3)%yC@#l{xp7gadV8j_hN>={#@TH!pTBBf%Cql4!HlQA$LF*;0_8>Wj& zOkt+H!lXnL-H(sH-mMZX4uVRMm)R#+tk>CM#Z5LolCrb&i?tQScktnok*-$55>^ax zu1GEKYd(kVQlfi-OAr1tV;8@8mTZb@TCdH zW1sLFXdvc{kX)~U>mB=Pw5##mx%`>)uN*DdQc~zJvS`A{@>)x^os`uy<-m^ZkNNl< zC@E@Le{XvM*&MX7_`XPdQ9M&&MaM6vtRW_T2S?)jRQ#qHGigD9bYh=aB3|HeOs?42 zsYh{4v83mP3)>%iZ2RUJGdC%H;T3Ts=sVJ?gmrM_BIWrR`_@94*s}+0*7#~aJ3URV zZxFvrQw;J%OR3FB|B!2~hpgsj?JU6}KQ%kOb`n!l9~vbTih{b3`#Zc3^#;ItBVMC~so(2@QusIe>*Y9{w5B>S4 z+Es}~@dyO5YF$KDVo{1ZD`N<*LjJ6|Fd?I8q(fmzOToPp#;+}yT~gpsS(K4bIPHqs z_0g&+i;*Cq>RPj7vGi^_#g|XDk zIYLN=9C=`FU}{XXZ%FxUs;e1tjneob>Vmp@=#vwZK{naQM_?Y|r>&wIA8!J=YH*ql zIV7!C=!)he#WasYZ@8X#-o{b(Qe{$#$(~mNCeRmHFnstwSop{P%FG|&@vlG8XiTL5eFM^p6 zI<*A-<0}t{bwVN(#-l?($w~A-3AIy-i^F|~6sFEx zo}H$Z7@M%o5E2qQqIOJOd{K~pFsZT1l^qomp_>@QnX<>igY!r$9|*rH@(gCU!V04( zUy&e{g6rqDbzl_H*k0Bv5$% zOhyKK?m~v08*_8h&#_ZW%+IH2|63Uua`vkk_HM3j`gSW2YY?&E?Jcp+U8{XvrRo~Z zW0Z(q3*EvLcdanL2UgR4OFShMyK7;EYluaytiZYCxQP2i)fhYzf| zXn&FNRFPx>)$psx2!;=^&!GsFBdBU~oN@0hA9CPKzpd;*c}D)-x=^x`U2o{X@~oeI zSHu#&A{o=I%ApM`fwb@B;ycPsG+1U~pLPbyZ_1ajD`nBV=bjSg^Svr0`Y+{F=_l|M zOj(FM+>lqSckZohi=@pJpe&s7@Q7Cj77cYXKbhO^vT^0(>*5}4`-+Kj*H>*1 z#>ratTf>Z&vGucOG|2CXk9_e{XsA4+`A@HRj;fL`$$wy7_a9ZG)^)Px(+B^|-a3Bp zG<3rnuXEL?F-L(uqm_jZ5ksf=kmC2*qC2qEZn^^6mw@H^*4_A_dl#R=t)qy;HFq*v z`(?EAPPm2lz45-){eyw$OMr0XDF9!xV+Z@`#EIzGaYs*=PG(o}W~ZFcxngxj=9KvC zueN{;#e}W*GH}NCd}xpELoD%+3C5e zzIl4hQvc4bzZ{<4);68mT#!?R4{@IgeEtX%R#tf1^vqqXvj8D`&%eIRSYO;Oq~{i= z$?q%o{w#g$kf+C&m4y=*FQtnVsqF{GEEGxd&S={hq`3C3yc{pK(>BqOUmj@;KZ;q%wH9`c`+xEmkH^K?gJ<}pHow^F6AcW1k&7Yx=DTyn$pMaWmAyD+Y9n`y zeaSIicki4+IF!V9obZbzBMxHq9k2+eF`$LC*p*|@U|x&HLK~6s>ltN9SrLz%m)=;L z`e?$q2cHy0`Cs#@w5)~+?sl44vt}Njnw*l7?qo0&Plz`PqJDIG9&^CeVk=qF5iqn(}oc9rYGb8?qpW^0pUiN+o|wP*ye1@n~IgAbO;wFWp$*saTal6}Ns zu!%Iy=1Z!=m<7wO$$679AlzwlkjMmTZNw^!o!%SQq)9=cv3R&KDN#( zCgI^i_3&6@+~LD3<(!75rY-lGrzNQ8L?gF-DNT847FRrZs-pwdk)0Gaz`v!@j^@4G z@IgtbSCV%^28x>pmIFPVITLfjm>5h7Jv^d@xqD2Uh`9x31o>mft2GWt7wYNZ=V!^M zsUYbV#Rot;fX6J!G7soxienEyi1mal&1{39nlvyzFshnWE?0tl(odEFp@D77Mpw(f zzCxXb;sR8sSH!=eT&(BtNU3tukZqcv#314yy ztDVSj6{=Dn;RyQ?@^|Aa9pmK$hO1XSlSaaT0!5Rzt&&;X0EM zc1dS6>B5EYEtYeZ7cAWt_OH00;NYORnBbrw?8+ydJ0dV;zM(|;Xd?u3(^a(1^x;y{V(Q)1=4|jLnzeEQN7Z%+k2*OZH*Bn6%(#0jj`npvzmY*OoR~L+Zif%6lk6y~i$LF;umiw{P zKxT+0JA7EQy9X;8789f2QdF#dCHM9eOQMHo>=Fl!df2eBHafweL;8f=M%Lb8+m^|D z2UU@sZRp=S&QjA0->>JLV~MSAAogl)XBn|8XYi)@9Mq99v8=&r*7eSBD6Yg_#~Q#0 zEUhVZQ45GKz-ZgzNdp-{3`g-JdZJ7Hzml3l=%k|72vdJ{Jvg?-r{2#LfAaMWfWQ>qU-|zqmsfi2|L?hcS2a2` zP3E~iNV$+9jdXp4afi6>5G=QSQKrO^?Wt)Ea$A+Rw>d93q`&mgw?-xU`lO|`^Qc@> zX+U61%tU!kUwK)1$OaD>-E8g7+r-;Tq@Lb0&!Rw%N1ygoqH>9e2+Ii`?!%%yJXj%g zHe$>Dg2GKtbk5g@!gCfsKdTatABzZ`!VHhm@qRCkmVW77Pc<>$DP810J1$ygT>` z!b*An)TXAX2(>lIk8KI?6XkBvFOaE3-@q+_Lxp|S)!VmKS0nmY?P(r9f8Ow~TjLVV zXeP2VFNl9b-wQ+pllED_uyI2|lV+>2LK%E=ZPt0X!R(OV?m_!wXrN}vZ2#gNdGp3@UA#EJU9WdP$3i^tMSYOy>zB!`&Ec6q5QioNaZ zYu>oY?8{!3hdcY3H*YfgDQzgcgf@gEJZx5g3LDO>tWXz&=R}r{{1YGVU`ED|>lL3? z^lRpEh4#batMbCbeSC7qPaihCtaN|6#ZpzMj*Y3zEAaG89KXhBJn(ctpt}d!-Cdw| z*KfXGY#0#+|J|TNs%KB1=;*?RVZ-h1Ja%LR1?j_s++3Z!0%M}fr%V)*`|5(yA5RDe zAye!kMFa%~ftV#<3SONDLN+u_u?DA-3@^LY72cQBoKT>Sk4f~>IV~SjTdc%|_3!`! z?A2KfCX=JX(&*^m(1I>~W%4i{7xs%pbb-t21mcxXQ&bf5Au@2JXO!Wbi=(%oOj=U0 zg)aYm|7JZx&q730oTvN7CiE?90CTQ2?Cj^8Z$=_nwMvaB{)RReAwq|2M4}SxcE6KD{|@ z)q=UaE>DRIAV2c|Y~h9E9^5Y1o4Es@jnLL>sgTXJ%y^7bxjNK7W31?ZMMV`Ne zWo9+kfkj{4mWm9e7M%VO(H1{dZl!W0pB0;Jg}m=dWLx}{$CWLtUEYf2e~t2<@Co?h z<0JKY3|@RAQ4L!mUo92dl&sVFrYCacw3$ys-RM2|oagag2=vX|^B(kJfORFk z=tF8H>)W|DmKOI?cAMYZe}=`5%cpWk?+Q78{yg4qohKpcVda$1ag)&JH4!FnnoP0t zjMd^;6?R+O_}2?!cyj&NLMH$2J6RMAnZi*=$I41a2V0%An$lpSkNc=uSuEW!#M4^8 z*TooXd%>oVNwM`IXd16TLx)=wJ?re1ihbbn2mwz0Nh#_Xo(LnOFX@d>A4mai9k zdp}oEJ-%k_+hhW)uCy+7M*^a2|@rT!^`^P3+bjR1ZCNSsEO$ez%%*C!+v0@$1h+0p&P$7 z&GPy$zhoU>63u@2KBxeHqw9mqmmlv|{ea(0rD^{1OXUsPi+fA<)Rd_&yX$GMjH_1o zKrQh&;Aq)Xt-`qwJ!PdNxKzjC4cHG_tzGSEa9*yS%;sayF5)(bc~KmvK)V|FP(a{6 zE9NsZ;04EIT>M$4_zYE3iXPZC_UK((ib|GoYA$9@&U2mjMK|Ah+w>PtQR?^R!R zH3%1^XYoWsb45HeQfa5)0-%}dkqg>#jzBc|x%clq*aE1Qo68Pfyt^M#X zNhwzPt^M#X;S9Kf@7WK3z3RE&^qe2he)#KEPjfihOMTpKzN_AO+gue|3p-^L`q<}u zS~~;nxUKs=`A0lK+?n|4!QXlp{H^`*p(n?AeL)%Z{X{)n<)`o?o#y%jx&)Uta(Dyo z((a4@8Stsx(TLsl#s3WWR{5xpYiEJaW$NLiZ|px-{8s6-#tixIQUMe2Z+rOPVQt?C zuAfmHj`Nh_Ro=eEoS$c;bNC7M7OVcGLY_{5Bl-&dPe_Ms^tC1{ z{kJ&%7#n@{0ZzYEq2H_jT9cwb>bLMjL$PVpHyiqYz^Xq#ayZ^o`AOKMOc`f_?d{7;E1&QE$j{7S z_1#-|f5(b}-C`~`LE8_OBlG8t?QTn@rzl`j;CWEzsKHgL05Bv)7qu}or zW^#CqTxz#ik&h;W`kf6Q@FFRX3CFyE%O;KJ^!czNGXQyvOlR5kB|?9+xNHBKokmzz1BRZ)@LrjCXzU*RwHP zZUwMoeeu_;epKWM`|t?tr@{FF6hwAJd z_@xBLnES~9e4@Wi1q~TMU$M`(xP9(RpYXTd1%E5(lRa1P8_g8b8QhNq0T?-)-1S?NIqAMJb6 z+P;(VL_=#agfEv$WlH;6`xo8=ocxEAN^&{+pPEK}d&C3DCerxF8;ymXT_IG#lvdoJ4zslYbawS(MSISKI9EN-VRKmFP|cOz!mzR z=6(}%wV`tPfcb2`Jp=SG|p(o^<(f=jP* zc!8YS5C5|s{M3H6 z`2m$$zJhiGUp@F_CpMDh1H3Q30>{}?8-6!%;CHv8Ua3?s=T5a3`6jplH?2l!2#Z}#MNVv%Mg*M~G4TBf2{zCQth5wvY z{*b5oO>4jWl+(v(2|oV`KlejUe|$gs@S@b4`8+0*;LeMs8{7}YC@m>=dIHD)vxLzV ze3n7aH*@{x_~?B~zb``j(%DJ9O2NM%!IR?nr~2W4-h+RtAO7c7{A?OOxqqSfkGApC zet>;=PO%T9KUycPe^0wSZ*3P|Uk+F5`@A9_nr~^-;7{4+mn*FEfNaH2hrV$S9e(`? zGzn8-Rw`b_dUz<}CI&iLg^L3FMFs;|vX9ofc_BCJtMlFy&wF@WuP95)T5^8EjMZI%IPBp{W$?05MhLCB(>fDHoy}UlA)s#% zQXA?OA!HN5{rmSnJ+!pFw>T;2lb`ioEObuE7_Sr+lF$kJhcaL%w7%*ec&wO* zLlD+PMlLJklu8Go5z51cXnC{$Yu6Fwm&!~>};3nOvxj+o)%r?ony96 zsTdaID4&GN>Y7`Ecor@vqiSvEems>=)Yl8@bbFmFTG=XQ}v`2}U!!SCFT zh+#$YXFEwJxDTTV#@SGAgW)%n_KO8v=!pfC_KO8vP+|eyx6p^m>>APL;aoRQsnyEd z2{uoq`3`U;c5q5kY>a~6im`215m`38#dVYTP-%XLZP?YlPu_?pcn^K07k~F%@OKY_ z{~hT6=?;7cZ3pQ8X%PG^z~6HRzDhe3_!o8f8U+>c2xfd z2f?oZ{)7GTdHtWVws%kcg=cK-4x6OXn62$j9)VK-XO#MrFGq8K^m!##aG_`JUrKX- zz?E3R1>4*oHt{&uF9rXUI^L#VnER7{Dfp+PO9oNT!u@1GbRUhBu`iS>Hhtucyy9eS+uW|g{h>ei`_TqnO$Mta_zB<#Yj|1_K z^Y+*?5FfD`MIZa&lRUXTQhljC_?*&tvD#?UN7S2QU{r5vkB#`N75>~?f6UdX|Hg9o zDe(*b9!3nzMWHXkDREi%ZTJrVQ|b?253vBWlY)Oi{DQ-6b7Na9fX|R2FU%D+kHA;f z)0_$A+Q#0|;2XI+0LL7K;L;IJN0~bjKMMXS^)g#~BLgQC2RZLiT@q&|8xibL~HxqiN6K-d+xxOtnJqipX8(c)LLI^ zzokn1*y>Gk;_XNEruKuJc>5{!AA|b*YMt9HB_D$|@_GV}dt>3VA?k@Y0H0!mTk#jE z2gkSEiEr0PdO#cvpbxnSyd?(EC;4^`Lca$1d;8Nz{eRWCQq8F5z4TGPyJcJKh@7?ra2C$@7yWuWfBR-<}JC|mc(5qTwB-YH$f)zfjv zsHGnN2OJ)O#~2Zl$6VOIpT|ebQ@8>ezp)aCOkHl$g+0H2PP=p2Z)`Yz?b##mm*0n% zhuylXJO03boqu3b^~c95t{3Fb{GvJOYS1RBz9$Ka(F5Qus_TQoEvhSn!YfrD3<}R< z*pxL0{VEnd5N_uIe)8`um&2KE_+8*%uT5G-P)AgY+MoMlyk0HcZ5$s@&~Mc6 z{r|)9Ek~e7q9fbS;bITGav)rDhU4dL;q=j+h>iyS_Yf-P8;0O93qhCToE-cH+lZ0ni__G%IDeE(guADE()$Rvg4@IsnB4Rk; z7jm`26}k3>*KmB2D@qX#a(u|uZWD(qa&-})WuQ}l7@z|$JBhh-e&YwgTPQXG2@Zr? zR9_DYx9RCX{5PzT$kk0y{1wR6!Joqwxw;A8aD2e+la+dO z--CK+MK=NM%%6d0`vj}b&Q_i=+4`(N6)JA#^pYMTsoVJNg}3bM1Mj}pgKxQSaCqgW zK5$q$8~wb4ec;`L4c=T-#&0$_|r9C`E#easLYmRZa(jFeY?WECgxY8aTy?U-G;&7!sJbK$hJ+ohX zcnFB+*xEx~*RMT1*ss=S%KTWdUa2}cC_Ik|gTkv+ zH~PS}@Iw?^q1X|PI5dO!kq}b|L%m_wtTtq*YL&GP>h5dyJGe{~*$x#HUtR=!3x_MR z9V);ZAYBKo?>StN?NEmGD8M!Fjp8|6k?l~njl&DNyVMzg(^&U+&MPb%@O$Al84ZM6 zR3`?-uT-5H6rRVRU+5JB=~OX9R{F!0wpP74D7?BK9D1pp%lWs-E{MSnE3!ae-OB5% z$S6oeYm+Ph*Y%M_kcikK;AD?d`{`1U&}OAo)!n5nR^-I4a=sK>-V1MGmj{Jox6PpN zO7{Ao@H}>FPpTzUYH2U;T`mA`^y}z$TdvF=%c{xJUCp@y)ahE;dslA%Ft^S-3wzV z0r-GxFDiAg+SxGXYAu`EBDh~$gfV!J{5{1#!~96wIEcU1j5t)>imV$hmZk8}RF+y%as(^1Q{+;0WG0EwW!+c;dj z1HRn`=YA{jnK#G3pTjjMm;6?Or*SyEOB+922W|Z9ybJsRPRHht>4o2EoYx$%>gz!G zV|Rh0&V$l<;x6!mcYz=33kUx;elVu>((eO*(hAqgwN`)3ETZqD2C$@LBLG)?F|+U+ zhd<5XkU#gsAUCxahbw*+&4a@Eymt_M#jn1T4rbkh z()s)@aEuT(d`17Q{Csg2_?H}xdRc9>nSI6i$8XSq{H-?HEQE9TH4caTxqT+NK{FxG z>l{vUCi_hAM>zbSHaNG>fD7Op_+N84*@W0aEhmqeeVnB z^L4~iw8>U~4)59MP~2D>*B4)z2WyuSpUjmNflo)mx5D{eAm&MX6%;s)cbc6PKhieZ z=BY(J_}u;q%KK!$Xbsb5zm)e`t#n?X`I`nl7T#m~i7etzuwHYBnCWJSls7)InJ#0`yUYRpkTWN(PdN=k@H$xRs>nY(cKFskcbwZGV` zsR7NsyzZOTQQ}wZ-WPTGe0SEiQ0^a9v+FwC)#er}xbHgBTMdm-# zlYVndl6giye8^C{tB(w64h`+2vb(>)vv4j)iSq(n`cCqW#(7w~4Xi_}hd13Mqs zizp+9kNalW(OtW!rba6rW!!f{JhT0gzHklUYkvow1|ILQ;p1E%;J<)^120d{>DB-* z`ObMB+D@-c>GysE?akxy&1K?MHXn36I2{GvXAhed4mqmp`pL0pZbWjFKH+lIa2n7- ztGrrd@-wwsE;s&cG@d=mW!WM_W1+{h6`kvmS4%&6DRT$K4xA?@RC#*2>?$U0B`9BSNKqL6rsKTeDJ=JVzpCQM8_IWRradp zartt+u)-C)pyzf$(HF?qF14S0d*%yVr~l05YsYznW^28yGJFcWXhnaPr@{~PtAqDd z=nM5xl6?2KY$xEx*BvwLlR88XYRmp*ps{1DNjOztI>*)bLH;V^_YV|GLO%RylEMiJ%x7glJ zCC*UWyI#6|E){kA@!Z>Psqsh(5XeH4+>?dshC2Cwc88Csr(0g!nEW2|Zy>oeiW>z3 z;`w%};G7{M$VhWRM?FI&g3!4e?wrzf?oS{x4FWFg| ztCURw95Tb+35B1#&E;q-{R+zGrQ75XVf!GM8fuse1l#r69THyGBWjclS&J9Btld;5 zs<*Z5|GVf*8MdO;wi0`bc4`>Q+q9?X)x5fB6_ni5JOk?8+d}FAP32M}B;3(vmfprg z{@(6%@n^`-QI(Pt=pY_<6i(Vp$L-Xrxf&-?!$zxxYOQLty`!Cz>!ply*NwL`t{cxm ztIz#-37_a&JTr+AUJ)KT54Bm%4ql65yX2Ws*P`T^Y?u17T%!9(_s21*8h=QyY~^{W zCg+$v#p7PWNe}6`dWijTXNQw6cE`2uYM5O|)m(2o(Glf4Xw{-n?dGH!?J>m5-Bon< zcD>|-rn_!5-u6N1#&bq+bN$jeqw8x%*RMH8uEf~aJ&g5oC-!5pFQZX?*+pWT+qw^R zaxr@zf0)EH#5`jLwq5*LYQUPA>vQfJUni9>OrB!B7)}=QL~^)Jl@d+x>wviQW*z(0cQojJMbOyNgbizKudgnrz z9-sIbF?vtTZQIv||GJt65&$iQkVtg2^}I|UT=F_!${v$w^Mm8Ih9mYj93V3Vj%H z_V0f`iw9E0k=yl0r1Z0Iy+vQoj7UkP$D-@|=gwcec>dh`O&bnBdEcf(2R8~=Hy%8+ zi9Stz|NQw6J~)5={onp4#`WOYo|3NFlN^Ukgd81^T`3eN?^EF$6=IM3L?|0)o~MTF zTfd0G!2_!8O4{X`G_cZAuW+0$qt5c0-a6mT8vyu!6N0LH6_>&wLTKu#?qFh(7kuym zyKhjjP#=qw8rZk6hw#1pStyG<%Dm;PfX%1NsNm1wx!B0(O#9hF@(;B8ZE)#h1x~98 zn128cf0SeOrwdgGW#1lLH9j*sCo$Y z3}8mbbP;MDQxADdeoB7&Eg@JQ`o|vv?3QZ3YPB#Id(AZ}-y8?5#%TpnHC?Smw>6Sm zap`T0!Cs<=7jF2Sd+U}QBgO5I&&X$YvXvC|V%X2KTG(ZKA9rJD)oeO`V?elJd*6fM zZ4dEfc3F=3{(C7-UVd0!zLSk$xv1N{utm#JH@!+)37M!Q7&lDEID9np&mAYkBe1uX z3@ENxleEapC(^NPoi!aOKS|4!yaD$v`C`UxHbnm8r;V)1+dbXB^L?B7Vw3WQx{pvF z2@Ct|owxq&{?3QO#UUQKt8PbG&6#vg4)*pNzjq#ZQmM-&mBta=Zjhs>VgifCQpaKmr^I1N$aY*G zU@g%5g{Y@)*FUAc^&6I;K!>U1AH-zrzkn@Q^5D?!4U?D9c)9CMA^GxUVRV;whc>LE z%NxBBZLnSbL3mTaCm&ncpX;XEE~Iq5aTz}v&tE_95dJfff=X1ET+B0?`Tn!8GbHmc z2@w*dEi{lh+9HhfQzeO)2f&p!3gd0WR6DxUC7kI*E*aPaG!BwgA{7PwGYe-vV$zKd z_dK?zU48kRGk^Ka?e7hbDr2Rv<)e=_ z34A={W298wh5PMjB-}}Z;~no~LvS*(Ap{{G7=~9|pfLyU7q7y*Nk-GEVu7t@xH!>l zGOpT5Ln^f==IMg?yt~qVuXCQvIBS+H-r)Ws-J7S}N8|y&30qz~z1i`W{jF{LTGzFn zJt1wr_4^uU<^V)r4HqTAPFUq7`2!?RVj>WUm^Bfo$ zz_j}|{jg`@AAU{_p0a%Z+{@dx-P-lQ6@Mr1-yWNLSkKELPFbcIzEb>6T zh5Q-(Uc|oULrHJ|wGCa+n|7M=C$TJ~FH+J@cdg|4w}?&x@=m97UfoocIgxH}z#h52 zq19@lU9~RuZ#YWM!W*i0wV2|lS%Ql=%R!akWT(b718(s-xW#UK!gl+H5k?a848$ZI|!E-p59 zY`i`qG+TXZ+p_@y5*EPruwWrKR=b)K#VBt}(F%Z6;&ss(^r$ zRsI11(vAR^Atl8t*Y9kedj5+!b6nlFrvwIC^17Z$h+nX-qeOr23;}m&ImzbYjlKH6l=*j7Di{Dh}p zextSJla$;MC1tL41+6Bd`gnZ8f`#$%4GR|}$cqApxIo4mic8);2N~PhyKYZO%d>E~ zZV--&$JH&$ID}9LEE!TZxCv8Psr=k_`8igqZjrAphqDs8oQ0B3Fx3X}9r3tS21!#+ z&(IB`;r7Q;8O3)5JgdgDH}MQHEP6`u6^=nz;=4=OLW~YfwTN{Il()+XU-W`2zdjlE z*GCAx^!~daPN}P#!n*R1lVeF&ajyBjGcR69RLag}UF;j>Jr5W!_}m6_5OWoB>6L$6 z_<(igCSG{)%zNhC;x2iOjDeo1CP8;TQM8gaj{D^V2gL z<)%^I-YNN^A)_hh$lH0N&AvYA>2u{bVJ9+6g92k?C&@or$OdM@p(zqaBPZ@_@D-Z4 zUBp7S@FT%g@ExqM8kMU!gu~|b!0Peta1MhrXvKpYA*OSBZ}h+r;S_Zo_GS;P8nA2* zd#eZ5jCY4}*x!3#Xl*Or+dZ%Xz)T$PogUZ*m7926_yF~Iw-?3|#N+HZU>N?a{B2O3 z0$vQk9bZGjAVa?cu^zv-HRuvIUfA=o^oB)jlA6S z9@r$j`-w0Yb-UODtLfGwYDe`L(F3c;r1&G@UX=TA4-EVX_liS+2VaxQK<|iw2igq3 zDgv2`;HP+CIG}!q;R&MxZNEi7gpKDN2v^%GN3xPl2Avu?4P%k3rkk;|>>_j6bVhJE zOV@4E|A<`td-IvDpU-Zts$|HxQi+S?9`0oOJG=gkU!8IzzI9y`;^Z>q*nPJ1;fKK_ z>;4sMfC;cgiK_WI*>Rc$cB*X&^F{xye!=s7!aNql*oC|KEEe0$aWN5MWO%I3!_MsH zVlO&}4U6^EIO}2~1Kj-*RgpgKT4ysnVB?KTd^UZ~JvEj7^GQXV4PiGo9~u*{U%!;` zwryT5-=L{A(@457sCKdm7MRSQ86VwMG_)x9RwEUyHgpIAE8af%fJ~oArK(QQ%ea75 zK9P9F+{r|d9W!r8WFzH!zTUVXa{IhhJAK%PN%B}xz=i46X8DHa18ds0`?II6e#63< z<3g>A*=uNugJvNv#tqN1$lK37|H&5%V-q~ZB%g(!Upe{&Lb;Lhe0wkT8<-u3A<6(< zFeM{*kcT16%}WbkB@}lddJ0-0G}(lUX6ZniC}^06AEP@Gk!sl>dU%_R?sRja#)U=h z9X>1kA)g~ZK74>3$PwHYP5G+p$r<^G3x};)7`(t#Dr`JZ{_}s>4EfLhD#)l=8=ld0 ze_KYO-u3*f)1n=dZ~IMTZu<|&n!n0xmdMx6vjwRk=vB9~n#TxBR)PnJwdi-s-VG~VD3yd51S z92LDnX-tX;MeT$~+Ro<5o4Z~*TaTZ!V)bu3YIG9|1fgKOu5RbA^3DJLmu(aTc|L2; zf1ypd_SEgUoucQgnpux^F3fM3g+ykavziJ!WS35KHKC?cyMs{(=| z2+^<$7=)|B6&2A7sE90zY$8`#1QZb!ucDwqF)Y`MQPGPind!>+Kh@nclLUPCz4!aR z-$&@_rK-+Wr%s(ZT~$5(!Qz`*Y*GH@S4&D>x|-jj z%?t#9&1Km@_nnBs)$7MlXr`=XV{EJoNPaI*rSl5rA;NM@0sa}T^cIeIS;H&YS z?s!@3)CMH?-yqJ3lGkqqJs#K=>{9_NgHanS3x?)7)T^U2&Pn#@V}kF!C*t)nqWb%K zufoF89r^0^!opB*@WN>2tlj}-@uDX&fW{_U990T2K|VA#_O(A=5by?Q7pnB(dD@m1 zqQg)RTi8Op6+3jyhlAU-ZTi)-BKfDA?;6B67Ko=s@hbE$skj2XSA&sf}l zXssKXHW?&F=CgxKd(Z6EZO)8tO?z4T0NnZrX&dF&;jyhSz8@a2tYR`&y3*m!&LDk2 z`-6In+_$&omHjRM*rzJ{|K9qycUu4cHPe&dVhe7$XQtS?C!cK?od3Y8!THnJL;3lK z2R%IX*})G#VD9{-bmpt#_p#INg3Lw&=bgYABfV=Yn&*6^QMM66FQXIq&=e$z>M z%B;nUW~ZP*$bbCXt1MHT`r~ixplJ6CKfC40CpMM7l&_Rwu>{Z0|MC4JKj-I{LCWMW z^uZj|rn{Wmdbt_&rDObG8&-&=2fQi;OQg=LIWBJ^ zHBz|XhD+_rJTG{-GV8V-v$l6@Qf<;R9$_r3R<*|M6Bl@=>8od&vqlV<&}CA)9`xQEM%8^ttz5C&+^GK6ZOjPPXr{P-{37xDhyh>^mZ>k| z6}RfA;D;vROL*}4nZ+Fj=h`><5{>TV(`SXX~ zar+Q+^J}cu(0M}!FBl?P@+FNUeE;Qge8!cX%C7;_%dDfBNt` zK9wo4!>cgYaJ@nN=jRR;ojVl&@rjSmWq*Mq{v7^3MrVm$6P=}uL0d065S{j9BLQ71 z-m+pFImao$w>y2QQF~3pMYP?8^t_*VUatiM#oGfH^i&Jium5e}K=IDNb@#6wh>1c2 z^bMuX?ub0rBCiVQbbFi9-k8efI!sTeNl! zXc~!#9nD)Y6!?#LZ+;0h_Hx~e`^A#RRJ4^opfMRV%BULIJsOBek)a{$efI4u{iu-N zEfy9QvWF4x%@5YjXCuF4-|pqwUOtH$DN}Oc7ac*rgf^GhB7-`4d^NpZhTlyd2>NwS z4Gs|>Z|)PU#mj6u+aYcev!4<_=g<1R;KJ2Qr`wKi@PoNQAJes~7$Ke$Pm57411zsw zSH^z+k@4#B;u}HVWlul@2!LK)Q5rBTx$a*CuP(xi=kpO{)X^YY#vWCOVp8kx`{wGSL&kAEe3B* zX?Wic`0;9w!v*sarcE3>ZF^Fj$ZdO5>@HDtt9xjVk2mtSd0wF9iI@7U&_7$yuV0yR z=EZ;B*5#pAFYT(~VJ!=$j=Xi)uB2F2eABl3XU<$+e8-oQ%pF~xzU|eonp0iWfDGp2 z`vs^|ikd)3;o7vWYUH!;MG4&6q*wS>d%DN=FY^stV=TX`S8E6 zrqs1bT`6PqA8KEkW32R;xND?;6iKf_YkHlMnTiGHy_iAf7})TKBl%(s%BM`HC$RC9 z&t>5Q9<0Tc>6r?~fMIAZE4NK&sl@Za^~$@-rxTnA{2}0z(lZr|CjsAr5=qBrmu z-i2{)RC(Yx?vebLmpkl+O8dDPGUg)td;|CBbKoao`&-MyfOb*iu~ty-4|Q}_Pu9-I z+R575Su{6zR)7`jl*LT4ilw2S1R+-0zyB&Au<#m82Z{xWNjS|xLrN431i0X=r6bHm z1`A;HW>2yeY~?og_AK+GpUjW;@0SZYL+R@2{QT0Fh^~Ct!OO6NwBMxM?*`*K+{c`- zU?bp&4W#i$fNl7It)JMpPeNSA-?mU_9VBo9igqIr066i?30@ToUM)qr4n!Z>ok~z$ zfNJg&^FVe5$aoU0jg>cmUCn`=_NUNEw3RW$*%-u}>td0Z$o5g)DUeGoL^UHdXG(j+Ni2Kj6lb%ZoAcK+Uqp)TfGH51c0z0T*(J$dXQ{E$8?(gaBGm5wg?YD6Y=7;cezxABb;oI8fTxY}SE@iM)&e~53bN>w5 zPYUhnYxo(nWIo(ed2PM(9CP$({~me{_^z!L({Fe(Iz4XBwek@^F6<)-I2QdwZTeAJ z+GmoEp^{$QyH?A5xR-PscAm$r4?ib55TA=mkI1X9%#S(d+5*f|HV8B1M{~|;Ush-T zRy?JB3~}ENC@mXDZL$h>qfaHjqAp(KS--3vk56cW2I7V%6wlL*RVEYniQCym_9QQ3 zPqK~T_TbAuoIG{$F*fh9i>FTh00-oAY_B}mY%J#J!^8=5n)sYrBx}rJ!k#rp^@%x`el0RUg^$MaaDNl=krXMBJdB7j>{lbA^6FnVOKaER#%59s z7FFgK&1$!AS3B!5DWO*-e&g7ot(y5~wqfg_w!(MIOjDhbrdgrO4&p-d_ z)sOvi*0Q<_*+cA2aSOI?7UE;Jal?Ank*{K%{{A+b(y1fd*;R08N1Q>uG14Q_EBdZr zqT@8m!Du>t1KD<)GY71%HgDpRhi3hC>g@RYrWa3P2`m@Jo=esw1Ynp?XUT5&(NS) zKKSdUTsAsjZ{k?oBzqIn8Q$EfMu*_Y`MF{QTbnyy)r!P1_Iwx+l}@c(*I5Q zy?8N<+$`Cr9*N}9*38dkYsHA%`4awgz`xsqk7$jG+$=qDdL$FCUj$K&juWFb%HWQUT_k_b0pY!R*^c>gWo*jOXn zRW8FCh9N%aU?F~4Q>CrGLwlF#_qmwrR|C37*LSQA{e89%@zfqZ@!`qU>doRqAMfGf zzS&epL77E%89(y{d>+-MvenVq+Tw-)n7e!oJsy>rfHIe)oB}M-lj!pKeN2hYrVRjA zmgh5fNkl~31kh%c@i5(``x66+dX4B@+NEeprF?fnKW5NQ9L%F4M4|c<9RYjQKR%hn z&Y8y+e=w~;i{0c6!1~56gfpPjnzS@1mEd`0jP#wd>O;80iIfKaN{O}>UG+5 z$G|O}C#{}Dw}ZW%H{LODbLUC3eX5K1^ogUqq+SNY&TXB#u&$l&eQpx&(!HI#;6nqG z#8Dsb=~Ai22>%_x-_^z+u&az_MjA8;B)uA|j*o?5Kq)76;6i&g0$@_-%>(b)*x5U{ zN#nuwKuYH=1Mk?>soo&m@)Ajlxzop5OzP5&b?NL)XBqV-J$G;CuB;2byTMq7h2Lqn zR4=mKrrT{dN4DS45LCwpZl_M%Ea0@{+;EjZs>h_xI&B)TtTvrzx9j9^+b&;9U5|PS z0J_!C-QokZcB)^itpkqL)^qGCPNz11twL7oKNhK9TuT1Jc7XVkb|Beqm3d)%P`;Rc z^jbX2y6UC;V)WD6$hIBUUFLx)a%qKTiX$X`k+ohXF0zzmJfgQOuuS|ICw^Q8{>A{# z-4>i!Oqk8XM_wF@#v6(EmSH;G#bvBDzvu`Ca6VYZ6607Rw#5!JY13rcUCh2>2(#dg zm|cJR=%S)fTZbl9W2vE^SZcV^kUeaJQPzhRiLg}I4p+!sR8*uMa;TAbojQ_uF$BX* z1Is3}{?U0s`FS+~AC0({ZKkqT$USVIE9BOGie@63=46Lvsz--zvh(VZFzw})jd&pS zHV9Q2)P@Zi>S)V%5AtUq@!B%&ncvwt38SNZ{F_6_hmm}hf z2pdzqm`qz2)WNxe=SNkI5egBAU@!O1=Pjt<&bfn&%%c{m{Lo-v>yfLq6vqYw59ker zqAAYxNjz44I(V$A#4s2x8%VC3QX9cWgtH_!+6ue9nn#NWBrx(8p`U;x*9SCK9~=tV zlvwDcT!jA`#n61m~J%7y%e_BqP(l$A1{*0P_ zzazWUwAStVckMQ$Ti5>WTBoIkt4GVsUAk`FQp`4X6mQ7NXP#L(B+NP0Q!l$8!Mh*_ zf4Occ(W6HCezBZ-l41t~n~YnuDhGV|UWz?{o{M;9Bg($fpVq&m*^x{|PwvMAxI`V0 zwa*u@uCc-!C$`11n!U|u|9bLH%%rT}<|1wsVZy)o=a)Fo1)X588L|m#W}K zPC&6l!%cRsNc>?_rR@uLQqS5vTID{2vmR%cb2>^`3H41!g5r-NZC{v;P;G6Wg%AGI zckrJGpIr+W$GEu}7C=l9X6ZQ|oq9IdNtXMp>`UOY%25GZ_0>1UjYTZRM#`#a`$DyC zq!yi3l->G2w0}u_j^6~^puG?#&Adfj!K*mvIqgvdxinGk#XG6oN_5VR%81^@p_9KE zx|hl+tC?3X)t1qFK$hDSld~~e3P>-}gJDcYT`eJ5PrGZwp$*OJR{2o8A1B`btZIio zn8?_c-LRT>9kt=Ey9Tsuf1l;tPM@oVxXT z)fih@wAupJr$g1xB>WGn)NKxYck!+-0LALXlV(k~px@WN<$$~Hq8N$7S#|OY^|KfW zl13p?l^FI8S#@n?^=P+CcNG-{KaF<(00O@J^s<#k2YqF{KL)W~c4xdR=4nL{>m={vtMRT0L>%#HUtHoY=X8 z556cu@~LZ&U)sEh4gBTTO{?#DX61xQlP0d5HuReeuDg1Wxq*zn0Gn6&wKEW!!HjQ)PFp!~(xeG1pSfrC zCP~ufOUErdU3q;uE-}cLfJ@^|?#ip=&ez)V`f=P4$BIBpR~oxu9T??7KL~a2s$UJF zzcYW6qZW?7vXeo2G-j!l5A;p8=##@9Z=NyP!l!C+_^4ejH3g!t z>{B`B0X#X$HW`mF;4w>^Iqb-M@QFSJeMbo|Cc~rq%u$b6aGF`NfsXWoj@&JEeQ6BQ{|)`QncYdzl$b73dq}q;<&a;NLwQP-Y1z;_tivT(>|GmXdrB+2 zg1FXR3A2mb%Ya}a#n#ts&_d-1R(q5qn%=d_ftJ1cQKE(NBkg8Nk>$E7jg84zD&>!v zj0Z=M_5hx@tdKT^o*`{2<-tLHt~FLkrqMd{&|C|7_yg%4Ik0tkRlMW%VA#@_y(E%5 z^CsrfZl5&%qovhgTe2gCokf$(Obj~m5AHa4sAv8{Ir3d73U>?{0vu{o--BIWB$gb% z>~#S+3J)grc)UKBH{e(Q`bp!zwzPWQmSQ!~;Z6>%Te30T#FLqstO;w}nN6|KE|mT7 z!U*bvLx${-$a#4D>e{QgZOxF#{@Vn``u7o`5??n-P_h zO{>U&?WbAs19+xk%r@sL5#WjtV5T_`ZdDg$W`-Wh43iR86igo?dvC=tD4sxY9ja`p z_=q@vn7HV&5h8i9!}qN~lnI0Y#F~~JFq$D&AZ0A=xr@0l+QiS&fE8g_sKytDb`JHa zJ(uuL7g}+KKZYSqK9G=Ml%bJ z&m$uf9*ofHFa#ayR4#L*GOJQ|pFp3nnqn2ci0Z-$KzqI1Yqh!tTDRbg3~fvBwhXN> z_|_Ag#c`JZ?gadiT&62ez}K~4P=lfaO6Zk3sjX-bd`m0L2;Qb`$q3HSx;2pP`U$Ie zzF8J$mPtCSu^o*`QW?!W^J7Yc^!m}0A-q-^d}Bpvk6>v}an5mfcBx;AFIdCRiUx}l ztkK}G<73fe`QHT3p7J%)6qW>VL89s^H0952kiJd-n4w=)R z6T@@xP}gQbjSMawMEX%N0ieV&M=C6rAfx*Z+a;QN?Y3t>(EC0*96uL#>WpG_V{vHL zGzWs(wivzu{R?wZRD^Z?PRt_$53Jg5=gYSQ8O*|>u=E0k4vWeG^Vwsex> zq@;ePF;e2E~Og>XQQIO~IU>Hmj9Vp>T1%}vI<4UXvrld!5?sHM>W+BdPIGw)s`d- zc#Q!+LYmM`*@x$ys!)XeLNGDuG~rp8ob)>YkR86&jjVUP!)y~ca+lm^7Sp{!f zZUpB!=p3O11PC8DhYTGINI`7`#Hzr}1aC|f67kBKep-GYKv2g-@F->d%i}N*Il+55 zKtknI1n-t;fnqmnQy%)^-8Uc6^7jOrM*|P_)jrt+gv5i>?yF-~0wOb*la!u^$(B-s z@HJwUWD`K6khP$VhW{m$^BxC7Th3kl9f*C-} z_B|}H__ci{i*|IM;di9R;b|RrWJ5OxNN>@gJi23)RqbGv*bEb7^T-ERI8;s&B94AicFNmnfT&l)<-lfcX(mPmVCMcF@9p?5`lY@oF3h`Ao#*i^&+PO`DL zy7!&b>dE^XR?VoPsA+0KMvaEpo}h?!P_p*zId`lGONQcqqUA)G2pyh?iio=WCKl$1 zAhd>S%nC?x@&2Vv-+aGm+@tQ^-N!EMeaqNK z_{!1SJCAu_dD})ECy95&FJg=6dj5^~nCp%orxl3HpG?ok+ES)G0mq~hc&6B~w9sUw z(KB@l_2nO$d34ac+zV{^1@`0xhzWJ@L|L5H$@mZ!NPcYr~@phygZ3vwqa?bD{c4Kpy^6XVNaTc$c3H* za)m9X?+D@wGo(7!=XDgW8#=3F7`l?CYAR1-T+`Hs*aAX(K-D#Da-bV3_Hj5ylg*o* zqkXF6HYYK1oieRecE!hFy|T|6%X9EPo&O;gnw$BUpZJ*F>I=b*L{SC2N-Xzzp$H0_ zg~iOx^Kh|Hbuv^HYa=ZcwrKY6YD0%#1gXX ztO+OYHvTFR=8M(+eyLWo+0T=Fk}9K4-(wfV7Z=3I3oh|ZUzVl~w5bR+{-#-@e(VX> zkSK(TMDv@21thfo2Q@BVfa9F4VJeQ6ev0jufJ1MqH%WFyceeXAaJ5;Sd@Y(;%#C>o zQn&`MmO=q!V!d<%?7?doPYOZDDi&7``rj(_GL4oWy&-uOSJ<84l`w^Q1-5)PrW~*0 zJa}adFR=O-T&!MSQLGKLiBLZ$UNLLw3y0TcVurC;KL9;%*RfuF)fiQKx0-P3NbUuHSY z&5S{N_nKX|nR~XHT`?u|z<{fF4j5ppvwo?aF?^rW!LMNSM4mRrPGsZcImcLhb;I7h ze9t!Cbt~VqZ=aZf@&f?J`n6yri5X}^2jKa(>X=czN^-o$HIJ*xp@f4jaN)rgRMo3i ziH&igH8G`{JJEv$k8-I-s3cV4mTMhBe_}e#aPY#$qRKna021#qYigf8HfUJlZL<+% zTxELI2eazMbszLv=~=Z!=o9^bxUDp|QRtTj=Ac@c=gq_2uypt%i>6b&n*KGvj9Pc^ z;>tX^;hx-h>N0J(m+U>I^HRYt!`g7EZ(pN*S!=(Ls8f)^?k??&yqfvz1@l)x)z028 zR-F^8*tm0S9FRHXAArAv-a?VPMbYjno+v1MwY}lWfiNI#JnbYkrF#@G_+?62XI>)L zX(Rv^wS`x-*R(t3JQ>;}gSHHlyJjHX#jE3 zu<^N{#lvjj&(Ce#@WQX`Q8DvZqLuYfd#n4f=7HeL7r0mql+gmFA3GG(#g?PJC->}m zUr*6x*P?lEzA3J3=Wq4tbzhI3Ef(&6d+sAUw-Ze^4D@9Q;ew}}{Hn{&DCzHx_kTAT zFQB(;*s*3Dl)%udC?E2pT9vq1Tx+Tl!(ys93UMe@0(HRdXmTdE%kN9nrhhWt+&V^l zur!VJEHPe~cXf|CBLF#ISs$8g8vVF@+X)B*V_Z#&Xg0p+3VOoYcSPY<9Bi)5C6F zJ@T;wo0bfJcF;|0Mx+;XZSrtCzM@x;`v={A|1|OU`yXF8>xZqoi>EJpWbTQni+c6~ zozN`c)xVW^J(wNYu4A1hsTnn5Rlhe`-IMzF>alygZ)H_gGHN6kBO4}+Ov{q{gJBJD zD_YWoW{&^y`|YJn3`EJTv&axfvEZpdzq6T^`dX8R``kHwQvV(WSHi6PtsEPV9oSqr z{Au32XRj%P2i`w5!)E8^2rngD-z>hLcwx$~$~ygBeJZWJA+N~o)!z+fomYQYryUQa zR7}SRSf;CAo)4W-bDcb=%3890#`BO2e1dd0+jqfu{*UKeV+sBnWi7QvNF#s2NOKiM&OU%$V zhc2n{!2yILpY}aE)Nh>z`N1xLUe}XG0^aqV zQ0roRYs8#zp2xjmJvTbydAW4bAs(-PtrbG|D54p)^Pi3`fgc{!uKl1v?b{8)SDn#+ zsF&Lfl26_}Cw(d9VY{s5vqqJ6M_n^O(zZH~oO7swIyLl{Fhp~Rg=i4>0=ABRAOhRk zzfXzYsiz$LC|u>91MevZopV;dbgrCps6jc_!i1<^o7wOaNg7(PLt7$7hK(RK^nJ}3 zh7m4o-crEjN~H3s zA4doS8KghTWzg{@qyj}GM?;spM?oIeT4T9+R2>fJEu?3m^WlFVJK~vI#tobA>ix>d zk#CI|@pP@~TgLW&Mg8*Z!cT6$t#usNJ9q2x$-w@t;^gL0)Q2~Lp2Y(PM&g8nMRuqH zvGxvCm5@B`j&)VS!sE~Kv)H8yTT@xPRN*p=hj;;=8?npbu#U|z?IWv(r&eN35Op83gHzVR&H<(w+(Zo{xgwVG_H zYWHO0nw`Ekni~r3Fn0!>=qK2fia7_J{*stsw;?HpmleDy1*wQxSfI z>dNqqTOG03=Tgs~BY+JmyMp?y3LDhEW4#-a;#35ms>Lmik6ElKixZ4_NeR_%s2YP& zP)sa#R>D}$X&{%e-x-E+*-hj3%Mc9)Lo^PTvrJSPrQ&t*DK)k9$(6r~fxG#r(1p_F zD}QAhcbhM$U$VSeyU(6GD?XTm6KoLKS8*dJ&WBQKl|)Q*6%yk&$fvQ=y?e~l7wwwcVb0nnrm^a5yM^BcMh-tJOUdtvjEpe zsw4?k7l-mm`0B2o#MbY`)*n9;%2~3vG(G}s%THp6g;Zm3adJ@3iHdw}&b9rSx#nEQ zpP^_-Iq#_wEQ&j{r-^_vFknXrt&pnDk(}`kO=b(deTvk z=mM-j3ThWCCy7|ZQ}nN~TId9(49#WZPY!O{#EPGs9)GfjxsrJciTV+#pg45o0s;$H z5^6bt6PDWdSg=e@B~}9Q)b~W=1qU>V48cX(hQtbLfCic#48}mpDQv>o2*c_KT7)tP zEuSF1wb5QsTV4PfLTWQeu?>xd&f7>KIN&Yok2=^6-9fZG0A|5+rJArc?^@LnxL}NO z4MxbYZU&>MD0X7f(cbXq#@I)OE`2oW!7|8`PTUm{N(xD` z(ajo<2-K>^YKnhWs}&g0xY-5kf%qq@X+2<{%RsGNVDn~m6g_X$>_V1RvX;0YGs?g& zK#A|k{zvFcCaow(qYN8UOq0ogDn)WD(xr|HeGjN)RceK#0kURXA|CkKc^i3a+O3h{#Y7~&`umG9A(-a%~KJ# z+QIyE*aTQzz{GD(Wwhp}IY^iQ(r}tM4C)+(lVX6FzZvl?6sr}YE2)Qh9T9B_`-@_m z?2G6}Y^tClJlFDJ8e^z$S zjbFa}#N&Va6KY;<*`j}53;%=_E63mSLVKuTNRDyvRSI zF{|SEteMxLiT9(8VCY!>CzN!4gB8Eg?9Wg~=?TS}X&wqJ>2<&bU0t24RfjrtL*Z~h8@$qk@*OSp+SJ#jpE{$>3(h&CpD-7lF&CE4L-9-MhK$x{f^)Qa z^n#B+e1<;+nk-l+^!d;<>4#I+Q+miOxNH{a^UnnD)Yg$a%jJXTMnTo&Q~Z(3{E;)- zy5OB>oIF}mMY{M<^X2k%$vouuOWLL95Be#eomccZv-OPV?ZA}0R?1KO+B`?{qYuV> z8W ziznGf=7(bRaW+M4lSHf2M}>Yo6IwD#O+p=$zhS|k(RvgVn)?q(2re@?E|6>z>u_BB z#O@RIZ{-cm$zuO;mhj6N-s)CyJJH3rj52qEMf28CyqD}hcmQ4L&THl^D{rIOOAB#|N@Att`Y(K%GvA^y*3Vv1eKQ++rI(1{gvq%o4CkE0x zu=HdLF!n|yy0IJn$XofZq4l7#^SWGq|G5AnmnVd_3nS9tk6-o<>!oO6~HFl=K3 zS>TNDW_1t)TrA;}@qjlOJbPYUd{#Wn z?*?Cn^1XJ8-WU=j?aa_I3CWkh5>(B{6EogoliB123$QQg@&)4Pfdkyj>m4{?e#=%r z@(3l(pD&i1-|(uu<(@s}W%KB6undS>@Usu`c|W|;7D`8a+hriQLx|m4t(g;9CyHo% zJYMv*gC5WezxWny)Vy@RwKo~f`|!&3qx4?PEvJFNP0LL8hToIr@$Fk4KYEmFUh(Jde1hi>t2Z1-+s$96-`?qkPwa}Klj7JAx!tUB^KC2hEyw{#mPi~R*B zs}#YLFu_zg`ASnsy~TO0Hmif(=^iF|F!qU1wEOWLxtdhRxq$?1cN+Z`A{}oblHKeR zmwUp*BD`~G*R*{Vdgm}K)j+{7HH_-MIU`>)G{I{IbJ64JrELkW(nO^2Ph5-SN}w$4 z_lUhT!|yT3?a#q0k32WBR0ix?6s)5i7itt>{?Oy;`p49+Fd4%&0Q=_J5gTc(ytE2? zGn|jVu%qTybE;^eUgkT@Uiy*J^IP;AH*ZE=&&2*j)F(LK^=Tu&Ml(X0sLW6nD32@8 zDX%Eom3{cu`j-fq{Hz4Apd%6M9~xmbMHkkW4Q1om1K4cqG4?cjk-g6T&fdok$>-Q5 z7UD5r*qfc~PD=OI%X8;?+(|j!Y+T?Lo^vBsuN}*6gO$5c*S@H0J?c6-&-I>1=egc> zblGTJ*USIEdG3#*-}QgtIvW1}hR*+_Y_!~5RHat!rBfB=Bt|h84NRA*-t5{eDV^OO zQ+)dL?`fx3^oS|G<(A*mdSLl-4+6}3(bLCzShu39bv|vK*jwdp?Wg5W>@U8c z5C8w@a;>~Y*LqkUK*pbB`Lp8x({c`PX}-sN^xx;Z(lEp4`>REUEs(1(`+S<_^XVr} z2hX2At)-sExknEzwMXd2TYSD-GOgTiN+DX;|M~BKx72?(|Bn9=RTlkU`@}as-&1vc zxA@HNQE+VWNc*hR5=~uKl-J3NL>~-~7k}t0*-hdLUQON4tBEf}Q})7HF-rbO+dJ0` zsdHWT$}--(_{4-nFITyny(mtv*|Q$=2DZ#GLl@Plte9spRXl5+5%-0ju<)TQdl7aU z^9^Y1Gnb`{)2>BV9weJSS0n-MA_+HHg3ScjJoe(8CqBlPj^A73T68hANemFR#E470 z3;SH1cTtv$q9-pNyQX^+5)<6XeE6I#Z{W*wd)5#&_pv89T#J_2B{r>5^mvm2*d+n;;Vga4d#lQnkDr)B6wMKh&Aq>1ho%c`vwA!6 zVbdf19ug=?MpZ<*8H&AMoOWdRU&_Jui%=UcveTyGTJ-xH;&kj(r@X9#a2`Hfq^pV< zis{4BuRKn9{$u{abP{K#!l*+2!qO#-2h5B5Ak;OjYWLEHxH;g5KKY?86BizS=rq3Y z^UraOyyivM^xu{(6_>JnYbAXfgbUw1&ZpHW4Bc@}D(uI8J=k*%JD)))*no^5YSn10 zxG=f(g?8=Ebezm;jjhiQ)NQw1{A_mRuf0>yNn{W%MzEC7G8DTVe@fW(nIrHP=u&wwam-0 zG=|(@UPPM3BT8V5X2BFLNel6a$RhCw zimaJz=72-fZ^qPaty94} z=B<1Wb1!cPP|61A9m&p;Ij!|4f*>~j0IT)ztgrBWrJ9gAxP0|$;ZhcF%ba{I=@Su; zz|KB_2d=~}y7CbGnmzDKPr!#KuUDbrJwO^KuAWzKP^(=%Z=~`yJR{ha{XzJbi>5{@ zYyypyX#|X8`FVBUdC`>5BRgQu0sGjavjlrlpI1|;EvuNOn513h6W|QlX-P<%B+~$a z(vsL9u>s|H4bZvUHILGu4{ly#4al5#1yC6~A`~^oNdpf@SVuciRoVjIQxc!_&KPjQ zV#x!>Cf)389Yh5E107T*31x^5s|;vy{>qh+#1Vm#NEI~R3mOw6<#OCAXr%-6(m!OF zV6IftBEP@~`tJq(QRTn`Fv^2?*>4|}3u_hlqcI6q6k>PQ@X_||ne_LL!=dDn%0=@? z24?<`w0sRvd1lI9{_xzmKgGcm5&hSKCa#=0^IZI@iGa zWbj@NP89bfg=h&t64wPMbwuixW#7;v@f!Ri6Ym@Kl+D4FgwM${$r(RJ<(VWl1WPCn z=~H<)?1)-hq({TC^F+Z3=Ly3pDn&-pPxpRljJk1l<#SYBR|OIuV^tT8s1~ z{oddY)C#3+qG6y_Bvbvc(+&_jPZS(#Id+~1AZZ6=dj>7A1JIo$-gpgkRGV1(FDVzE z6ix>OI~}}e$)zhSJ0jb>T^`ng6=BCl8vwyi3a6vIlkNZ@I?1L;ekmFP4CKQ|j!9)= z=Rqb?X)5OfwCH@04JnSTH;^wP9x|?IL+nQaBAA*bX+W)zm)f25ppNu|z%5PFCfWo< z@=7{M=8i-uXPD4Pz6gFvX#~Gr0aDGcNdxk;2o008WU6>%kHB_lkU5Dln+7|drO%Lo zl~2~J)ab^r*HC+HBAIMQ@5A{3jcg=Zhs-ByN!oUTC+ku2Wcix-&Kq)4b@#Jjmi~)m zS~)MtmD&NII$48HQOk?u4bw@SiwuGqMQ9|Sg&%ZMOeh+^tx>j2E6^bMDo2CN9{d3< zI|93$Ov5xdLXi2SJ#uK3dTM^IUA2^Iv#qCuTlSw0tulNprFslg6i5L9b0Fn*U{11h z6>>#Q+4iZiWiwG(1FgCb!;PYopxXEWvs}KEaNSEi6&c!9(h

h-1mI`j?mn@#k|La->TpE!0$!%8zdVs3AcoHeV~ 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⪻^aYFO!99beHFX-Akj_(lfyO_-YO zV@o>9a{G2SO?_onfr3X4#Vj4#x$pJTRe>Ad|6_6V(9rU!aZ^tGxm2@e^X3JUUDuRc zJp1PQHusWqM|bunr}XW7^W=D#FxAJr7-6svW4Z=&{h?8yh4kDwULAl5d7}Y^P9Xe} z`EmK?_nRlbyQM0&x>@4q?CogIPj1gIZjEvBjm`0+9{4x4PAQNvZM}tp(BKfhxyb63 zxnY;gosFyqBE< zBGU?jpsUo{+<|Y;aWz36!sOdUXq=c{V%ka=K!<8=DK#xU5dk%28IIhv@@lb9Wm%@ZT}tW1;PB3=l}Ucgj^;xH<>9V_aeW7y zs<%vya8i}UZ}(4;YPHf7|DEmgJY&k#FZ-ng;r9VM+ItA(pIq70rd)Yrb$PhJ!YZI- zoLNv9YUdc7p;BdsIXj0G`}l(%i^EQ z*B-Cvf3Uq+I$=uRK#ZuOYyxlnP{fox84(zADnRC!Tt2BW1PPEUVYTkGPOO}=YfA6I zX88w0D=tn8D|zX&fyx~NlM=moUjDYICoRw1Lm9vg@!l9*qfu3+tHpM2U%>J}Fg610 zdhjQ9jJ&_X9z_^qHzo`_8w@J=K9H4+w= z=DNl&n%$BYOq9>rTEq{Tyt`xB8}%aZ$>+A5{&O|3v|`!WS;U>j4b?&BwhpFN4mR#$ zUWg<&+DGhYE3lVlMJTH)tCgw@sl7*3*^JF#Tjo4xaQMKVa)Zo+=fjR1EDX!WAo#dM zVi0`5l>QaAt{~~y=qO)^i;yjcFHldYni4LF*F=fu`KG32NrdTXiEAp2ag-3m z`o6nEU|O9jAvefI7#0^F>5v`PGNoD@fLy||8W-fMrc`D+J86rmB%Nm$YRTm*KH1w> zo#{hZ@D+huH90cJ#T;qgtV3TT!S6>V`lab&sHtMDzgFv?>Wfyz=+gYMj!`!rww+kt zHbH7K;F3ON`LVB;^nQMBOLL?krXnh`GFn*oK6Mq)9kr{lLi-mRhIJY5&VZAK(QqM% z4fz9n0GXK~MEH0l4i78sO|<8_*jbq}1Hc#LwI#Vj@RmO*WZUM@s`MafdQE*zy0jy_ zqGv`=MR-J6t2}eCMuonbePLcg+>F;-Qd%H6y*!4;6-LhA@IX9>W_gYf?AT%N<|F2l z94q)szvl8uI%vmC<&b=cY~eZ?^W&V=*Lqv7Z7!D=w?+E-$q3}BZOx2qEszl*jS9bf zKVhU4>NR4mF5X&Tt(X`hjE{|Q@(Xh}_s|rnl^IO`hW1`f?&fJJ(+TZ?AJ@diwrwiO z?a2?biuGSwmf0A~Cy+~YV>UCN%Y~sNRZ6K!UNb>$4$tvJ62IJ7ACe^uR=aymNR)UW zCmWu%iM3mRJ1wjCIMdm0;NDKmFYtq72AWB4s6h)VqemBm8UstBGrMe97}sE>T63|bCy6B7az@wua4#iPnfLkYj|mD zxFA8d_I!^=VnRGWeb%Hb*LqFc)F~?enu&Fi(iM$Kw(*B_>h}6v7w7D{W<~wcg%gM> z-Ie_VqD`sujy0FQG%d-DCk~$=?{)}-q))50m&wB%y5-psK0MQ;-rbcePPRxkc@DgG ztF`l|hv!%1ve|4qzQ`_HQ=oK#LqD_~%Mwx(oRr0}vBgTK$fe%^jZ7gvoDTYI@XLnm z%&51715k`MR=Dmt=A%Kz>0dh-qhau2tm%@h#_99p6Bo{Gnh?aX_jZR(KrK+Ii?UF& zaJ^qvSH^_a6i<)f2zRF-Z%09VeUgh(;^yS1^az=huOz%1U!7YX;pP#M-#9y!+>lf( zvvu(Fci8!1U{-|Z;e*4wJGWN(dDR_RkvchD&a?9iOOS-rWy|g{EnOWQe7r^W9x1hh z1{>T5_f{;aF6+~nyP12j?c6|8i179U9jzP6#nS3Eb*Y(IIjlSU02d2W z=KxQEAD?Gw?I!RUzRywRHYfv&^R#X@J|QvwNo6V?PgRzruJ!V6-*%<9?C{osDutqE z!OJJ>I*)ExkYZ+H&$}NPZDKOTE@#S&>23b||9B}qZNopdC$3r0B9L^d!iu570x}Q_ zG^+%f8TDi|Lm$h7u-7tZfKhEYIHVLCe+MQ4zC+02gb12GwAL6lbus3@tjhQ$AFoTA z-`kKLAgfwfh}^|4=AqdWWtoi$A}(R!CiM)I;d9okEuNTLaR_AU6yWLV>0r*Z69;R= z(h1=L^ok2X`1@G7J8=niV04Nww#MByK$R>_>#tT3R@eXuG{vn8v$9vWr;b z{muerd&eHAhD_ek+;Om7{g5JZTjJfU+yXTj(u(P+?)|}&_f9M6N^)a6_zHQMm6N1U z{V6Kh_Q{-#HS?OYgKR|6(!eOOZRE7qJF7Nz=Q>+PR4*tk-_#l%)&4S+4GsSBK6^hS z5#AWk#rd?c24J)wTtl#HsQKWKjjs9dxSl>e|EW_e_grPAfaIYpJ45(ll%3gvon4q5?7+A}9Hz`K6}%rTI*XY{&?Z=nl;- z-8|S*n&Qd}QYV*nrK`*1yek_wx~YP_O+6;fS-W8<;nnY!MRv5y3p9eTd5?^|y9af< zm{HzA!2@iigRy=CuAXaDbNp*Cj3Ck@Je4u;6>hxI*Zj`9LUrB3Y*7G@GjDC`w0x;^ zdQn9{^^rxn!i-rJrjhY;scRf}nb$s+f)o zzs6kR9Y#F7i9#cm)`1I49(-d_LQDr>BdBw|>E?q?y8(H zs_TNYvT(+($qk2>Ptl6zNZT*2?c7%FFUzi%>AjdgFF)4kZ^M_U6DPzg{H-zE*#DNW zk29*^jlr!F$}ni>1et+kUJj~Z^sHF0UWh92$DlW~n*|Ro0-!2t{YKod?%m%9BiRD-Tn-FvIH?O~{^^9~hi9QSO!NxjsBk`yW=c+EUtKg2+;m{DGSa>J;J;QSFHSCc^T6g9!e4QjBuKVa7vvlh>>~la;hcGt6#l9x?MX;)jOUBfTT(S`g>qI)!t@FiPm&lEk`!pKn6$c_4Awdah9aT_>q>>S zN3f8x^-75lWv;omaNT#?bG3`F%+6YQbdpHcdSp)5j=I2@_N@l|3^jxs5Cbj;`WqaM z5n?!acSNqnRwBmRGQfzAZxtY}Mt^NE=$IHTdeU#8<1_gW)>l+9!)b@ESs6-GI zT%|_i=qj}3aJYP)OH7oUOPEtfNWbU|U;hbFKFZS08PnyeS<_n!Wzn@g5kX#3cMD1S zM0IRiUWfoJjBALPM=&#%C4rpt`H|((n#~0~H!FKtvm&7om)`o7oQg(D~|IiNJ*Qc z@`)FOYjU!CY~AckO>B5JJSZ|)3j9?mQF5t+4c|PdM!Kt4kN{l(1jB^31Mk3kJ1$B7 zMP7KU5lJ#uaA;XGgaQsAY;s3-Lca=9I}0E|_V7q-&c?NKf~+su_O^1fxQg(@ z+rQtSov{4!G$!Hc>l&bNt*XyyjCPKkvc8zQ<|OlXBaU3Od&KmVs$2+XMd6$SVADFE zUu3_;zQ7owZ5j^aAf3hFjYf5_QwU^*quJ$1G(cC#0e4&?fz+8!9ib}=YOgqiU>jCJ zMuQ+jEC^FGu;UuITYQZjpJcxzE9i`?+B~yH6x&u`8j!!NUhUs-ctBB;>IVmFdaL5y zB~ds@t7XDJGfh)?r#_j9#Cyr5+1aY4Am1rdI!4YAp;Zl2}s72sQ1 zG$GQPD-0H>q7c%Azbg%lEUxiU*QENVthl$#!qLLQg)fq*JbhxNF7}c%MNVvq+67fb z)ocA)K14omnz?#Q`DEObHTkO9Q&XhbO_5A@nU5n1OezeJ)uacJEJ4lmY`JU9;LPTn zU|YT~&sF5=XnCEVmRsl-QJ)@I_UdozEiEkgK2l9;AU`tL!&B^}b{UB3j*rU=vGtiS zy&z|PTa|xcV?=m&b5)9;l|be#Q3)&%+bgxa7ZiOY-u#f!JjxeC%mQi1Sa5<6aYp9CWCT^_@pcC=(AXjRxevU*`S7b?>(JT;Es}ls_$vV{T^8H4Dn= zNKBfX=;_~5SMV2hIAle(f-Tzj5SfK9EJWW}0n0>98xs#95x2!MCLzvo?ubq_W zYtJIAJgpuE5GHQ&K!w_cu<)?>9QLy}vlB{%B0oDOLGaYInP!f@d|PK_X`FX>kjToy zLmDfAzwQ=}am6j8HjxzoHs6vl1*kPrk&Yr+^rG=NtU=jx2%VR{|Dv77HU1&nFI&XTmuamNL@Xao?{~B^TocYS0+^*sj5mmL}?OK7iDwZFd zCU-~HzShVr5!nhP?rutdS7a@+`a=WV4e`S2#gnsrlx|InVa*KJpa!nl?){XTry&kH=W`-%L-Z&T-~LSF;Pw-O(o&TL|Qy2+dnfk zK~S~4bCOC`+p@62Js~|OAa7o&)Hk($`tppdl{4y7#pLX($aHg5%vS36_a?ozvL)RI z5k>oc-B@w!%0V^P))f5{@fGP(X}a9)JHphCt2uP}!;;M1do@TQ~dOExrRW-2zf78gx>gRN~9$I;yME{FGuhYnx&&-<{9p3Y~3ywG*Qj+}P5R z5p3;K+r3b`>GoVzSnJ{GTTlA5Tg7DNc*M5mg@jZL7UXX1PR4x`BRIdqz%4r?&IJdZ zv~PzwKlp3RRC_D5TaU7{MY!t8jK zOoU`b-Era7$-a(JO$DSNM~GIYO^)|)mPCufdZJ@yssptO4@LRRWYkWr@K2VxM}(?Z zPLPzB6!?VXYJ_uVBvwVC-#*&Wq*OEv)vBwr6Znl5-hL8Ca|bh1Ta&bErGsZeePZL% zd>@kFifw)i_KJq}fG8hpM=L7_`;h$DqK*XDAscT^peD%A&6@9I?H1RZ+x4c(IjS13 z|Mq|%&IG-;h8|_?iH$Wo+7jB0t!T@T*sxb}5NNZvNbci}M*&AXvJCrUF>Hs9gB-GZ z@tN*0QOe|mfGmv{(bTM&lolZCKC>9zt8M<}$@KS@XQEy-?>zPS>59D7?{>^N+97Xo zP&w!tnwgH_JR;LIqC7sjNa4&ChQbLTnrQpbyqJvIaGw5SA@#@GZw6N^F8SaFc@;_P zHaC?lE)NzL%&(??G2xkfC%#IWyFv#M=;&={LX741*0px4vLWW&NWZNtF( zDxg|wSW|%&2b{#h=opp}rI8Aw`8*+Gi!7yO6T~GMVZrH@!SY-+f4FDrr8R|d(_e43 zi_OgOLT`B{CB+K{PES)1WWtOiZLtH1p}JjdsMb5k*$lxlZ&+xyiXWI(t0JEb++307 z9yeuz{w6GSIfZqeq)^({U-leB)~^>bo#df{RF}S>P#&}4I5oWaz#rSvUEP;f%t#Zg zcMXd1A)jPUNff9D?{8ahsxu7p61IV2FM@qZM)&OUqIW5y_a9M??ujLW-AZ7O66kwm zv6V1tC1M@-tEWCXf`V#o^d-yO>cz8>%>kQ%fwxgSa!}85Sx;rZOEujK?(rW&ACRKm z^5ORaohA@(gv_Trk6QYY5$JqMjQ+FGWa@4GiIv?gbi$3Dz_0SWIWIx*vHdLjC&OE?!%C~(w*B3C3J*9^R%G;k+=bM=4bD4Zs%GT zX`+t5R^XZ97pjcJQ&Z5hO?`83Xf+DZ?<4F{`Z`wK1Z3f5V&Y7$GiRBUdJENlPNwFD zo^9wBee^y00A0hjULJ;;6!%llqsQX{7Q0Q_wJ+qtg}IhiNR)$c6K&4!HJ1MlHi((P=N6=tDID`>!Euu zA7ErNT*hGJoiTG6ff3-tV7SoWxxoC75j<;v-47mZ}RT%Y*dKSiZbqx;oh9yofp0{^!u&e9Nop3vVQ~Ufm+aKmOKP9ZnqtH{iu`kduf@iD;Lfid)bITHPQllH^o2;PsWUx5RMze$)?wY;mKU? z(duYxMVj@xm~6JUKoW*!q_tfj$69P2$S`$w2;_3auDoC`AkGh=k3w%>eDs6OBlzqh>o$hsIIRlRFjLw)K3s)1Kb+4sKFQe6g%O;6lXUolYOjCvxE0QB71Cec@m5qfM zIoSugI0o}+?q3A%19sFSt{V70il7m_qc$E=kL@1h7;bzTH$Kex7;ziL#SB|QI`E(s{ad{$p`vd2HAf5jrhSh*n}%4OVq=_*L4Gaks%YlM+Sl5R&k77)`0)=89Fb|)q z4b-ys^a+s@EOu{@tJ!8z*cf4AY=V(#ubFSJu$5(J%Uf>UO6$Pz6Rh>Y|C2bQ#Ct(T z$ou*`f2T{pkH7v+X8t2hu3UUkiVX6T1$*j00D3?#IzA5+Wb!ZO2>cqH&^@}_1YIG* z$FIQV#PMw#V7Sewc%_Tt5h|f}8+VsjwsPA%I8}IF(5NOh0#_{UGpRX^#vLUFm%c*m zYy_RGgE=@`);DyYqESk)KgGMBLs-kk@eGtp@q#7Ps0`b=gPr7!oqcCer11^(G=y+9 zJ*x@BX~w~MNf1oV$H&9y{Kc9VX6xPE&Hgx2uqir5^EVt8H>A9p7(hPxmL9A3^@q9o zlq49e4-EX{h0%Kw74Cn@&o7{=rR_xw+a@k-o>e3P^8+ys|9PH?BPShZZiRTJ&fCyC ziU9?}M*)r)V`vJ4#wrL@0em}Cf?Q+V#EZH+ZWAx+Ic(zfCpkRK9A^F>dwHQn_yTem zW{7LRzE?uu`TryDyW^uQw!dedvYU{Qx=RQUNFfP5q!Cgep@rUi4-i^HNrV6~AP{<& zCLIwmA~hl#B#ZUiqaIlHuRz*BFR4ce$Uz65D@OYzxVy;{k(7BoSA)|d8V8* zbLPyM=DgQKl?k-RHr5=1(-X1h)-0bVl#>TLy%iAAz1i3IiL@aZ=Ep=<5Y{Zca7XDV>%k&TIJW{*BHn5 z8~co7t=q^SWdB)GU*1{^z|Nn8Mlp!JuW3^W0r7F?&hk~9x;|XKlAK~}#hwY&HFI^8 z1Fgte4l_2s6P@646{bsJouyIQT90R{rc6{R`2IX@vAt`;jN@FW$9Dpu`cV98 z2gHxjSGXfOj#}aqe9W~IQDGAYUFjsGQG|Vjs}LSF0X4t`bs2{XJ3e6%X}sF-8J{-& zrWVwf+ILnZ*NU?V8vk;&?NVbS-?*bEV#@|yz~e~UG#GJVPs+oxIU>Wh5Hk*At5~oJ z$}iik@{V<<%};5u%VNA@J2S;quJXH@vJ}TXO;j%JQ^Z}lV%$`#9DFDG{I2Y3T+ogS zR_g26(9HC38~8|`uurC()TrdnJe{}7H)J#A3%TBU_C9D;Vmbb$EqJx73Gri}sk6+|!AwWg^(^QAMAS2tjekF9RXnZdji2&uiC>&&{yFxg zJ;--#NmB&mfGG%d(u2?8k3kKD^_ePW0D6uHrJAJcdr?zOIQ&mj&ipP@($Y$Pzh}G) z=gaj!LMo*1n4c$o=opS>t;34u zg2k&1m!Q2BRC5NdL30U*zuo*iBHZ{8SEOZsYjIwM#KBc2q2d6{Hs4x_U%P<(Wgv_TvNrfyXE)_CpZTuaz1^BhIoI?u8Ha3q3pA+CyY?_mg!5KI)JC{H$=N#5Hn<<4UcwKZgnFrEud0QI zEylKGbuxzEnDnv5w!_iRe*gS*c+J1t3UOcC?&Lhjg>3^J_q8qV?mTkvk$c*3&Y&I) z`qag}Fa80Jw%CLf1)>oKR4P`;qboI=_}&-lhC(BfTTZI6>BNCgt62x`e)GLWA>lIN z?wEhgX_i}g?V+v5t6DRU?>n)uRz37XKNbA`pD%ZzF>eOAa|txSL@^HQ^+$7oOhUOq zfqRw{e;Dl>7PQtIwJvCAwEyFTrTplxQcvgj>jvuaJ$#Q&mk0dyLYxRwdYq-DUF~hlbD%RQMiC(A-Yrd&ssH`;VpRyQ!mOV-xqBR#v~DmA2Wdbhz57 z&a-Ld_waD9tccdICpFXBV4oFs(1x36t3O&a8Z?wzYAw=2y@`Kx93S#vQoft_X>tGN z9pVIe$`q74*V7 z96PK1(Smn} zH1~}*-ha1XZD??bwE6@$S-R})`j#3o%aOp8)}C%?sS*Fe)S_6Ky6eD(?2T=@;K=aksK&+}FVAU7Y2W^HY>$l8$b zNtC+|()`d>ZsIld%ns?v38TYN6P&^l!of)a?E~WbIW z@8I~Q%U$I*S^m6Tgkq0=zC-T^vdPd)rEXT~g|S^w@{u>MjqEbGS?+lk?K4pB8l>&J zsEqyRu}IJ<$pvhM793QKoIpsawPgb>Gm$eh>(7ZCNhBNu#dF+1nh z{G*Aogy3#4edHQt44KM*J^xvChY1Po?~OrlA znD9oc+0FGptAC>!k+30UmnjDh4{w^XN;R_Qy)j^5)j%q5JDgE>7Ud15lMy5ilScK9 zh;(R32~AS@9B1@W5men1nwTM$agGVDV&ck|Dz&p08m)Qh#@LeX4KmcpGqw*N98shF z(Y)gRO--6to8K?qy}3e;duR3PCf=GB7(EO1&w>rPcYdBUBO>C)n2bRKPOr!uI<@1N zX6?`%HcGiDl$t^JX3gvMJb!X5@;4fptgGLGofg^_A3DbEz)Nz)pw>rfh-~HFEviv< ztLol$NOeo)eHLTmo5faa!q08gI<#7_heuS)kicM%QauZs`l+rNi;P5NAma7qedOis z%T?pJ8(_tdXyIyWa*A7bBx6x3b#5G0!xFyFB1e4{{N3($)0@^Gss@Jk7&xKYV@Vo) zvrBXNAiqFs^N@xGvmuvzLB|^jKKnW4s5H}INoeMT%*qZKxxr#wF|P0KS|Y2xZjob8 zE4~{nvc826%T;PEFvg%2JFejbQ7Arz&a%N-s-z23EHZ>>;P@Ik@GkUNl2)IX-NEaq zij-s$x+)to;?hP;DYuMEV3BeXL|dEPAqsOY*NW=FTXH{?`W|_ zj;Y=DVCMAYYQMaxW15d_+0%9=OIgvSY4h4`jvlZTD=&0x(mbp+dDY`%-_FV@aO6qb zfIMML$fGDuj#>Oz#-4X1(5_@$aax4$#2psnVuxHIS@tZ_z#-D05RG+7eg!*4NPtVk;E)`;hfpY9Yfd+S+%i6c6BIv- zq*}D>E&nmGlXtYSJN<{R$8^s~&HMiAG2Mox<&9vmX2_TgSrBiy_0o}MKeS~!bHR^g zc)74D7hjPs(pC`bs0Ej;-VSN)Up;o&(xYohqz98oAKcu>DbgpGDDB2RBC!u;lVmFw z=qR>=I}rM4r;tA1Y#7eX`e8E;pizo{#g>eT8FgTrUcDxkx7^?V*39_rb*r)NI65t7 z^L=i2_z+o?**K~~i>>)!Lzcdr!(fZs8(}TQJ?PuEbT|{@U2-Zyj??H2Su_!8_^^(MSTRHAmjD-IFQwXo z5ArS2|KOy~+Y+C(O8@bTry<8>M>A(iw>#NJ=C|iK4(x0&6(bHvXk6LT#NSLhA->1( z6G|t3LFLr;&Z{|L+$O8^u*y-xR&3cY-Wa-i_B5;X%aL!){{DF^NJ*FD#u>}ru)pBA z*;E-{_BWO92G)_sQHUR%WGn`@JjJ&lAci4l*qA4EU55pmFMN{WiiIlDQBXiwlsQF72#+c;vM{eyvbqJ}oEGeq$V zY*D-BVcsC?$}$V2s`etm3}RCkXmH4|UR->EF_*GKtXd-#*;j%vRHo^*n~>kSEn zHVFGKnGR{nO zHa^N%!j$CwrQ@_)h}%TtCKI-VAm-|@Bi6x5jP1Y~$Dth9??@|nux?4iIEpR6xD4Fo zQR(y>wl12?w3va!g6fpVkyXr)z8NP@sCm*?`n`Sh%R9#F#wKIVo}!mtpR?jb$Ds{X<;yO3ZPK}e1mhgI=@|%YE4Om>eW-`%E5GL$I3_J zT!WgzF{0iH^tGl~wtdtVF=z}_-RXt~8miDiM!sxwB=W)P>#O=&@mS)DZl6>v62Q?< z0af(b4gYcCyDNXc+Uxn`gG#lX#_Rh*r1tWNoV86h*>W@~sjQd#^ukKrQx6|*rinAJ z<06T@Fh;%;5g|8Dv%S-$liVuD-~#ilFbc0US{S3v{#9k3G1lvCIa%f(^NLK_ZCo(k z-<{;7t(pNEc(8OSKUk)`Q zvf41&MpMpZYGMPpKrvL{gAI*w(W2h#`Z%?8*L_X)U!!Qvof?|S3N_D z6>1kH(P=?hxRV|YZyQT0@*egEq6y_0nwGOh!48Q?9CPL{Ur?U$GaTO7J~;PhjNB#n zD5FoDu!-YFw9SY;u6?s|C60Yn zCypLHar~{fjvEV%UUILTCiCSUqo=g_%Nf&?howdZVjSZg7?qZx)=kbxjjBpNt45_} zq{yI<@PM)n%1?u2OPuW6NwzfJ;>n1&^5tgblH6isqM5&7Wa@2X>8z6=WUU@OGkayU zNN+i`_lREIgYa`i@1bewX&Lz0qh@rs>F-T5W}Q^Pmj$qwbz*xN=vZuJwqYX(;J@lm_3 z3r3mHC+Rm(SG>uhOf`%u37=x+s??Lt7^4^I=N1^5XN@R`oR4p#5ihx2+R)Vqxz478 z_a7jhK)!Xz(fs&uw}&SX6Vp-UAX);jlvHZRRdHh9#~raQTZdBmbT$S$51d~bizQ6pLpeQ;fE6@UED8z-B%*J{y!MZea0-CNZO#vn~0`f77P-)gQ~ zz*yos?vx;105XZqmekvm?j3-xUz-#0<9oO);+%|=eiOgS%kg>cnKd(>U7S7J%XYDO zqr54NU>@pp_zko{59ucB)T?J)Sup3^AHRGwYnsxldV}0cpH8e_eJ);MHqgoAF`$)unAP0N39 z9TfX})u3k-EjOrYV5>=2KAYUynNN+*>&1#dSR3mhpIrBXq#oj=Qf#@l6fEeH=ePu@ zND@a}N2u{#h79bgd|?ZpzIf7dOHqBh_@{B(xB`W%G6+$v?0&289D~acjQ5qjV=i1J{-r_y-2m-s&hUX-E-ue^|pz)wCqnS~+*{$u60{tRqs%G6ffA$aP-BGJwdKsq*fKQ-{z7Kf|WKKEG zzN8#Ex*T+@#3?%MDFn121sfCevxy3F?@am4Y1|Qba*}Q94CBg(`Nqne9Qkp$9Fj)t zDGQXd^Yic7PGRU=(9gK0hnAfRF<{3~%6Z{Vvrx`c$B`zkx(`!QokadVCnrCj;(0Jq z4|VX*tsUA&W6=yZadbobcyd*Gq+ZbvM2vHm(NJ})IOo1g4R`r;oSNyYebHghq$7vc z+ZN1h)^o_1nYL{+oA($BcOezC`L+p#+vVS@SMybJs)WWhvem6YUd8NPJtE2Wd5)T& zHSD8{R14+3vtw4yx1ra4=tEn7VQ`-j)j|{J<{Ooi`fWz1MEYoCkSY6z!`KR$fu*^v&hLah}2Ua;`oNN_!T?&A)^)c;W zhi&-!@1Ye~yr_fgmppQ0GEB}+1%Nv^mttVFg9dk_G(`KUYEZ7N?3o&YfvqRY;z{V< zs2`?-w~@~(SfLz2w>CI*Extf1^l2g;Rh7)u4&A^h&)3;T?0A|O&(E=CJtDD}}FLk6^Z)H)nF?p_8X7b-da6uxJR?k=e-dOl$s$8&nFM9FyV9LGht zE=#4692ROg5Sh=Q*02!~8wP1mmyzrB!t8OA+K*lJ%9=Y5hFqSV>%M5#xQS^!S6iF$lCQ253FnuY7O@F)!NjOb*Ikgn4Xqi zExhiA!o9E63Nh}EpU|ddljhd&_{~cW?yjkBFq@($bgvs58g2>naI0RWQLVa-V(VA( ztkyN9U%iAz;T~S@zLhKMspGN0gUTHZTlr4d$^&Rll`h7`K<_V0R0A5xxJVNCHMI*H zH)16j)>Ah~N^7pHb>DLGi_Nx5?lQ;Z>lYTJ?#m~edA==gN$WlXw#{C2kLyW_rdZ(%YNA- zX3Okc+b{KMqW=wzYYba@4~H;O>gU^5pUK|3M}CoC)`NIlEqcj|@~-W2EADwwZu=Lg zPgjr!FPiBqAHh*Y3UgOtr95ve$Ww;ThQj8#M6M_>7TA80?ME9WC}F-WL)nI*VGoC# zB0BQ8(Cmr$Xncf`8BQJNxMY}zYt)HbsKdCI8}mw_|rZ{ zx;!4lvM(-xqlcu6h|$}W4-3Pj^3kz5a_chV7n!omHfYhg^DB%F>t!|Lqp8ZG*>bn- z;Ao|*QY76l44h}Aqp4Okz5`w+F)$>N%#B5{e*=5{r%02nUr;qG=n1LPS#C#8LST1S zW;R|pDSBGlT(woJ7TxOj7}1lIku#-N*)q9ecG-NnIL_F6CvSKa>vh?=L)#JI8b+SB zB?=UmvaCYYf-=IgqNO9f%EYT!T-QhD2rG7U#QHKZ+bP41_^Il3+ZK6yD%OK-%ZXDO zYPrdyAL5_Gf>Z?VHGk0C&y99Qd*gMvLit%fZ{*ngjNEgz8WbrTUu_V))aYb%R)4G2@KxKW zqK37WVu|5F(DVUldK!7FL@Su+a%DXpH$j1_G};OTg^q;=#& z`y8LV=a`R#K>?El4v9_KtB_YP14|Bz=iZ-!p-xEAws%hAPOvjmN*CpOY+k&+XzzU`-YE9cf<&`Df67!}eb;IgcQ#;-+&LN zxDqDPlX?$hV-c zoA4xCU6?~~=~&&ZeQR5PLFs3!wMc7I7N^#)Q?^(8w6v~v^ga2_Aq*tUv<{ccUj^lF zi2B?@bQFC-1+L0;!)FuPI*A)Wxy`BE(4c9?G~tc9PD>7r(?Al`X}#Po=3nlEt-^vO z9dvkvT5{k`7#$89=Z?OJM!&lx9s{LBw@pI>n7rrJVw@#h9 zbxU5J?edYui;ujyY?<;_(6COu@Z&UA(yk4D$m7N=TV8f7_)3XQ7m){Oq zcVKkHfmFMW4p1_Q6VN4Z!_uy1;>7vr{uJiQns!c zxO#T5UI1?I2iI+D;^U>qkN2ggu^h9XV~t@oYf86KV}F;4s0Baf^frE7Tdd5vSY|V9 zYXWQSmD#fCx(MV~+7@X)8_ThWQx!E1qTg2O){#r^9}uc0;A?zvnwTjR?DfJH1>4!P z&*zy&84FQ|%Fb(sJwg8#>c#)MRjRT z3-R*y^QbO0zY~FG?S$0>RoQB;UInt#Y#ms|JkSjrd~aX6jU9)+e)Jz353wb!>{7~Y z`001!v=|TJAw3lDO5Pscp59*G-rhdmzTSS`{@xaEt9KQT>OS78x8|)o1K^R{VBVzY z4i;4EHLzo`7n2(MgTV*KeqiE5T3u|pmDQALYBjBzUZYYCj~bphQ_s7GPYvH1I41|^ zfmmx*!KrW6Eku(_h|Pmyb1;r}9dm3>>L7%roaK)v<=pj&gXYM}K*JT9<##N6vHjNA zQr9;!PRme6^{(AKAv&OeZ{4V2%IG1j+Ud<}RQ5`2Br~V@)v8w6DBi}u(tft5B0UrKmbz=Y zBFvg%GNbx1!`_Waq&m;l#>|8-1SS^jJSd`{Y`(rur$3x>e*MEEs z@*);jrm*4-Yk6%ui@%+G;lgCf*-I~h4iED+BF$X|A~UunyX`lQZ$H_Oo=`zMbp*dEErLaG!b>+Mh)oH1qoW5Wnn_ zY`-CvZFAIW!=eUat*W#I9!4!x6vB?W=%|ml3#x-LRmspIeImqf zLim`J4GWm2bWR36r&;-Ot05csV44V%2OL{Eaq-ta>s(=R^v5m~$1-vgmOP#f35{wL654V|Xb6_9hT^Xg7RiPnDSI-;C9ym%!8{y{4q=g| zu;BQ(kQkmT@wL!q6EB$?fPgZ!GQibWdJ$#m6)x*~kpD-w5xzdS%D!PknVR^a^VTkD|Hb|bhDIr#*9 z-b3299yY9X+aWKcXJn+e9+m<6$$6%p>ThnbK9Q9Rv}3Y2EFih!ZKWB}7Vn1PT}#J1 zWuW67ba#5U%G6E$j=pn*_c2$VgYTglI^(?SHsE2Lm8WZWluUkC$mvyl_nfxW`EC`` zk<)iOfbNO7~P1n<$Wtd?ap*L_#!gte6b(Gfl zZf5y+c&GK`cl-?Rp3}clVDIEON1zOOj=U&KANg*IsSn=ucfQNPyTS^z?&F}DgI15c zD>U^1|A^L1t4H3=<99dlj^pw%D~Gk+ijS-8+8wuIymRJLIc)NgH|bs39DRtryJ?puN&V$p#+?D=Ns=Zd-B|RI@#E*)lT>wjc&+K@ zLbfhF^P|zVNIA7|{rZKA)~{c5@S~vY;TwQkv`jH>A6vbv^Qy@Wy7tMnR2ySxO~>_q zZu!tr4jF;n9;$`5L%^1d1+XP!TEwe9Nm+SO&wCUCjY$B1DmFf zXa}>)I$>*PDDU)C>pm#c7tqaLB!d=XT-+P+Lw-6IGGPHm2$_WCck+_0eSvntHbz-r z8egD#s*RKexDw44V8iJ!&tq&h%J@V>EyLIha+K!~=swT@x;hl583-NP1&E7llrhfB zKUecjp>#v&(u}1|Hjc0L+Ary@>pn-Ivni7?biB;Qu^}x}H@QuPtfsS#wHU~16(b+&)wHVGuU(9&2T@5p871LgyT{bFP zIcn>ct)vqz8VH`cg6 zwy*3dzg~WBIsQB95^AjWJm?=v>EZ||x{U>6XWWg7gFDHLYK^5KB~~TC24PZL-pVps zO?~!|@%f==rx>lWZf!UylfL*=Hb1zk<)sfA9oq2bnKN&0JlN=iOD)rW*z%>UWc=~f zmLD*}3qZvP!9AoN&=^9}e7xL;sbf8HG^Q_UAo|H=LcJ&8hvv3=*r8%OA@YY@&l}&fS;q|Frz?N8fC|A~DO&Ba#?U+iV9^wq^|^>Fs-97uM)4hs{h-PEv+_)*ElEfjI&F!D zfd`+jzOA}4*kLF4ebRckS|=2(;H_`IdFu^%8(ZCtGx+yA?HhgSepD5`wlGacIjoSj zVXn0etcW@jrDpc&*Nksw7>ACZ$tv0^LuTAryuv9Z3%s|Ba0PxL5w)^p_K zfwEd$Z1ZqSYqxx9hxT&K(&g>a+_JK;k4A1@{7LpFi#PEO!Ls=5gClY?Gf!q8e=leF zVbItDUe<)2s2=K1BN-N~_*%GI7fxzu>B`rtW6 z?uUa(j?Oba*l+BAe{G%fZ%MhHj#u#A=sI1YMztAdr@i*@e1%J!zL`Fg& zT?Z>o5<#ePTCiS9SD-#pk$EW%5sekqiX`%Pk%evwEiU_h?8$x^e)DH+U*{y-Cy&xe zUUv|vDBD<;Z6tp4C-3Du@-DXW5%nIS7|0|#+Ag`h;TA?KB>l;p3;>zR-HvW!j9)QV zb69$pc}btLL(&V!>xGTIH)rfv_Zbe1dhkd*u>a$Gl#}CexNdL!v7SCvI~sS?UyWbX z4iv5J5+WIk$Q+pB39wx<{|ou4LizCzQ^Cg&iLMEU3DQ2Ecz0Yolf8N3XrDeAWsS9I zrO#-$`p5c=nsD*{pStzIk8WYMGTEYWz0xsprFUfQIN8S5FG-uNV=yB{OWSYyHn-<# zrc=hyUK%WPcUrdi_#|vS!MP)B@}gRc)r2K4SX69y@RT)kmk;cc+|}2ua=EGB zcOUHbJaoG^F@`i(ep7wh^}KTR^5>C`=k(rO;Ck=#*hK>?_61R{`|*^Tk?z28phM4hXr2xOSLMt z0mfD1i-%>dkSCaOVTPqtBYCJx)Z zf4Piq+$XBF)yu2;@<42$%vMwrbc#h0y*bm38g@zU=Hq5I1uPDd6O5(0hVyZLH7q}K zjv6$q7a2H39eHQ?%t=4Vac5=Hl);@at^>DXV{LVR`sai@L_-bQpf=g$1#JWt1m7^N z+JFHjuRnivyhG0&XiDb0r7k@0I&qG8ITIt2UrBy z<8)^u>}5a)io*q32eAYhU!b40-_^eqxu#lTk||5%>YoCZ;ddr}KM=WUT|m6O7tXEB z)w_vYw`eg6CZfwNNBiBhwrafr1(MhK< zdVu{7g+aas0nWQ&Era@jn4x{+$TQKNp5fhPz&jKVRum2ZWNJb#!VZ~a#I+UtIga09 zc)nVFM-0LDr_86sYA0k8?V0XG`^Ugdv0o!zFdv8)PGCL|FY1cEfT5;5)DtHkmZ-K%k+2hf&3ML`Fl}x zB3@Jk;sx`XT$G!6|Cmc!Ky%n1J|3xOGRn$wU1aj&zU@mn>tN?G!zg z_4X7eefqlS1ey#$JPl0S#SG~Gx%zjIah*}8o#oW#qCGr>Ldb+A`g5okb44WD{&gMa zY$}7qHT4c)4~~C>-{Vh_Tj?c{>)uCe0@+Z zxAMB+s3ZT8SKK}xYi}fPsLh+sQTwp}kcQiym4w`0uKtF8Vn0^OtlVYlp7-u1VCLS5g0d18*S*%=^$!0;qhb(~Th0 z!XXbv>wWC+QQe~UpzRi&)We{~mtr*X(}%*kPLBuouuz&JoXpUnH-45rF0$ z)G$Z?t9pp`D(au|E!ydJ?t>H1o(MYV5r}IU-1?#o%cmZwOREs~BE+kpUnUrfxR7^- z7ey}>pcUac0cfE#uqRVHR=+_S&h}Q`m)}D?*AULtr&Hh29N(V>{R#lo&I&{!+SMi( zsDs5S=G$!1p31AY1@so_%1;hGLhFYzqWrG#=KE-Kt--G{dSts!LFRQt>WSB&$ z9Pd*RR!9#Ah*Quh$^rV{C5N6N7s1`-`VAfGGX_^YFF_v!Ak10BvjpzXa67?047jWI z#fl4E_{d+7-T{%IW;)VUe-)oR4hVlry&*1=>jcCl4hvCMEQAf~-pZi4lfUdT02*A` zSv-dpTlNmzmT+MjDZ}1F3TG>U`#GQm0O2_7F+5)Y7zb_rIXuJSH5+W%wt%Pb>dlhgEeoBwhtpmW=$_AT{trLLqN%=xvIG>bPXS$E%8TozyyS#0$ z5T%H>bfFM`eum#O5guum{RE)$gaT&6kMNXd`m@)E$~55PHXQ4*I!{K8*56a)X|ePHx8fOzOMv{cw7& zk48R8MqF^Td1`O`Y@3*kw!peJzwaY@QW$FIBpY;r@}qBs-kZ*CbPV2;UfvR64&qzV z=}8uWJ{;#p)YB^>(3CD_o907r4ii@Xj`T;QNzb_7c@5u{AnxZxGm{~_$<-%|&UoKI zvmx&;8SK;(h(78;dt0qUWTUO0fXv*iZGp~#u&#WNOy=*LJVBdgy@T`(qMg1=j6m4l zF8zaCm+o!Kw7;%*0$u4D^eLh{^-Cn9-S&z&w_ow@f&Fz;G`WcPoT$n?@)lKDuOS}N z_p^}>`V}{usH&BTi5!M_N#9XC$Ok{Gz&}ot_(yT>5P8Hu4ihCT?jg_@0H%Q=-FyRS zWWtX;x-W%01w0`g|Dx!tksQRXQtc=31-i0%5aj(}&>HoWf%s?@wM~iODb-!#A-!{s z%b0(MkbW!CmBv(hIKp*6nmN#^0MKtzOhZJ9HV{C%`yY^*o}vY%#b6WlIenr%$$J{p zVGO}MIe~P)LRyicKG!EoA8F}nh_fpEABz-sHYK}5@QUbV-U9wzMLRU?=k$4qlU(RV zRF7O`a_U>joEFiZ!oUTcl1->b#7md!(xqIw0+%1OcDb%Nsg4n!T{@d9?sC`FjvjYi zOGcK4XZsVw~=aINuS4h(nn6 z!Cff|xL>Xz2FOofrwA6h@`Ko=20*?GF~Gb|Bx=8)u0M~q@jP6Uh|*`FK3hbhUPUCD zdcqxoclfR3h$!9o#QKt1^^xExwtD z-}~?!dD503o|E8HF=#v*zuy47igCs!)ctSp%~Z&N)p+(Xfbu~8=aIi?)vLW2lVG6Vgwp{{l_5d-j_cOMXFlqkOpX zLiv?vuuAfheJ+;;@yi?d{TqPX8={^3%sy9+Mt(=xzgO;vc*-l6s|CJE;qt}1%B5^W z_}3BMRW__mRer^S1C)pJ{fdZFT=2bv7SJV0XT@5CayJJ47}Dw3K1R9&%Hgt)xLjA? z1G&a_u>a(``ilSPLYD$vJGjR4+*d&UJmz{l_M7@L^i}rzaLG>Q6FSd1$lcUiASKAz@s--kB!G?#4EOh>2t7yH4Z_Ol?;^GH{DqFcYhbM*Z# zyU>$dvNJ$_|3}*l_M+Be2v{}w=3qOIvS;SQ*T zzSR?YPi1<>H1CM<+Y`X_v8Zf%OH_7SB;4J$h)Qn0@VA9rr*YiB0oXz+o9~Fq?gxas z`+K62dpi76@a;L_<@Pt&!69EAVE&a1 zW)oi8j{xfbo&exOW$5pf-R>b>T~sp9L_QAVJF)@&8!&x{Z?>a+0}y_j@S?HgzlH5c z=N`)BiFB$WomKb_ahPXeWjM;`;zc=4owRb6&oL&Z{5$jHO3U@#Q{Df>$A7~U;@5xT ziz^>bb^pt9KQaH#yu0cg*E=C+v#o<&wmBVsQoS;F6mD$mm?hlE-a-8b^y+1R*8qn> z*BSs!^g<7S?E_Ga0sRTu`dExZ+{i|PunPh6=o=U9%U!ZL5IA|lwhgp5^AMy>c9kBe zM>OXM`wO{GqLKM^*wx7na}zG?Fk~lj*lehrj>%q^tGtXkTs)I~crKRbbL9p+cpvbD zKc2hA93u2|ve#iQkwGASnscP@$xgTuKy$n+wE)=S0^o)tJh=kn1A-D9Tnd2Be-XBw zi-_mq6Ot2@mrBP(Ze@(mFL z^T^H%eHCR)AiWM{Z;E;1`)+yAU5YTC#O?5SMsw{Jz!1DEg-he%K5oB>p>D&(Q1c4b z0fs{l7-8;)vE(Yz$wW4|`=XDjNMwL!zoNhE#Pdn0Z=^GE8(W4sfiEy#Z6{*%eImoW z9Pu0$@wl_`WsIHN0A}qL`tm#WkM#AT3es(@RN&7?903@duiI{(_S=l0Hg09QDz9O~|di0Nip;x{29hf8hSI{efo;{C;>wHWEJ(=>C!jMELTx z05regPBcbb>^kid6hHD8hk52srh~EsZl2S1{+12#+v0>qj&Kx4JOk+B`2F~I7)$;o zJ`RI8`P+&vrIP`;3i!Q(>j8f{!Vdr}gWucv?Qyuf0rU=_y5pTQ-xPQGZ%Tu{B^O}7 z2N&w;l~m>p4q!jUN9A|A9EQ@z??=9;FwSrlpL6&{2*D zQlM^LQ4y9v4}_iA1zF;nX%~RjLI9Lr2y;oit{y-*Kk>T$g)|c&zRw7tuSA*tE~4D? z;8sZEzx8AO=nBDq8=lfEhtv43B2;|$KlOi!xB##vG2rmVD+Kh+1#YPi{q&4}W1C^2 z@;08=5UX4=-{n%7FcyM&P5i`Ct4fDAcz<_Kbczo|}a7 zGVD-09d1Rx*r_3YJy4R|JWBb|E@EuNpj6c^%5%^g$%X=W+HZR9dPm`1-@DxZ>3fQ! zBDmuDztw%5K9o;6%}Eoub`tpe>+=12`{~ zA7`E_x;*}Hj(N(<=^Q)J7>eIh*#PpQ16*{i82*v>6d#4B@e6>*V|N_S1i8-i{^B~* z;V`c7u4k^i)9+5^&&$h@XTIqwpTw9>rs4g+2&kX-tcH0cj5&DR!DAHY-i~nxjiqP| z;&PqzDbKU(xy!|SE=$Gyboxu5Td|L&J{A2itw+RoyEf)VM$>$VYfh(p&c-=+Kx+_b zEKfEG^3z&Z_e!vt&c%A)+pyz30~-ac?V+`PWD^~a^=()ehxy3gX#RxOR$|S-G|Y`p z(|v@W-b%c!SB5>|fS6|T#Q0%9)+pY=+--gM-@qCkFY&I^KMnJf(@Z~MykCrO;U|DC zVVe1QvCs4!en$fE9lwhaJ25B!f$k4o;0y7QJ_*mqiV}?LrkOFnt9#>j9kEGo0N22? zZsIiAyNK@S*V)eHa_PBi?uTq3utj!o`vhwfs$o6KQZXNETn4+>L>Vv_>(LAOGcYId zgf(^l?K8}~PGT@&pM(>bAO4toJC3ys?y!q|Bl__i^a#w?reUv#8{97eL3oF`D)$xk zyRZ?Ba{mbKMz}Fp+nbN|J}1DpHWl1A@OwSxMqKt3=Xw`YN0c)k>p3Rd@9D+%d#-ZP zdS2}-PJbe;;Y7WOCOZxCH4*ghfPC?D#Mc7(i^HCl>R7LMp4N%D=IgLNDvjo5wNjA= zUUa~ktv;xC?WtZ7ecd-<9wQlZ**(A;tSK6tn7fsAxi&^{*?<;wWg;-ZM z4Qs+SnqR~mJAE@5{ItP;AM+z4M1fu^T7VWasScQDz|BRN*;to?Z}dxekMQ`tN$&+0 z=aiZKp_g@s{}n(_0L4jg25TT*7qiUC7?)Dr7Gw{``6c^NhCU9!99=uN=8zc;L|5|^ zk@<8h=l|Xtw>VJ~ZEpnD%ijczv#=)qrv52l8GdI%u0F8e#CqwQYP|8P4!_>be$y@5 zeiQ56X>C*%*0$e-TVOKbT><8r`oQ*hT-?B#!ZFDEeEopE05-;v`U?96%sIElJZWF> zu@BrjSijX5YjbDA-wr$gIa?9M^n804%XgenC<`qVbg?d({x$%!Tf)QdfuL8x{GJqU>E%Y z_EFeKk9GB0Jql}O)6ZOx&;q;i?ccx- zN@ZV-vLDh{B5y0P=V~SPSgl0)S0bL3kUJ|;_g0GSfGog7tQn>Cvge&HtxdfLIOPO9 z=XbQ0mDbDhdTXRX>*CJRI%S$mr{C0mWCQyz`ZPJu39FoLAzbp4I|VS~39joIy{GRf zEV(#|1m|tk663jj?i7o;ZqIY9;nv1szGVc~t9ijThxOOAM~&wNNG3*#Ry3z=S_Hc< z?H4!!yEj~S?1Lq6^A`d7*P^BScG0r(P|?yiNVKHi41qZO3m|EI6(&wJ?3%9V*9UO> zkU|7k6;kh871I}Pm4uH-upa?Vv}Xe+*`Eha<|ipwkLx2|BrGH7iA;jOHo`v-oM_(& zoMhh%oWi)h{VM!@8TaQW1MEBSWDxsbWPF10DZ;Wg#UsNhJmM#RB>S82+XVVnHf4Vz zV@^w6<&f7nIlX!kawVUzz=0?DuB>boS36Y+h)547`-E=dTox=WmSf0*9l- z5x^0Q>oJaDoWgGxv40KYwTxe2ypHjD#v2*G$l=ei|2@X>l#RmEl-osD!F}}l?avmYP!J`P_3yfRy7*jkrL5lr}2vdhkS66r&r*LYl z4=7Vt__99;Vd{#SjBDGwz~9=Q2iy+St}A*lF68HP7|&%qkMVrQ+d2HpjCU}8g>ez% zR~heQyo+Po&3F&vy^IesKE(Ji<0Fhq7$0T)H%{vvj^|zWpJx9V_Mc_{We)i%<1386 zV0?|R^k(eK*q^bLG3QotPUL=m@(XZ1rer;)TRo;^J=FHAcuxGS#~f*hFbhRPyd4G{ zggVv`l-w&Cfx_9qwb>tGKM#Kl!jxH*s2<8E;{{mGO)Gb{FH_jQ23!%lIJULyQkIKEk+! z@ljBqk$8)9dyMhhjE^%u!TCSM_#B7-knuIb(!$uvxH08KM$=cI=XntY3DY0ADd-;s zIa3)p3H*%$t!4lZLb_366TjWecnjmLj9=t1CpgS0!jLV9O*W>th#xUP0$72U0!M>) zGk|>=Q!9w(RuIEHjRD^~;)W7m@HYlIxeA;Nj9mrBFkm0V8H*M~e)=|+zl{aQTf?ElveV~5X!9#W84^dhy!Id0uyE8KpFVW z3n6FX%u9je!Mm%#iJ(S2xb`t{iXF3{qBVFH&%BFA+o31h`N_+ScQAg1aS`KJ8Si9# zlw&5I#tY(UJa|etiQ+LY1Q+7XOM#mpZ%2SBZ%vRl@;5=qCNh|DC~yLJ_c5>!&c8|k z@5oQQOJLq5fOpTspMVq-z`J3<>5SVk?!mYZ+I4~$z+V+|_&JQ{GM>kHKI857(+K%8 zf3<`CuP`oR{3_#}{Ps1*yX;#KemDE~uzxS(gZ9_){187s%>E;cOBf&J=jR!J#P}Lv zaFB2aF9#yc3l!nla>tBiLt-o8(0aWsWwNs`2pBpH-BB9c*y2vd7c=DL)OlG2kojOQ|*$9O*DMI4f9 zOR}Kak}RmUBnzr7$%1N2vY^_MEY30hkT7#91su6AQjl&fU|;qJ*;V*!GOmr*l)^bl zVabpJzI4G8;!6tnLjHb?3pwN*#&a3ZV?3WRvZn|tdkX3*at@Bv1&)Tl8RWxN zU|-<&EK%DtFWTeD5z!tzS_ixYlB7L2wN7-w+pD4nhwqK>wM1|DYl;5wR|fV49>D$q z>>tSC2Z7pm#30c5jws|YMInzV3VBRXh+bnap3|73kjE5-g2ohukh(j7X-rXweuOZ! zyFzYvg*>JxVib6?aibC`t|BJS>fzNA58~!yj=Od~J zim2jNDWTB@MO2WsX{DrXv?)Q^q)pm{w236upr|IPplnctek68_E=5peZI_6Mq6oSx z2*MUYQ3OHh`=7ZJWp|%_p7;5^ulis2IWyN>A9K!`GiOe6$4dK5u~^w>ip9!4Q!IAR z6pI7@&sUz&=wf4(v09VVI*;TsiDqVJ!F1t#XU8T#e(q&iava592Rl4jdU3Qf&yGoZ`rOU3;WmoC4t902_ zy6h@lc9kx>N|#-w%dXO8SLw2=blFw9>?&P$l`gwVmtCdHuF_>!>9VVI*;TsiDqVJ! zF1sq1U6sqO%4JvOva530Rk`e{Ty|A1yDFDmmCLTmWmo00t8&>@x$LT3c2zFBDwkcA z%dX00SLL#+a@ke6?5bRLRW7?KmtB?1uF7Rs<+7`C*;Tphs$6zeF1sq1U6sqO%4JvO zva530RlDq}U3S$jyK0wRwac#BWmoO8t9IE{yX>l6cGWJsYL{KL%dXmGSM9Q^cG*?C z?5bUM)h@egmtD2XuG(c+?Xs(O*;Tvjs$F)~F1u=%UA4=u+GSVmva5F4RlDq}U3S$j zyK0wRwaada%Z}fR;vTWYWw*p-x5Q<)#AUa{WyjC^a?USt*)4I|Epgc`aoH_#*)4I| zEpgc`aoH_#*)4I|Epgc`aoH_#*)4I|Epgc`aoH_#*)4I|Epgc`aoH_#*)4I|Epgc` zaoH_#*)4I|Epgc`aoH_#+10qGwyWI;b~Pi|AlV*tz0zv2(>0&dv&FXN9w~!r58j?5uEhRyaE=oShZU&I)H| zg|oB5*;(oAtaNr(Iy)TXJ?hOv&z|7 z)MFZq88e z<_z_2&QR~>4E1i#P>&a@Y3B^}c(KyX8S3$3rJXa>yE#L>dzz?sbB21cbB21cbB21c zbB21cbB201XQ<~nVlvxy&QLFQ&QLFQ&QQ;Dgq3#AP%n1QP%n1QP%n1QP|p*+m3Gch zFLusQFLusQ@8(POV&@F?Zq88e<_z`9&KVl$hx?HB`f8vb?vsb;)lOOGf~ci zHaYpYlTW&tz>{t!(9Ch^lxE6rm#19$Q?9(ljZ`h1nYT-e8>w2{NY&y-sunj=wYZV0 z#f?-gZlr2)BUOtVsao7f)#65~7B^D0xRI*GjZ`geq-t>^Rf`*`THHw0;zp_#H&V5@ zk*dXwR4s0#YH=e~iyNt0+(^~pMyeJ!Qnk2|s>O{|EpDW0aU)fW8>w2{NY&y-s#cd> ztIMv{W!LJmYjxSRy6jqAcC9YER+n9?%dXXB*Xpusb=kGL>{?xRtuDJ(mtCvNuGMAN z>auHf*|oatT3vRnF1uEjU8~Ek)n(V}vTJqOwYuzDU3RT5yH=N7tIMv{W!LJmYjxSR zx$N3pc5N=ZHkVzS%dX93*XFWobJ?}I?Aly*Z7#bumtC97uFYlF=CW&Z*|oXs+FW*R zF1t3DU7O3U&1Ki-vTJkMwYlutTy||PyEd0yo6D}vW!L7iYjfGPx$N3pc5N=ZHkVzS z%WeZ_>|?TlGxjlg-fchcwl})%jc)rTxBZgaZg<=5?n-IzE3xA(zYT$fS6%t5uKZOu z4tKheE?3*tw{3lOxw}#qz0@gP?!NOQ{d|`E=+@1Cr0p(j+jX-a(Yy?4*Uf%(>t;WS zT{rtt?7G>HV%N=n#E#XmYb-y~+emsCYs#*h{fK>P>4&|LcHL~d^R?ai+U|U9cfPhe zU)!Cp?atSB=WDz3wcYvJ?tE=`zP3AG+nulN&ewM5YrFHc-TG2mrflC|8S4?rsv45) zkYtBU*?ts~?2xRRAz3#=vTlZC-3(dpbjXfvvQx%-M6#lV9Bpf@N2K+*6TN*Ik~=t( z`yGvc`pEjSFcb*&C0xgf%WCYg|ayxR9)IAz9->vc`pE zjSI;d7t%(!w~KIZB4nG`uw&P_kgRd3eTeNqBu`jsu#04{i)65ig!kv@4|b6Zc99Hr zkqmZ`40e$Wc99HrkqmZ`40e$Wc99HrkqmZ`40e$Wc99HrkqmZ`40e(1=OWq9MG~>+ zr;5-&ry)5%k*vI_h+TO@vht>eIvx8(=%=x5b%r{fp-yM0)8Q?~&#$pc#p)cA)j6c? z|3h5=AHw~#mTgw&kZIdku9Fo;JB&%~sp23kk zgCltcM{++!az90KKSi=Shh%jQd5@E<&au7L$@`pK=On9hlt1d^V@@_X$?6>Ctj-}> zokOxZrw-xnX(g+3NLJ^Ntj?(Ku|87m^tlk{K70 z85fcnm$K`eFCus6uNJeCDf5sMWIFN`$wj8i8OVv!i9FSnWVr1~@+KvjavAb;C#Skr z(_|?nSx$4hD?dY)Q*x$!ip-W;WTgektO?1i3CXMpY0rPV0%o^aX+g5mf@Gxy$w~{7Srd|36SB%lW=(7}YeF(> zLfWG|+8yQ5?kJCTM|re6%A?&;9&Kk$=&;g)WY&aa)`Vo%gk;u)WY&aa)`Vo%gk;u) zWY&aa)`Vo%gk;u)WY&aar3J~X3CT)}vTI+5A(=I)lR4I%NM=g>^@{kPdyz-*+w7Og zVz*uCWR;WEPA+k>X8VER61kNY+%5jJ*TDvg>$A*71;b9WVM{8Xv!AD?ddrir=2($8yX-eyRZc z3Rulg{G%FV13zOCro&Qb;{|M?wus;&rk*y+0v^|nIj->7={2fiZQOUwfDUoA|^|(}#QOi4LcdMnUp3n znKn}>%PbH%9o^HrM5e|8b<;XUvhbHx3+PW@FLFi=5c8RvM6y$0A<*Wm44~}nY^Z=b zXc9SR1`zK#_&#SfVB;M0&dm}z4_oIo0D9-OL$}E91mYkG(jiAAhy5J(bJ)+Z`xQV; zIcr36hltEffKeFF?PLSkA}a`S?4(3EH7sqkKe^oabv3O0+>t`)f<1+oDD zHx2>p-ZTiXvn&-lMQ*0u&5J~CiG!ukA#!UrtONYqimf`@*L8{9mIlotx5vT^Ah+d| zEpHN8f!!4ZzhbM%$~@R4vMLdXaa9hi0rXdO3paAWUwsl#Ur&8~1=Iq1_4ugo0BkkH zKm?K@6AEAv)IlR`fG+42xg!=50Np##y#w7l(7glQJJ7uY-9KbNkH{YfLOi5E7Lel~ zt6&AJgN?8mwu!7B1c{IaGoT#scPH)cq}`pgyDJ{Lg$E#rgCt0Y9H@X=SOd+_0bBXC z${2`1GGsylEP^^{gbmOIy?p&bEF?fGWJ3wmKm#;EJ9P7P33))9duVenZSEz$wL@T; z$bB)833bp28vxz=(7hks`x5~B_h&;1)ByJHZ-RE{7I`2O_-xF99+3y>uZJjmXp_jp zw0W5RT8E8ww0i_Qk2LcY5cJKX*m`UrEP`5*rdU`n@@GE(oB$mnkI#TEk-wxtqsaPE zfbYMSz!s4w$l=KpXcuX&5P50{EQMZ?7WAKnR@y!j3;1~ky|#rw{j=D9wo_yS`x}V$ zIrg7N?|FPbZ?`v!ypRqJu$3=|KyM@MUc}E!$Fb zMKC9uSBQLo&WAD3B=S)Tpx-?Ru+@#y$LM^5?kBadP2|%oSSzvxn_C)zvd{9M9BBJl zJ6~%{`_EIM0@jIqF$5L?{qP0#U-XJ>jX;CQm*{?(1n7U62|2J3YGFNez!tuCK_C&* zf&H)f{CX>2n@gPEp#KfAeoOtgt6?L*{*0aPnnZf>-J1gJ_f`Qh^lpGozDqU&qX1jq z&wvs@=leQX3(a8l@biNQae$8>7Qr&W)(`8U1A6#=TI~Ip0!4t0AG`S3-2_O3Y@mD_ zd2MTiP5itu_I}EQa=_kC`1=WeKU4m578JljXa!=}P7K?r+dc}gx4j8A0)Dplh~npC z6+dC4Bm;7w94J?@kO-8kB4~hC*vg+fp-eA=F4!i@4D2JqC+*ETK-Zw_B|r`=ggWTt zXMYDlDr7=#hB z7jf=gA!?s^ST8EB1h5^4-#GlnwL`b4eKUaV!84#4I-!R@TS40)qeSg*LH~eQSSsp3 zfmCROEutcEKuiaD&?RcN2q7EAb_&t0ztcOj&{%~>|UMDIs17-kqM-;(AQ6pmjyGgaMQPh#m z{MnxZr~uj>MZ2TpAOVsgokv9W`NLQ0m`i2QVwb2>Yebz!45txK#u`zR7K)m@07Qb+AoTP9C(2%1wa=Q8V!~ld_r3 zqGrWGiKslv^01w^8rF--7f1tQ$fwP0_Gjk+cIIGb&N3j5f^=9bs*w7^ScpI}WWr`q z=NCbzs0)bqf<{qAg8;ilGoT#sS%lA`bjAoR69N6X=+8xeF8XuPpNsxn z^yi{K7yWqyAs$j73-SQ{dFanWf8Ki71Y1NEdoTn>K?a~(ybzWF`ITU+Bp$F;LVXEs zFC?c6n?;q*5LHI|vO$1O88*t&M3s|&d6lU7l+Rx(>Y^A}Cu#xxv7lB|MK){|buph8 zuMl;~5NH*3X%e8na3S=Hx@@he%M+kW)D`$zKf=2wWLDSRoJ|0ji{O|$OG)x*nRw8O}neny_)@N5`nsFW1s{!idvcq<*-TAb<|xi zfZyxc{(T%YimDw51)^>smK&Cdx{)?F;+y9UbrU%)%Y?O}Zcc#>qHZD1TdBLX2-vTi z0p!3lhPsWi+wp(#KlIo)J_7`po&&9qpq2 zkOf;r{SmvXM~S*K4%UdeD;_Ajo6o!XTvIFRPf37{d-%ME{d*&T-L;vpO%!uxb$=SH z6ZJp=bct$Ag{`6jU)#{xM%gy%w_$S|wzhSO`Y8>vp-9xv*!YIn&gkyERrJ6a!2T{-&>(tO+U}YTi(s?p-O^yK=-44Z+t@s)0bAB7dUy19PXOw6 z&xR79es^r`-UQg*y<7Ai0_3$v5~M>8R6s4PfoAA{t)lmgfe0i+CKSLTsDnn>0A0{4 zdQdDRKq_QI3DiIXG(kIbi{48h4w4`pa-afgVGT4x2W%C+cML=z88V>&7C{{}!UpJq zUeWu+LIR{hHk3dOG(Z!yL$~NSfjCHlbjX1UsDnn>0A0{4df!+`fK&7C{{}!UpJqUeQBhApue$8%m%C8lVZ< zp&7C{{}0eXj^cPM&?qIW2IhoW~VdWWKS zD0+uB!UpJqUeO7$kN~NW4JA+m4bTMb&@K8ffjCHlbjX1UsD(Aq3>~5mN9S;C9FC2{ zv2l14VB>IX49CWB>W5=vI5vi3V>riX_%dLu7~Tqu7sGo*Ck}*oNCC#3#3HByZ1U`( z6Y-U}RrC?)9f95v=pBI`&m{T?^p02o>tG{n7Ci#rBZfdSWCD7;tI;E{Hv)SjHo+Fr zBRxQGIfW49AGLl@9&`Uxu342LdfW0K_C9MGTcn;D@n_-*iBL@LFA4$$f zV(&=o9k~qFLMwDakLaTYLOi5EHk3dOtcLZl3ATtn+Jhl53Nj!M76LYpZiEf68McY$ zIZKa1Zxni?W+Qn^sxg0-DA-` zHVcZN3Rb{6*a(|po9N>PK_aBV3@C@Cuo~9GCfFi6*@Gc43Nj!M7Q!-E3$4%zJ)(I& z)W^p|3S>bMRKW^Z2OD8CY!f|t5F|nx%z$!O3aeo~pgS7f(LJJ1@E`-wI|02D&^rOW z6VN+h6KoNk5(D&Y$|y(!bWJu*9#;fiqSNAGnP{HN^>}p0 zliPUoC&aHf2EIx_)?H?vpt>5E_;knid2Peo^H z9uWW3PS`4X8Zl0bgG5LL+Ddu@2>}NNNJ}VQJiat9Qs$i?=bMSi(dgrVaJp&&zXg33&=c0S=7SZS7 z=XY%9B#6$XJ~t1_Mb9LrnQ=h*%o5ljdKR{3trwj~d0qu{i_S+cKM}G4f3w$!o|6UW z7vzX890=Gi#71E)G(iXSiatLMk|7%^pbna#1A0YY5C_RX`wOsjK^=67E=qz_z+MqK z%*}?4&?$Nzy7Srry?I+j7YoEf1h7?%ZgB}L0_+tx0lLM#qD#o71ig|}$b=j~w`37e zew&o9lWk4Jk#X~mKLMzaI0c{tszkuxuY*%2XqDS<_ zsjw6}L|;O?OQ^eK1+af9HZRQsbT6gd!Wn?Q%lN#kS@h)|q(C{KbNOb`R}6tHV1E(y zi_!p_i`qqBiLWaQ0Do7m1AJV$P4wb;NQV-jZt)sug)X4JG7d&TCKLg-SjW&+jj%;@ zbpqr;0}$sD^p?~C?XRNlsuIB8Rn*lGTMg~5jsehh_Krr;f56Tk z6QLaNxjGHjioTQbI~R(+3*Ebtp$>XP-;Le7mx^9Pylbj}Hh&rg*uDoJ_t54ZyS)Ln ziM}@ph~r+$*CxOk(f1Vrw(cjk2e9)%ljz1|pstZN4`T1Z8rTHAq92MtI?(Q+T4;u? zq92ZjJXj6gqSv9bj_pSV0o#w%0J@KKiGEZd0;!M#>qI}+2>579geq7s`mg1%2D*S) zo)`uAeF9rgGy~;NQ2t~D(jW&G0rr{)0%gsVHP-<#J{1qCfQ_dpdkP;(xC*f z-$q>3C=C<4kiYykA1qx?C_pTo{`*mb_eu`ekTr6AQK7zop;v2CeiOE0Dj&jhIe~Kzc&iji+-Q@HhU0(44~cSWk3v@ zJ7JsX50Zeo56YnyngE+03JigC$OGy=Tnio0Bl;t3ev|^({D}BJ!sbUC0sq}`fX!~& ze2m`5*!~!QpJ4aX8qr&bYs(_Q)@Mnu6uL!!o(T=G0k(?%A`w;tHn(DLYXz{swO#a= z0@(QydtXxaWiw#!OWJ*v40%usP0%I!YjXLz0G7de(LD*ke$N`{7X1zT-?05HpWpKN z9iQK20zSVZrtdaFx9HwMkObMHzbEb=dPV;fCwhB|7+EbwuMy)_i3wuGgzU$pi`gkv z%z!*GJ8u-T%Qi8)#lQ+Nv9+*8%1GBxc`cF@u}L3|R_% z?oZwRHP8yQIbaYJz%)yKLSjOiX z!0y2vfNp#&jDmE?19an8Lo;j^Gb{$EA4dH!>W5K3jQU~J52OAN>JOp*kW46tI%tJ% zF^3L0hnV9%AU>Yg&GE#?bGkXc9eTu!j)OEP zfMtNa(UhM+`3aPtPy(v~{S@q^Btto1KV=U1G)z0`eJy-k2un5i=HhW6J@1W6>MiA!eKh z_#c-A_#a34ILgx~PeVT~5719*gE%!dt*}+h#CXUA^d_P=5xr9*kOeh>-l@G}P8$U!unw@FF$l1qLAwmvW%P)d zg#Ah7um(1ZnT(yudC&mtPe}mkr)(0FN!!fTVon#RfOauc;~)oUKb5g&8uq54Kdlki z&l&}cHCe}zb&cxT5j6vCaX4k<+F=q{dBEZI3Jz~yI1@zDE5OYpE zVE>#}F*61M?PhEeb8Z@}6LTK*=M?~Le;0vT=oXWMjU38yDa$3E+-5N|Js_u<=*-+K zW>yL;1N8GU0G)hd$*&MITc8Fy#mpH6#50HOf<&Oakk7(3K#b?d0JhFAf)%h)%muW+ zAPI5+e-~i;f~{hTB9IA-pb@&n%#8)|nOhF40h{x%IWHNqpahlz{^k)k&#R_*2Gjw* zinoa=NrW6&2$Yw!i@9(JRKXT8rPwLmDyA$IYN1n1c?zr-GatSA_@0j~o=wd~3tBVs0d^8`p@rDG?SyhnQtaPz%_)IU6Xuh0j~oi@BA$ zThXt>AMevmU6YvG5@4g4+cO{!i0AgzfS=npi&;+ja&&o?G|LylQlNeX$6^JaD~AC7 zR$+TpDr7KeMm+>rp3-GR?Lh~p1g zK$}0JvpN>+XP1~elc812UHHALPR!jyfX_AMV*Z5QpVo`Hhkn0@wmegsd$GSZ5jKdq zkC^V;Cg%QuK)d@Xzdr>QLZg@mu=zl{n8vkY9wf#GYoSxjL-Bz9ht>e~57X}9MX+AX zIu8l}KkGWgJiM3M#PaBBF^?rf6>JpK6c6}p!rq@_fcnSt#QcT$*3S_0 z*I4Kg^P~seVw%x?DpO1gdQazwX-yOJ47zQ!ZQCkl1O7H_7V~_Zm=~zqh|Y@82ZC=IZrXn$aBc{KT&+FU7ypaHuzu76~Eqr&kz?=BV7 zMK15*_x*T4XEUE4Q1(HSm=9O~zlUc4^HB+qV|Na$7xVETAQ$HQ=93g4k5BObDdnGL zz*^`Pv!wz$#C*08*#CSHG>iEHn_pn-i$*b9hXC!jt`YNP9IO!YRV*wM^EIDeSBdE% z#~y6-VB?!~C=v6m2L;e6=DQidCv$ewyI#!q*#Dk(-)|7}Ljqv`2jchDG`n|L}6TE#PQP$!-j3+3YZ)cc#n3$n!v1(t~y z;{jzmh|F?jyHsjybOA+bQ+{&CO%#CAY7VB>%; z@eZW^zy!#HI@l^+BnBel9YhQVr2};bVT0!i@1Q2Y#zE)}9SF1?ivOWGun@2@6x&1F zVT*VNW9#5}NQD_t0kz`A(}w2@FTP8>VQdd8fll!bNrrO3{-N2hUc3ZsCs3BqE#6^` z;vJ69!|^d3o5LwfOakmCZW538irx`LuoTc4kq*7$jT{7{AQK9p2Jk-;`y=t2Op_HDbZkQX&^isT9f_DUfoB zNP^6g!}vT(lK6KtTQkv!$XImBr3^|XUvg!xBvLw2=Al1=tua!>zlcoz#a5Zy%co5~ zZ7xFcR|#c+q_K5A7S53QXw1TIE>`AHYFp$w`w^_nW3L3Anb<8vFGBlb{N}p40V3my zOD--goKsL9Ntkt5iqa!mfj*KlVFDoz2&z+kXnK*COh{%|tqR3RMRTi0=UzT5b zQGVWt0crW?=bkY?GOHkW-kkihNN#Cqn^KkInNT<0x3We!v zU-N1CrG@N`C9Gm$7CN@L82LYJ^*>ZQ!+9+u=VI3*N708z^BehcMt*5oVe!1kQ6r9K z@7LD;CD6V&f2|WC4QtRI(g-qoNc$6;T|AHODyI|0ofhRBh|zpC=tvGh^x$2@ff+&D zVm?bbIQhnkRrahJNiFYQlh}H`@Rqe_5+syvAr<6T82mG58J;JGKPvKlV{W|7)ZePf7biI+Wh~2N&dTw``0-a_Fb6vJh8oC&jEWGmU7NVI100o|9ZTBJv`XVGF>0e zyN?aZbmdgP&XS>SFtu&-oL`%JF*Xaa7tJp^tj*#&noE0|wXNyzpV@2`QEso&1j?f^?(h(i zY1o@h%>vrlUa}r{^ocDgp}ZLX^YLpV?2jhTWnhnKA$>F-8@9IJNB^1ruO6&*+Yhsd zY(94T`(y!IwzVA-?5)K1j6L4J&8Xl0j+;(&1hPkPzUxD4zyIjiZQCz9j`faPLK(hn z{lCk@mPhj(Nj#{W{&Uv-Jr_OSqUTvOLwl>TQAW?Y9T_cf z{V^9m|Gfv>tG4BA`k|EmwR`q>+cPKX(cS=UCdDrAGW2G0E6u03Y=1{}`u*7R*yd|T z$^Ii`Gq-1=JtomJtc-EpUY%LalkKya=-8X}j$^RnNcH!r_4ThK!8}@8yY?)zdF<$| z{%d7NkNtWK_;ov*ot+RwkCYwfe|xS)d&A~q?f=)u)8=X0Mvwhm_Zi){qg8(&{ntm^ z>RjxO>1_N&Gm73H`g`g>wY8(8ois(`x87}^+iSUhM6&;%D(J?L>sU2PI_}YKW zcbvyNrbPBSkM{F?tk}IB8QHdWbe%)~JI?Mo=-F%v`pRsJ{xQ^M7mdW~M|<|4`^VP& z=bUyN?Pvx&&M=#4gn0fng8rlP?<4uGzklwR9U1o@hcb6f*yCevd682f6=>`z54Cl#`dlqX?FB&f6VMr_ZX z-(CYd&X;I*|85Mo$7DXO?K%DH9{zW|x?{d#`*+7N{NG-)c8o1@eV6Nwq`m*!tIqa= zJ+JIsFnaCT2NxTiZSm{-u)QPLYwH4})v>)1y)W9cYRBl>Z}I=fi1p7`?T)OX4|VqZ zjoue$|8l;i%Xp%jAXE6F=`6OUu{D9MGkL_D>gpy^7GXX&m6|hn1Wcnejgp~g*cw|u z)Lo5dvYo}U%@n?(+S-cRn2MgYeHJCwwtaY>hT z##Hxt3iURE=#hx_)i|^=@ohCuA+9VJh4o~!vzj)lEb7P5BeuQudYUVZMrQlO9%UQL z4lDLK%KDDRVf+7#K6~~Yu=!{3&-ShD$7#;PcuFTx7PT9-8X+p1iH$qj8`IfOL(}$y z&CuGgb@r&({>=DAGum&_bHN_dF=$M3k=yn*6Wb#@&W2xS(r@jzN9W(29sOsUwPlZ+ z?WqjsWg0y@o?~U>ik8^+_PnxZ{x~w&amGc@d)wdry3t;+$7ixTvZr&Lqemoa&pxOA zIty#-Om`%99FOP`uo2qx!g}oQvmHm@`m{0k$79cCdzSSdhaJ6QuRnW^Sx@#XuzS{n zy+-UgW{;As>%TsuKKhU2bl0}us%`h%v)`U&_B!g<>u>j;#*Szo_4~HAcjRu*R@;}+ z2<=gh_Qrp2H#*9X=e}m|kR@)Mj*i>W8E^mdU-Uk|V~!KO_xyVPX?vspKDpyA(mzI@ z!kxuF4d%OE>(|>+YJ1f_{n&ffj;AqPlNV;{M+ZfGGes%ej7#9SN}bK zhb{Z8Wmk&qY{7af`z0FdE!vy^X@>C6Pl)!()INulVrPNdE_YGQV?Wp3E$!2!)wzWI z{%4lxdVtMg$FchFjzNDe|6Tvv`FI&;Q1qFq(DksLPmaKs&CEs`eLm~&-{|p}?dt5D zwLePRk2}sjJI5aF+WvYTT26_bsYlPL=t_Vsvwr3{pR7VoaEozviPA1}{<6FoNhr(D zkIc+3Dqe8dh{*q7L36}_0XrJAdYBuDTK;7jb-)q-@xK8Be$o8DUU23$u4j7P`Uplw2%q?-EU65az&*JW!(%gCF`FV+v*`*{;Ggfd} zPfU!I7e{jET^uQ4QIs{&;+f^F;1gbBnoN{$1LU@{3FIXXoOG>%j)~n@)+$&Ar&#pIe+) zIJ?lEXt_n@oO*0vG&e8LW$yaey)eivCFc1>xuq`ky!^7lIrCh1&nddNq=2nbcZTN9 z!bq8IVSC7(WB=?addB5NJ1n;-vcteX8R?6#Kb9R932|Q0#gW3_oa*#KY5u(2xzQtL zH_9S(rad1GZyU0$hzzxt?oiil9G#55Yi4FXSCh3m zpW|SU-9^QP?)0$j@+-=@Y9hHMC0t^;GmE$ciu>}%@^8DoydbwcQjl9l|L4#9OqmcFGx@B@X%i=>B}T?)XHFeIZCYf? z)X2n1nHdwuQ#Nt(xQyv(6DOY%8B4p#Q?eo%6DLi~!f4hM*R;>n#PPJ75ScW7>bP_) zj~P2LV`A1>iIE8tvnE@c6EHd^k~wB-*2HnsGsaAfWKN%&Ic3^-Y@}g!^2Es#rs8G% zr12zyp>b0(&zd^%l=Q4b8f8(K7|EJCCT;wrF;h=Vv=L4r<*5;;GJ+_v5gC7mZ9FY~ zOh!gz?8K~TSyRW4nPhcs@0>Du$|T!I(33q)`qJ6a9k#(K<0p@wIwm7AGA(obxQTXyew;XU{5YaTlfI`XD8&eZV}D;21YLO z-wp`B6jhB&`_mXq_Qg>tHtn$(Q(}nWL9NSae?-(&k=ZuIjDYGxj|95XiN4oX+3z*Z6 zgy`y41#>;SI$J{7Z`b+$^E=d$3kokN99hV4STUlcpk!peFdK;PJxuuN>ZUJN>-=}^W-8P8_RJ$~CIuEvXd15*X z7-zDGbQZasBQxY&IghW-&0&hZmha`Sl4|*@+#=olm8I+X8$~PSPqJERxkldPFNobF zU&+^U9pBPiBTvga@};bjd*mDGk#G5C|NG=Qc~_{8qU^{w|y3ZyexH%dvPiC!E%LcM&R;OU)M4syHC%p>*HxlALXA-T9anXvI!Ya_MyX@ev3#d|vO1ohta)Glp-xaK>O^&t zI$5QvF>0(Dr_$7TH9?)C($z$1ln2$R>NJ(1CaKA4ipo@{tEp<5{3zRGvwWbk)O2-* zI#XqMQVYnP#3F9)TL^nx=dZJu275kPW&s?VpXZCRJB^7u2MDXYITjeRxMT6 zsq5A6Rjs-~-KcI-%hb*47ImwtQ@5$x)pE5$tyHU2y=qW*s6VJbs@3XFb(gwZtx_8U>QHZ}H`QCJQ@yR;QSYiQ^`3fP z{X=b5AE*!2N2*(WtUghnsx9g>^||^&ZB<{YuhiG7M}4EdRo|&z^}YH*{iwF7pVZH4 zyB4jq)<%2U*MSaojNVBP&^zmadKbN`-c85q-Sr-NPd!NQrT5nR=s3Nv9<2A%L-hXo z0DYj2=!5i7eXx$#!}KBgP@SL;(}(NfI#C~?N9d6{Ngt_?(nsr2`WStzK29g=XY=zI#rL+WA!+lrpN0E`V^h6C+bu6X*xqs(v$TRovBaPQ}r~RrKjsN^qD$a zpQX>%=ja*wTz#JYozBs@dZwPG^K`zRt>@?hU8v937w95ASI^VMx-6>d_qtZ!pl{SS z>1Fz6eT%+T*Xi5z?RvRhp;zivx?VTvJM3j6Odab@s->)Cg zjru|TkbYP?J9oi{k(obZ`3d9m-NfJUB9AV)vxJI`fvL0`gPr*-_URBw{)j|Tfd{<)m{2M z{l5N(-mE{+AL@^ExBggvqCeGJ^k@2W{e|ADztmspuXT_9Mt`fn)4lq8{e%8dZ__{N zpEZ9R%P6CbF`n^FU_ujPb}|FZ&Ss$5#q4T!GqGlOvxnK!3^IF}z0E!*&g^RjoBhlX zv%fjO9B3luAT!h)Y~sx@bBHWN=1Q~JRGKPNZI+m;OpUqPTw|^^OU-rWdh>fz zYi=+%nw!irbF;a{+-mB~ZRU2f+^jGw%_>uG8q6K$59W_%wYk&WW$rd>%%99X=3cYb z+-L4L512;tpn1qVY}T1a%%kQp(`5c^9yfn6>&;)y6Xr?NY@RYL=4sPvo-u9aS+l`B zXP!4Nn2qK|^OAYlw3}DVtL8Pc$^6ay-Mnr(%p2xS^Oor}Z<}|_yQa&$XWlpeFq_Q> z=0o$5={6smPt2!gi}}oaZoV*E&6nmY^R?+Q-+96@b>fud3$+#d;55C-oD;oZ$EE{x4(CQ zcc2&X4)TV22Yd0}Fz*oWP%ptd%sbp0?j?Fhcq6=#{J`ds-cjDs-YD-F?^y3RFWEcZ z8||IorFbWLCwV7(soofGtT)a}^TvAe%Z=!dqcbb>sP4XstQ@l*?bZ@FR&CBwp zduMoOdfDDt-r3$c-VEz5!UXGXR&Gcq@d0xIZ+neJRc!l2i-UVKfH`klz6?-M# zg^$+&r{bBwg{-J(?f0%!`Kip6BkMKwM zBmE@*NdGARXn&M{jDM_uoS*C;?~nFR@KgK~{geEY{ZxO9Kh_`Tr}^Xk3H~X5x8q)*uTWT)L-ad=3nk#;V<&9 z^cVY;ewAPCFY&MPYy7MIYy4~drT%sP_5SbuTK@+BM*k*%nSZl?i+`(M=ilbv?l1RO z_$&QYe!bt|-{Jqk|D(U!ztg|VzuRBq|H;3{zt>;u-{;@&Kj1g|5Bd-J5BuxVN5f<$vw>_}}>7`rrAz{`dY5{*V4P|0n-ve|sQ-3UpusFYtpP2!ohl zr(i&^b1*R2CD=9CEr<z1$zhk1aZN>!Qf!OU`Vika6oWi5D5+nh6V=* z@xid*kl@fDAvi2JJQyA%21f)Vf{{T|aAa^)aC9&#I3_qYI4(#Ijt@o$Cj=?MiNQ(1 z$w6u`CKwxx3(|t|!Gz$HAU&8EoEn@KWCW9f$-$H$GdMk%8cYkag6Y8-!I?pJa8_`3 za858II5#*i_+5|_P!!A!<^{z;NpN9M8k7a)!TjK& zU_nq3TpU~yTpBD4E(4;}~_g9n3$f`@~3!6U(=!DB&F@aN$1;4i`Y;IF|G!IMFA@Kn$eJRP(K&jfA3 zv%!Yox#0QWgU61*C`7HkUs7W_SUJ?IGD2;L0d3Oa+ggLi^=gRbDc z;Qinq!RFwD;KSgfpgZ_D_$2r=*b;med>(ueYz@8)z6!n$dV+6)Z-eiG-r)P-hv3Ix zTkuoxbFe*>P=z`)p%?mL5Qbq)xKlVF+<=?h@`A?iR*|yN7#(dxnF;y~4f2eZshK z-*9laUpOS(KRh5jFpPu;g+s%G!}xGmcu06?m=GQo9v%)46T>6I5#h)%DLgVfDm*$I z6&@2F8y*)XhsTGb!xO@k@Wk+>@Z>Nx921TW$AxL(_;5maN|+u_3{MSD3p2t=;pA{i zm>HfPP7SApS>g2XjPT4bJ3K2qJ3J?x5uO{K7yd5H33J1l;jAz(%nxUWbHakKFg!oJ zAS?>!hV#PWuq3=NEDg)T@^F55QMe$i2rmvV2`>#7hL?qxhgXD)!YjkYVP#kqR)hPNI+Hh%jU3h)?`>-~=A-plXDO?ub9NrS%8rFrkg|~;x!xiDma8+0zHiUPC ze+d7`-`adQTpivS-X)L9W6~PlE$ieFc_v)L-^Ff`tHM8p_k{P#z2VyMzVQC=fv_=r zP};(W!iU3k;UnRr;bUP__-B42;K}gu@Gs%|@UOByd?I`@Y!06aTf(Qq*6^9IEqpfI z5Iz?^AHEQ73||ai3SSP}!&kyr!`H%1;ori)hp&en;Tz$b;ag#6_;&bC_-@!0z8Ah9 z{v+HReh_{beiU|xABUfWpN3n)&%)2cFT$>9IMOl-{VF?+=988awmub91M zY0N$`aWVVG4360^W=PEbF$csP7!!#(C}wEP!7=eM!(tAJIW#6A=CGK`wx_nf)$5%HtqBjZQM>*BF^JnqH)xQ?6nsQBpknE2TE zxcK<^G4ToUiSbGCW8;(KQ{q$O)8fa)r^k6u&usOZ?XOZSmXV zcf{|E-xa?*eoy?~`1bgY_+&$Ky}LpNu~h ze>(n5{Mq<(@#o_ xfR6n|NEGu;w@CH|`XG{)EBugBkrzZrjP*{$)nWp~W);_t-Y zjlUOvKmI}d!)33Ie-!^X{>ic%;-AJpi+>*fBK~FktN7RPZ{pv^zgu=&{QLM1@gL(q z#ea_f68|;+Tm1LB&@ z#u;xCv%;)2dze*bPqW&rF?*T&nERT&%|7OS=KkgZ=7DBkv!B`DJjguQ9AFMK2bqVM zgUv(D!_33YA?6Y0P_xz?W=2eEGLxIalxEa)O)!-?+&t18VIF0UG>{RR$>u3$ z!mKwN%%qtzXPb>?lbJT>n9XL!Y%yEStl4JHHQUXc*8tGe>Q(He>HzIe>eXy_nLp2f0=)q1@j+SA-tR2 z-9~oEcI>cSZewe#wa$8**cEoA-NUZ3d)n1@jor)M$KKcOZTGSFv-e+ihkby3pxxK* zXZN=cvJbWg*aPiB_96CQ`%wEZ`*3@ReS|&KuC<5R5u4h~=C-h<9kpE>Y-JC(kF-bF zN7*CoqwP97X2)&M_HAt&dz3xe9%GNS$JyiUW9$j`M0=8btUcMDVo$ZF*~i(_?c?nk z_DuT(`$T(|eUg2$eTtp1>+J?RX{YSjcB9>7r|mg*vz@V9>{dH#x7l;;b~|Tx*m--N zJ>OnnFSHlgr`o64i|y0xGwdbyQu|E%EcR{J7*n|-l;iG8VknSHr^g?*)cm3_5+jeV_soqfH1gMFiY zlYO&&i+!ton|-@|hkd7gmwmT=kA1Jb-QHo}XYaJ{w;!+{v>&o}*}LtB?MLiK?LGEm z_T%;w_LKHg_S5z=_OteL_Ve}&_KWsQ_RIGF>{skp?bqzr?KkWw?QiUF?eFaG?H}wP?Vs$Q?O*I)?ceO*?LX|j z_Mi4&_TP5F{>LqIySd$6K?xF5s?&0nb_Xu~WTk8&UBQABB%U$70 zH|n}BxXK;w9_fy7k8(%4N4s@y%#FLA>$}=D?kIP(JH{RBj&sMm$G8*RiS8u#Sa-5J z#hvO-bB}YUyT`jT+?nnP?uqU!_aygZ_Y^nb*1HXE(oMOu-A1>`O}lg4W;f%uxUFv1 zZFA?k?QYKPaP#gwcfPy8UFa@yPjydo7rUpsXShq;rS6&TS?<~HGWQ(!Tz9#aE za?f*DyKCIF?mBn9yTLu*-RNH6ZgMxfTigrXt?otcHuqxp68BQ~GWT-#3inF)D)(yl z8uwcFI`?|_2KPqyCiiCd7WY>7HurY-4);#?F86Nt9`{~%ySu}^&)w^u@=sx7` za(BBAyN|e!x_jKm+{fJ~+$Y_q+^5}V+-KeA+~?gF+!x)K+?U<|xv#jdy05vfyKlH} zx^KB}yYIN~y6?H~yC1k8x*xe8yPvq9x}Uk9yI;6px?j0pyWhCqy5G6qyFa);x<9!; zyT7=e$;n;@RdK@KhhuJALWnqkM`^Qm>>5&-}kj|{89dBe~drYALozv zkMSq?6a7j4vHoO#ia*t#<{#%z_mB5y_%r5Y<)7!T_Sg7p{dN9&e}jL%ztO+I-{f!hxA+(OTm6gtZT`jnCH|%U zW&Y*<75UiISnDlME-z zlQ=PnOYn0zR? zE4e%QaPpDlqscwV$C8gHpGZDw=FZ-+NtT*SZCyULWn#nj*{#bb)DDlW-#&HT)bK=W zmXFPDlobq{mrtm5y$#dbWlhuBGgBA14ZDtQPrl`x*dWWHcGwL&4~P33Cgj$`lWO`B zXJW_lnt-x~YY=$2)__y3vGvYR+o_$0%WH};rFOX1J5II69kr_;WXpsW)^A+mxTAM{)@|B#JaqK>iS0w1ogPWU2sXuL*c@A6OYA7Niw)QcJBA&{_ON|y zjcwFc#IJ~75w{|4Mcj(G6>%%#R>ZA{TM@S+ZbjURxK-0RQ5JK^-0k3BEyEaf9wYV` zvB!u#M(ivcTQ_#&hLsjPJPFTJx=U#VviGhoY>>U9w+uV zvB!x$PV8}Fj}v>GdXLlIJ=(iR{2uXp#P1QmNBkb~d&KV%zeoHY@q5JY5x++0UK70`dPDSv=nc^uqBqpOq4o{6Z;0Pic5HwoI>;w> z9xflN+f&OYrdsPxoST`DHB zmL?m~(qtpr8rg`pMmD0Yk&S3;WFy)d*@(7AHlnSOrNmE(pAtVMeoFk5_$l$zu0Lf7 zJo~vNj@BrpBq=3HDM?C6Qc99il8lmMlq91h870XmNk&OBN|MnM87+~~5*hI`;%CIq zh@TNZBYsZ&ocKBMbK>X3&xxOp#)9UuBz*21t#D2Oate@BfSdy46dA0_@M@kfb2O8imcj}m{B z_+8?6iQgrDm-t=ccZuI6ewX-N;&+MPC4QIqUE+6%-z9#R_{e}P5I+z<5I+z<5I+z< z5I+z<5I+z<5I+z<5I+z<5FZ(kAp^3C_{f0_IglX-GUPyp9LSIZ8FC;)4rIuI3^|Y? z2QuV9RuO-U_8+7ENP-MWkRb^&BteEG$dCjXk|0A8WJrPxNsu84G9*DZM*YW#KTdpP zL53{IkOdjCAVU^p$bt-6kRb~)WI={3$dCmYvLHhiWXOUHS&$(MGGsx9EXa@r8L}Wl z7G%hP3>lCi12SYlh78D%0U5NPLHilBpF#T>G@n898MK{2*BNx3LDv~{ok7=G&2w4v zT-H375OxM(XApD-F=r5S1~F$4an^K=Z2UQIu+d<>Y3Xxt)M9Nul9^WUFFB*5BZnw+ zh%$#L^O2Bjk{30#n^Bg0O2FpLy|cpnM0B}B$-2!IV71wk~t)qXPFlgYPW;!suWC_!<0EpnZuMhOqs)! zIZTTg<`7~Iape$C4uRwlNDiRq0D2Cf=Ky*RpyvR34xr}%dJcf+0C*07=Ky#Pfal$& zvtxGa>|8QAEt`hsWaHPsvtu*cWN*vh&`ikc`Kg(y=?PzNo12!@uRR^tvf-=!`!O>6 z(jNH}w@eSxadl2xa>MW>bS7jRz&MDIyw@gH* zPRuW-G|@?$rlX$xf70BvAKYiLBrCaK(YF&j8^BL3sYxsa&}t9r(H^at*S{}*v_08r zKR>iyF7Ip{RH`#MHM3)4Irkd6Nd6EF{wQntrAmXFbvCzEnrW+~Z7|;3ee3-07fdgo z)p|wSH_Z;qhOI3VX{R;O4yhaQqis^J4f4Mfrz4C1H%|D$Z5NBR@_&lyxu^CPe{3Dw zez-lXyCc~?%}m6Lbn z0C-N`l>_8CK%N7*IhjySCX@rfIhjySCX|y2KPPeH;{knj{F zJO#W_knj{FJOv3)LBdmz@DwCG1qn||~dz!dOX0lyXSTLHfn@LK`D74TaDzZE0|1qnex0#J|u6eIuz z2|z&tP>=u=Bme~oKtTdfkN^}U00jv^f#5F?`~`x)K=2m`{sO^YAovRee}UjH5c~xK zzd+y@2>b$pUm)-c1b%_QFA(?z0>41u7YO_UfnOl-3j}_Fpf3>g1%kamuonpS0>NG& z*bC8^|& z_@RIw3izRb9}0wifzU4y`UOJ2K=u=@KFID z74T639~JOX0Us6cQ2`$n@KFIDmEd~`zL(&83BH%$dkMam;CBgrm*95^ewW~62~L*a zWC>1|;A9C-mf&OwPL|+g2~L*aWC>1|;A9C-mf&OwPL|+g2~L*aWC>1|;A9C-mf&Ow zPL|+g2~L*aWC>1|;A9C-mf&OwPL|+g2~L*aWC>1|;A9C-mf&IuE|%b82`-l4VhJvm z;9`k>FTuwWd@RAo5_~L~04kXPDwzN(nE)!m&l3DB!Os%>ESUf*nE)!m(-J%_!P62v zEy2?gJT1Y~5B?1*hAmc+KHv)DF)7u)7*Vq4uQwoO{awmMI2 ztMkORI!|n?^Tf6~Pi(96#I`yw9Z}pBOMJy$@rkdvD?afRcf}{ZPOOMee8pk$iLVnY z;uBvdR>UX1;&eKqI4zd?D^80~{dHm`9Z?(>OL-NC#izWA!{Sq3#bNO&ui~)ylviOxN*LLnJKcs!-2eI1DedP!7wVnIQ58`V( z_f`LkukGAd{V%?@b6@#Gd~N5x@`w1^&VAMYXaY4= zeCn_IUwrDX`d@tNuliqn>aY4g?JKW|rT)rm;!}U+HSwvx@|yV6UwKV@>aV;eKJ`~# z6QBBnOMT_Fw6DA-miWqR;uBwaO?=`juZd55Hc@s-!aC%*Dp+E-o^OZzLY ziBJ10uZd6lE3b)9`>XyJpY~Ut6QA~1o)e$;SDq7}_SgBUw6A<8mijB-iBJ8N@5HD6 z%6H;Zf8{&zslW1F+E-o@%XP{_;?pk5L*mme%0p>i`6cbEZWqh-I&m*P*Q<_C`|wL2 ze(A$6efXshzx3gkKK#;$U;6M%AAae>FMar>55M%`mp=T`hhO^eOCNse!!LdKr4PUK z;g>%A(uZIA@JkBBF5_@xiO^x>C2{L+VC`tU~|{^;xX^=V&qzgXH|C+gE0+^fO8 z8r-YFy&BxB!Mz&XtHHe*+^fO88r-YFy&BxB!Mz&XtHHe*+^fO88r-YFy&BxB!Mz&X ztHHe*+^fO88r-YFy&BxB!Mz&XtHHe*+^fO88r-YFy&BxB!Mz&XtHHe*+^fO88r-YF zy&BxB!Mz&XtHHe*+^fO88r-YFy&BxB!Mz&XtHHe*+^fO88r-YFy&BxB!Mz&XThwDU zxL1RFHMm!Udo{RMgL^f&SA%;sxL1RFHMm!Udo{RMgL^f&SA%;sxL1RFHMm!Udo{RM zgL^f&SA%;sxL1RFHMm!Udo{RMgL^f&SA%;sxL1RFHMm!Udo{RMgL^f&SA%;sxL1RF zHMm!Ud$sEPv{s!jR`IG703XbqpQ=JfVkt>N=qV@_Y3W;W+99u4M>(^_@DSmNs!z2YnH z)T;NzSKg_0UPpZ8om#)>6<>L$)_I<^)_ESW$~(2r^N6p!Q|mmB_{uxA&hv<`cvkB? zkNDIdzNq1g8osFEiyFSD;for+sNstmzNq1g8osFEiyFSD;for+sNstmzNq1g8osFE ziyFSD;for+sNstmzNq1g8osFEiyFSD;for+sNstmzNq1g8oroXKh^f*)Oy;Ve558H zsmVub@{t-oso|3vKB?i88a}BP`9#0?O>3Q3603Y&>leS`^Ze^PQ(BYX)Z{lc`AtoJ zQO-+7Nli$?jH#PZ9O@338-_+zcHTg|Vep8d*)Z{lc`AtoJQ zO-+7Nli$?jH#PZ9O@338-_+zcHTg|Vep8d*)J-~kh7QCFTce|m?4~BWsmX3?vYVRh zrY5_o$!==0n+A4oVD|=gZ(#QZc5h(!26k^?_Xc)vVD|=gZ(#QZc5h(!26k^?_Xc)v zVD|=gZ(#QZc5h(!1~zYC^9D9=VDknxZ(#EV_HJPB2KH`X?*{g6VDAR@ZeZ^Q_HJPB z2KH`X?*{g6VDAR@ZeZ^Q_HJPB2KH`X?*{g6VDAR@ZeZ^Q_HJPB2KH`X?*{g6VDAR@ zZpd01*t>zf8`!&ny&KrOfxR2px`C}5*t&tO8`!#mtsB_6ft?%Jxq+P<*tvn78`!yl zof}xWfsGqjxPg5e*tdaw8`!sjeH+-ffqfg;w}E{d*tdaw8`!sjeH+-ffqfg;w}E{d z*tdaw8`!sjeH+-ffqfg;w}E{d*tdaw8`!sjeH+-ffqfgDRZAP4RTHah*XXR8_{w$- zSx*DoHn432+cvOm1KT#RZ3Ejjux$g|Hn432+cvOm1KT#RZ3Ejjux$g|Hn432+cvOm z1KT#RZ3Ejjux$g|Hn432+cvOm1KT#RZ3Ejjux$g|Hn432+cvOm1KT#RZ3Ejjux$g| zHn432+cvOm1KT#RZ3Ejjuxta%Hn401%QmoV1IsqBYy-}n>Mg%1DiImX#<-! zuxJB|Hn3;|i#D)m1B*7WXakEjuxJB|Hn3;|i#D)m1B*7WXakEjuxJB|Hn3>JtYE{e zU;}S9@MQyEHt=NwUpDY%v&fgs1~$4%AZ>J&fGmt|m*a_TRl3-A+=^{gy4ZHyifvW8 z*jA;AZB??^R;7we8i`{lzhaey_9^!C%%%8te)qgQnC@BdMeq7PkEGV#HXH0 zHsTXc$wqwQX{ska@sxaIl|J#5e8lG=R`L;__)0$F6JN4;B1kBtu1^V?_jAyUMbtp`+AkzIT$ zr-&{-l~F_&Ukjfy!c#_g%E*;3M%w$!7Zq4-+>~LM>ikres)7}(g-unlW~nMtv0BMg zm8ke!$6!lUNoJ`kK(WMSP^78=vs4wJSgmKuU`bU0%6V?U1-9BjqG}?nXZ3-2Eaw%0~9Hlx%Bj>fL({jwnEv5!Ph`Pz| z*d#j=^?1&mE$_wU*v|=aZN=gKG&SQfWjv;g$CUAyG9FVEjajOqQLH}4sftGNDG%c| zW!$EW+mvyeGHz4GZOXV!8Mmp5#w=CQD3&H?9H)%qlyRIgj#I{Q$~aCL$0_4DWgMrB zs~)YF zPSvCNZIkVexGZHzrVPoHA(=8HQ-);9kW3kpsp>%aJflT*V3w*56w6bqI#7ItqEvOD z_&Qos)q&!7Jr$VTx_)j-p9>X(@<)b{ib3%e7g802;!_BqAyqM$r9ecgVo-bvFOesy3ACD8?ePs5X@IJkF{OvsAUAScS(_ zwW0VtPC5f4KEq2jqWB7rscJ;=X$IAZ;uBvrqWCmJuhF&?-W@y{`b3B`zb$FoSPt9g zW9L=#uYB-n7uAw-o?)h1QhcQg`6`0zK{nNt@^{)rHD#8nrW8xN zsHPO3c2P|!KJiskichbv08Lt1^s(k`U$!H_UXCJ^5${vPr()b zsO6Ri6~E`@W$N2zXYvm50X z7zUfq4TrPa{j~g;iU?GBCHm~vj?`P;9x#x#8DcX-Y-T!BBDbehI#VM4`0_dVwUU{M z?fN-*GJ^**crb&^lVvV*PN zGgI4U=I8X5)#wU6wPk*0$Mm+D3pq_Mg3xS$k~`9yAT%4GDR%3Z8MZ3>EJ|rh7$?~CAn)!?pl(&mgKG_tzFXEC9PeKjLQur zGu;(IfviLtmm|&Kf_&En^aN`kWhf5HI3ii@Vi^(9B_g^^yMqg4ZT`*+bo8@2Qtr@} zAuPFoGU(U5a)FXbsT0LA>J`jOohTN+Ekd>H2K0Z4J}=SdrOp(~2Pgr0z0BGYj4rtW zB_KO4$&O2%Di*w<1PFXtxH+g}ch?O_lv%kcA5cDF<)-+Q00fkkn}ZvaOKw03NEKMR zDG0}tK&rsfO*zklK&rsfO*yZG%F<2ob=a|VQ+y>&TX{@AD4F4YoyD$c*zq? zQo(x9jP;%w>pjKtc#u@E-c!yiWwYK>d^(4ug7uzqo~MJPg7uzqp3WhuV7;fDmjSYE z{mkr!&58;n6|DEn2J1aVYA^Y_svXvQ%HO-4rGF(lQv;<;4a}Uq^zS^H@Lnmrw|$5H zeP)Nemtb{!Apj`smC7@l7Rl`S1+EwJODF>5vgWGQxd84fh zYL}pP3D1?JBBi7v@RAf@CW?dP`>PjZmfCyy)1 zK}vFvk{qNY2Pw%xN^+1=a*+1(GiPgg$U#bSkdhpvBnK&b)v$^YF6W^m^(cFF+c5g= zSXS!nv{;^bot+jRK+^Ai#pjtL!zlY*Grje^_3~?9qQc~8X-Kk+k}RVn%P4i`TJ8lX zk!6%*86{apNtRKPWt3zYWnEeke_J)E){aD@B+)2IG)fYUl0>5<(I`nYN)nBdM583p zC`mL*5{;5Xqa@KNNi<3ljgmy8B+)2IG)fYUl0>5<(I`nYN)nAyx4sQ9vaQc35_>eS z%$z9d_9yB@pP#qWiRR{2osZ_t@Y(XtEkyrlkq#|!wD(4#lTl`#Mxm3@qT89*g zGKn)fnhnXX5U%HB(b+U5zmB$ia(ZIR;!N@=^EacTmD@5uH%KK0iO-59$BR?QqfFt9 zg0`bf;fykc(?y+id7isG&t0B|E?wHCOS^PYmoDnkMP0h6OBZ$NqAp$3rHi_BQI{_2 z(nVdms7n`h>7p)O)TN7re%gi!eE$%boea!Q1~gni!v!>4K*I$zTtLGGG+f~Ohrss_ zf!WP~gbRHC5cvKfpx}ajk1ubB(7p~NlmP)35O6_P>B)5}9RjnWfmzW&ZW;7@eYsBg zAt2uZ@+~0W0`e^&-vZfYAlnS6w}5&JsJDQ63#hk%dJD|92C~gSwiys_0r3_PZvpWZ z5N`n_3w+5D&~AY*IRe@(Fth^NEuh_ke$Sr;W_bhIYe2dMW_bhBEg;B)UHs|vbGPkj1cSLunb@;xx+8$gS|ly6|lH!$TJnDPxw`34{(Fy$MV z@(oP+2Bv%icoD#h0A2*}A~59}P-g*k77z>pbrzWN4XCq#Itxts2Gm(Vodu?R1L`cG z&H_`ufhpg>S2qE57MSu4O!)?;d;?Ryfv;`?U)==0x(R%B6VPXYuWkZg-2~<(0{Sd4 zFACLK%oT`S|F zKnn=8fItffw17Yh2(&;}9muK!S#=<*4(PLhJ`3oxfIbUM6$hq@1NtnW&jR`^pw9yO zETGQ= zEuhi@yc|$z0hJa|X#tfMmn^0sas0e}Ml3{4XhLi?0>@U%~$s{9nQU75rbp{}udS z!T%NfU%~$s{9nQU75rbp{}udS!T%NfU%~$s{9nQU75rbp{}udS!T%NfU%~$s{9nQU z75rbp{}udS!T%NfU%~$s{9nQU75rbp{}udS!T%NfU%~$s{9nQU75rbp{}udS!T%Nf zU%~$s{9nQU75rbp{}udS!T%NfU%~$s{9nQU75rbp{}udS!T%NfU%~$s{9nQU75rbp z{}udS!T%NfU%~$s{9nQU75rbp{}udS!T%NfU%~$s{9nQU75rbp{}udS!T%NfU%~$s z{9nQU75rbp{}udS!T%NfU%~$s{9nQU75rbp{}udS!T%NfU%~$s{9nQU75rbp{}udS z!T%NfU%~$s{9nQU75rbp{}udS!T%NfU%~$s{9nQU75rbp{}udSfm36Q(=o>B7~^z| z=X8vEjZv?0aAO?Y7za1T!HscnV;tNV2RFvSjd5^e9NZWOH^#w@amq7JdB!QvIJhwm zZj6H)KReYThVHc|SI!DYdRPl8lj$NqYtIlQ@s`#q2*@Y^;>TGtQ%3Ilp zPk*rsRnDue<0T`^NTx0+$8k+OjaH_}Y>%sQB8F?5mZ9(OLrb)rzkrU|+5HS_1afimxrezFP6M z1=v?BzP14SYQ@(UU|+5H+5)U_ZdlC zQsb2xuhc-L1}ZgBsewujRBE771C<)6)Ig;M8r49f8fa7ljcTA#4K%718`XHDZ9Mtr zc2IZu;#N%?NM5v~UK>bWv(tLobn zo@`4kZ_H^uZ5Me@j(U2tvMsK>Bu7tbakY8mjX8Q!18MWf>vHs@mRg%f-j&my)aH>l z=BTHI*XEJe<>*O`r_Cep%F&bBJfj+KRO5}d6&oFEKQ}toey-bZuk7~QE4%&n%5J~C zvfFP<*6p_?>-O6w==R%^$+Z3;P`6Ial z?fhMmPH*s|BVI=Sh`eTX?S_fDsZNUurZ2BzY=5J~AHC$)DO`Vce*Mf~ZOM64EBUdG zwX%X`@Mi9ngS%v-VbOPTUT?8>TK`t{7ymZ!Wx?D3_`UJPzb}_x&R8q!SM2)j6YW=w zv+|=+gF=qTR{|pi#{%~wf#dFy^Mh;BCD)HExqk8d;IoV8^Z)s5kLw5L7w_MbQKRYUnG;`+g77msCu_DHwWHbzXNWbI|LK6B7Q z!{@G@+p=MsZC}@^hwVEr+r#A-OzWxk<(utcEN{HjbM0F%+ryP?P>}VH?IW=2-1(`w z!Gg8HipciN;0X~G8d_(itQXxPtK8Pl%(Wk1(f-uO+QCct2hV}k->zx9Z?gRvo57)$ zxh|F*mxJS#vgmAbe#4HnlhbqCW+pBiJQ;%peT)98xp{dT>7X!+XZBcJ-L!Z@Ka%zS z$Crm|$t@+d{2%{+|6VWteNa>>!r0oW3)*70$2(@XdENc7c5ZIRg{iFH9_#^8M|P&j zPL;g)FR%PBZ}h*su6k{=%6^R2Tej@>-7i`9{f{q1A6p&W^U(u4_e@6jtQ-31fzd~j z=)-p(-1+ciboas0U1vofx;T2ji|%yM`|eoNd0!UYu_n4bi{85sy=NhM_d@i}h3Fj% z(c9m4ROjvYMsGVRdh0^;mW!h|FGO!z5xwyZzVpU4(Hng9`Yd|gWc1p7qgQ3oD_=3$ zdF4X%ipl8ZH?8Qr{GjM%7ue3r4vJoST=bGXq8G1?Zo7Qn&TR|Pi*DVc^P&evx9${3sxP7;E>&(#g*V)eXNp$_~yDeLH%+Pf4j+1#L z&Pf+XC$5Z67+W_U9q*!JC!=E?9CwbnDLOifj*9l^9JM-X_KfP?ZKvKR>i1T6`kSNP z_#U0!>S%n-b;kFI#@3n6n2Xk#Xx*S4PZ@gjP0^7Kx)^2~Pf)lrt3PG+M_ z`zJNg$YFbTM*7iVYxn9Lws&;cT|;a4wVkzlMQbk^I@C;d4!tRQ#35@sk2o$mL@IH} zn&^;qLl2jtKYTKJ*h5D;4;zafDg}G!NOZ86gBPNQ93CB%?AT03dq?} +#include +#include "stb_image.h" +#include "stb_vorbis.h" +#include +#include "debug/logger.h" +#include "render/render.h" +#include "cgltf.h" + + +Buffer read_file_into_buffer(const char *filename); + + + +int wf_face_index_comp(const void *_a, const void *_b) +{ + wf_face_index *a = (wf_face_index *)_a; + wf_face_index *b = (wf_face_index *)_b; + + if(a->vertex < b->vertex) return -1; + if(a->vertex > b->vertex) return +1; + if(a->texture < b->texture) return -1; + if(a->texture > b->texture) return +1; + if(a->normal < b->normal) return -1; + if(a->normal > b->normal) return +1; + return 0; +} + +bool assets_load_object_obj(asset_manager *am, const char *filename, r_object *result) +{ + wf_obj_file obj_file; + wf_mtl_file mtl_file; + bool has_mtl; + + char path[512]; // @Buf: Files with paths longer than 512 bytes will fail to load + p_file file; + u64 size; + Buffer content; + bool success; + + // Load obj file + strcpy(path, "assets/"); + strncat(path, filename, 512 - strlen(path)); + p_file_init(&file, path); + size = p_file_size(&file); + content.data = (u8*)p_alloc(size); + content.size = 0; + p_file_read(&file, &content, size); + p_file_deinit(&file); + + success = wf_parse_obj((char*)content.data, content.size, filename, &obj_file); + p_free(content.data); + + if(!success) + return false; + + has_mtl = false; + + // Load mtl file (optional) + if(obj_file.mtllib_names_count > 0) + { + has_mtl = true; + char *mtl_name = obj_file.mtllib_names[0]; + + strcpy(path, "assets/"); + strncat(path, mtl_name, 512 - strlen(path)); + p_file_init(&file, path); + size = p_file_size(&file); + content.data = (u8*)p_alloc(size); + content.size = 0; + p_file_read(&file, &content, size); + p_file_deinit(&file); + + success = wf_parse_mtl((char*)content.data, content.size, mtl_name, &mtl_file); + p_free(content.data); + + if(!success) + return false; + } + + + + // We take only the first object + if(obj_file.objects_count == 0) + { + LOG(LOG_WARNING, "Asset: cannot load model. File does not contain any objects."); + return false; + } + wf_object *object = obj_file.objects; + + // Setup model + r_mesh *res_model = assets_new_models(am, 1); + memset(res_model, 0, sizeof(r_mesh)); + // Actual conversion + { + wf_face_index *unique_indices; + u32 unique_indices_count; + + unique_indices_count = object->faces_count * 3; + unique_indices = (wf_face_index *)p_alloc(sizeof(wf_face_index) * unique_indices_count); + memcpy(unique_indices, object->faces, sizeof(wf_face_index) * unique_indices_count); + + qsort(unique_indices, unique_indices_count, sizeof(wf_face_index), wf_face_index_comp); + u32 removed = 0; + for(u32 i = 1; i < unique_indices_count; i++) + { + wf_face_index *prev = unique_indices + i - 1; + wf_face_index *curr = unique_indices + i; + if(wf_face_index_comp(prev, curr) == 0) // Remove duplicate + removed++; + unique_indices[i - removed] = unique_indices[i]; + } + unique_indices_count -= removed; + + res_model->vertices_count = unique_indices_count; + res_model->vertices = (v3*)p_alloc(sizeof(v3) * unique_indices_count); + if(object->texture_coords) + res_model->uvs = (v2*)p_alloc(sizeof(v2) * unique_indices_count); + else + res_model->uvs = NULL; + res_model->normals = (v3*)p_alloc(sizeof(v3) * unique_indices_count); + for(u32 i = 0; i < unique_indices_count; i++) + { + wf_index vertex_i = (unique_indices[i].vertex > 0 ? unique_indices[i].vertex - 1 : 0); + wf_index texture_coord_i = (unique_indices[i].texture > 0 ? unique_indices[i].texture - 1 : 0); + wf_index normal_i = (unique_indices[i].normal > 0 ? unique_indices[i].normal - 1 : 0); + + res_model->vertices[i] = object->vertices [vertex_i].xyz / object->vertices[vertex_i].w; + if(object->texture_coords) + res_model->uvs[i] = object->texture_coords[texture_coord_i].uv; + res_model->normals[i] = object->normals[normal_i]; + } + + res_model->indices_count = object->faces_count * 3; + res_model->indices = (u32*)p_alloc(sizeof(u32) * object->faces_count * 3); + for(u32 f = 0; f < object->faces_count; f++) + { + wf_face *curr_face = object->faces + f; + for(u32 fi = 0; fi < 3; fi++) + { + wf_face_index *curr_index = curr_face->indices + fi; + wf_face_index *found_ptr = (wf_face_index *)bsearch(curr_index, unique_indices, unique_indices_count, sizeof(wf_face_index), wf_face_index_comp); + res_model->indices[3 * f + fi] = (found_ptr - unique_indices); + } + } + + p_free(unique_indices); + } + + // Setup material + r_material *res_material = assets_new_materials(am, 1); + memset(res_material, 0, sizeof(r_material)); + res_material->shader = &r_render_state.shader_pbr; + + // Load textures + if(object->material_name != NULL && has_mtl) + { + // @Feature: Load multiple materials + wf_material *material = NULL; + for(wf_size i = 0; i < mtl_file.materials_count; i++) + { + if(strcmp(object->material_name, mtl_file.materials[i].name) == 0) + { + material = mtl_file.materials + i; + break; + } + } + + if(material) + { + // Load diffuse texture + if(material->map_Kd && strcmp(material->map_Kd, "") != 0) + { + strcpy(path, "assets/"); + strncat(path, material->map_Kd, 512 - strlen(path)); + + stbi_set_flip_vertically_on_load(true); + v2s size; s32 channels; + u8 *data = stbi_load(path, &size.x, &size.y, &channels, 4); + + r_texture *texture = assets_new_textures(am, 1); + *texture = r_texture_create(data, size, R_TEXTURE_SRGB); + res_material->albedo_texture = texture; + res_material->albedo_factor = v4{1,1,1,1}; + } + + // Load emissive texture + if(material->map_Ke && strcmp(material->map_Ke, "") != 0) + { + strcpy(path, "assets/"); + strncat(path, material->map_Ke, 512 - strlen(path)); + + stbi_set_flip_vertically_on_load(true); + v2s size; s32 channels; + u8 *data = stbi_load(path, &size.x, &size.y, &channels, 4); + + r_texture *texture = assets_new_textures(am, 1); + *texture = r_texture_create(data, size, R_TEXTURE_SRGB); + res_material->emissive_texture = texture; + res_material->emissive_factor = v4{1,1,1,1}; + } + } + } + + result->meshes = (r_mesh**)p_alloc(sizeof(void*)); + result->mesh_material = (r_material**)p_alloc(sizeof(void*)); + result->meshes[0] = res_model; + result->mesh_material[0] = res_material; + result->mesh_local_transform = (m4*)p_alloc(sizeof(m4)); + result->mesh_local_transform[0] = m4_identity; + result->count = 1; + result->scale = v3{1,1,1}; + result->position = v3{0,0,0}; + result->rotation = v3{0,0,0}; + + wf_cleanup_obj(&obj_file); + if(has_mtl) + { + wf_cleanup_mtl(&mtl_file); + } + return true; +} + + +struct gltf_loader_state +{ + asset_manager *am; + cgltf_data *data; + + r_texture *texture_buffer; + u32 texture_capacity; +}; + +void gltf_loader_reserve_texture_space(gltf_loader_state *state, u32 count) +{ + state->texture_capacity = count; + state->texture_buffer = assets_new_textures(state->am, state->texture_capacity); + memset(state->texture_buffer, 0, sizeof(r_texture) * state->texture_capacity); +} + +r_texture *gltf_lazy_load_texture(gltf_loader_state *state, u32 texture_index, bool sRGB) +{ + assert(texture_index < state->texture_capacity); + + if(state->texture_buffer[texture_index].flags & R_TEXTURE_INITIALIZED) + { + bool is_buffered_sRGB = !!(state->texture_buffer[texture_index].flags & (sRGB ? R_TEXTURE_SRGB : 0)); + assert(sRGB == is_buffered_sRGB); + return &state->texture_buffer[texture_index]; + } + + cgltf_data *data = state->data; + cgltf_buffer_view *t_view = data->images[texture_index].buffer_view; + + LOG(LOG_DEBUG, "Loading texture %s, sRGB: %d", (t_view->name ? t_view->name : t_view->buffer->name), sRGB); + u8 *t_data; + s32 t_width, t_height, t_channels; + stbi_set_flip_vertically_on_load(false); + t_data = stbi_load_from_memory((u8*)t_view->buffer->data + t_view->offset, t_view->size, &t_width, &t_height, &t_channels, 4); + + u32 flags = (sRGB ? R_TEXTURE_SRGB : 0); + state->texture_buffer[texture_index] = r_texture_create(t_data, v2s{t_width, t_height}, flags); + return &state->texture_buffer[texture_index]; +} + +void gltf_visit(gltf_loader_state *state, r_object *object, m4 transform, cgltf_node *node) +{ + cgltf_data *data = state->data; + + // Transform + if(node->has_matrix) + { + transform = transpose(*(m4*)node->matrix) * transform; + } + else + { + if(node->has_translation) + { + v3 t = {node->translation[0], node->translation[1], node->translation[2]}; + transform = translation_v3(t) * transform; + } + if(node->has_rotation) + { + LOG(LOG_ERROR, "Quaternion rotation not supported yet"); + } + if(node->has_scale) + { + v3 s = {node->scale[0], node->scale[1], node->scale[2]}; + transform = scale_v3(s) * transform; + } + } + + // @Feature: Skin + if(node->mesh) + { + cgltf_mesh *mesh = node->mesh; + for(cgltf_size p = 0; p < mesh->primitives_count; p++) + { + cgltf_primitive *primitive = &mesh->primitives[p]; + + if(primitive->type == cgltf_primitive_type_triangles) + { + // Mesh + v3 *vertices = NULL; + v3 *normals = NULL; + v3 *tangents = NULL; + v2 *uvs = NULL; + u64 vertices_count = 0; + + u32 *indices = NULL; + u64 indices_count = 0; + + if(primitive->indices->type != cgltf_type_scalar) + { + (LOG_ERROR, "glTF: Unhandled indices type %d", primitive->indices->type); + continue; + } + + cgltf_accessor *a = primitive->indices; + indices = (u32*)p_alloc(a->count * sizeof(u32)); + indices_count = a->count; + cgltf_accessor_unpack_indices(a, indices, a->count); + + vertices_count = primitive->attributes[0].data->count; + for(cgltf_size i = 0; i < primitive->attributes_count; i++) + { + if(primitive->attributes[i].type == cgltf_attribute_type_position) + { + cgltf_accessor *a = primitive->attributes[i].data; + vertices = (v3*)p_alloc(a->count * sizeof(v3)); + if(vertices_count != a->count) + { + LOG(LOG_ERROR, "glTF: vertex count (%d) != previously stored vertex count (%d)", a->count, vertices_count); + continue; + } + cgltf_accessor_unpack_floats(a, (f32*)vertices, a->count * 3); + } + else if(primitive->attributes[i].type == cgltf_attribute_type_normal) + { + cgltf_accessor *a = primitive->attributes[i].data; + normals = (v3*)p_alloc(a->count * sizeof(v3)); + if(vertices_count != a->count) + { + LOG(LOG_ERROR, "glTF: normal count (%d) != vertex count (%d)", a->count, vertices_count); + continue; + } + cgltf_accessor_unpack_floats(a, (f32*)normals, a->count * 3); + } + else if(primitive->attributes[i].type == cgltf_attribute_type_tangent) + { + cgltf_accessor *a = primitive->attributes[i].data; + tangents = (v3*)p_alloc(a->count * sizeof(v3)); + v4 *tangents_tmp = (v4*)p_alloc(a->count * sizeof(v4)); + if(vertices_count != a->count) + { + LOG(LOG_ERROR, "glTF: tangent count (%d) != vertex count (%d)", a->count, vertices_count); + continue; + } + cgltf_accessor_unpack_floats(a, (f32*)tangents_tmp, a->count * 4); + for(u32 i = 0; i < a->count; i++) + tangents[i] = tangents_tmp[i].xyz; + p_free(tangents_tmp); + } + else if(primitive->attributes[i].type == cgltf_attribute_type_texcoord) + { + cgltf_accessor *a = primitive->attributes[i].data; + uvs = (v2*)p_alloc(a->count * sizeof(v2)); + if(vertices_count != a->count) + { + LOG(LOG_ERROR, "glTF: uv count (%d) != vertex count (%d)", a->count, vertices_count); + continue; + } + cgltf_accessor_unpack_floats(a, (f32*)uvs, a->count * 2); + } + else + { + //LOG(LOG_WARNING, "Unmanaged attribute type: %d", primitive->attributes[i].type); + } + } + + r_mesh *mesh = (r_mesh*)p_alloc(sizeof(r_mesh)); + *mesh = r_mesh_create(indices_count, indices, vertices_count, vertices, normals, tangents, uvs); + + // @Feature: Calculate tangents if missing + + + // Material + r_material *material = assets_new_materials(state->am, 1); + memset(material, 0, sizeof(r_material)); + material->shader = &r_render_state.shader_pbr; + + // Find image index + u32 albedo_texture_index = primitive->material->pbr_metallic_roughness.base_color_texture.texture->image - data->images; + if(albedo_texture_index > data->images_count) + LOG(LOG_ERROR, "Albedo image index (%u) out of bounds (%u)", albedo_texture_index, data->images_count); + + material->albedo_texture = gltf_lazy_load_texture(state, albedo_texture_index, true); + material->albedo_factor = V4(primitive->material->pbr_metallic_roughness.base_color_factor); + + + // @Cleanup: @Performance: Merge metallic and roughness texture or split them up? + u32 metallic_texture_index = primitive->material->pbr_metallic_roughness.metallic_roughness_texture.texture->image - data->images; + if(metallic_texture_index > data->images_count) + LOG(LOG_ERROR, "Metallic image index (%u) out of bounds (%u)", metallic_texture_index, data->images_count); + + material->metallic_texture = gltf_lazy_load_texture(state, metallic_texture_index, false); + material->metallic_factor = primitive->material->pbr_metallic_roughness.metallic_factor; + + + + u32 roughness_texture_index = primitive->material->pbr_metallic_roughness.metallic_roughness_texture.texture->image - data->images; + if(roughness_texture_index > data->images_count) + LOG(LOG_ERROR, "Roughness image index (%u) out of bounds (%u)", roughness_texture_index, data->images_count); + + material->roughness_texture = gltf_lazy_load_texture(state, roughness_texture_index, false); + material->roughness_factor = primitive->material->pbr_metallic_roughness.roughness_factor; + + + if(primitive->material->normal_texture.texture) + { + u32 normal_texture_index = primitive->material->normal_texture.texture->image - data->images; + if(normal_texture_index > data->images_count) + LOG(LOG_ERROR, "Normal image index (%u) out of bounds (%u)", normal_texture_index, data->images_count); + + material->normal_texture = gltf_lazy_load_texture(state, normal_texture_index, false); + } + + + if(primitive->material->emissive_texture.texture) + { + u32 emissive_texture_index = primitive->material->emissive_texture.texture->image - data->images; + if(emissive_texture_index > data->images_count) + LOG(LOG_ERROR, "Emission image index (%u) out of bounds (%u)", emissive_texture_index, data->images_count); + + material->emissive_texture = gltf_lazy_load_texture(state, emissive_texture_index, true); + f32 strength = primitive->material->has_emissive_strength ? primitive->material->has_emissive_strength : 1.0; + material->emissive_factor = V4(V3(primitive->material->emissive_factor) * strength, 1.0); + } + + + object->meshes = (r_mesh**)p_realloc(object->meshes, (object->count + 1) * sizeof(r_mesh)); + object->mesh_material = (r_material**)p_realloc(object->mesh_material, (object->count + 1) * sizeof(r_material)); + object->mesh_local_transform = (m4*)p_realloc(object->mesh_local_transform, (object->count + 1) * sizeof(m4)); + + object->meshes [object->count] = mesh; + object->mesh_material[object->count] = material; + object->mesh_local_transform[object->count] = transform; + object->count++; + } + else + { + LOG(LOG_ERROR, "glTF: Unhandled primitive type %d", primitive->type); + } + } + } + + + for(cgltf_size i = 0; i < node->children_count; i++) + { + gltf_visit(state, object, transform, node->children[i]); + } +} + +bool assets_load_object_gltf(asset_manager *am, const char *filename, r_object *result) +{ + gltf_loader_state state; + state.am = am; + + cgltf_options options = {}; + cgltf_data *data = NULL; + + cgltf_result status = cgltf_parse_file(&options, filename, &data); + if(status != cgltf_result_success) + { + LOG(LOG_DEBUG, "Error parsing glTF file \"%s\".", filename); + return false; + } + + status = cgltf_load_buffers(&options, data, filename); + if(status != cgltf_result_success) + { + LOG(LOG_DEBUG, "Erorr loading glTF buffers from file \"%s\".", filename); + cgltf_free(data); + return false; + } + state.data = data; + + // Reserve texture space + gltf_loader_reserve_texture_space(&state, data->images_count); + + // Prepare object + r_object object; + memset(&object, 0, sizeof(r_object)); + object.scale = v3{1,1,1}; + object.position = v3{0,0,0}; + object.rotation = v3{0,0,0}; + object.has_shadow = true; + + for(cgltf_size i = 0; i < data->scene->nodes_count; i++) + { + gltf_visit(&state, &object, m4_identity, data->scene->nodes[i]); + } + + // Resize unused space + + *result = object; + cgltf_free(data); + return true; +} + +bool assets_load_audio(asset_manager *am, const char *name, p_audio_buffer *result) +{ + s32 music_channels; + s32 music_sample_rate; + s16 *music_data; + s32 music_samples = stb_vorbis_decode_filename(name, &music_channels, &music_sample_rate, &music_data); + + result->samples = (p_audio_sample*) p_alloc(sizeof(p_audio_sample) * music_samples); + for(u32 i = 0; i < music_samples; i++) + { + result->samples[i].left = (f32)music_data[2*i] / -SHRT_MIN; // Left + result->samples[i].right = (f32)music_data[2*i + 1] / -SHRT_MIN; // Right + } + result->size = music_samples; + + free(music_data); + + return true; +} + + +bool assets_load_cubemap(asset_manager *am, const char *px, const char *nx, const char *py, const char *ny, const char *pz, const char *nz, r_cubemap *result) +{ + stbi_set_flip_vertically_on_load(false); + //stbi_ldr_to_hdr_scale(1.0f); + //stbi_ldr_to_hdr_gamma(2.2f); + + float *data[6]; + v2s size; s32 channels; + + v2s s; + data[0] = stbi_loadf(px, &s.x, &s.y, &channels, 3); + size = s; + data[1] = stbi_loadf(nx, &s.x, &s.y, &channels, 3); + assert(size == s); + data[2] = stbi_loadf(py, &s.x, &s.y, &channels, 3); + assert(size == s); + data[3] = stbi_loadf(ny, &s.x, &s.y, &channels, 3); + assert(size == s); + data[4] = stbi_loadf(pz, &s.x, &s.y, &channels, 3); + assert(size == s); + data[5] = stbi_loadf(nz, &s.x, &s.y, &channels, 3); + assert(size == s); + + r_cubemap cubemap = r_cubemap_create(data, size, 0); + if(result) + *result = cubemap; + + return true; +} + + +void assets_init(asset_manager *am) +{ + am->shaders = NULL; + am->shaders_count = 0; + + am->models = NULL; + am->models_count = 0; + + am->textures = NULL; + am->textures_count = 0; + + am->materials = NULL; + am->materials_count = 0; + + am->objects = NULL; + am->objects_count = 0; + + am->allocations = NULL; + am->allocations_count = 0; +} + +void assets_free(asset_manager *am) +{ + +} + +static void assets_add_allocation(asset_manager *am, void *data) +{ + u32 old_count = am->allocations_count; + am->allocations_count++; + am->allocations = (void**)p_realloc(am->allocations, am->allocations_count * sizeof(void*)); + am->allocations[old_count] = data; +} + + +r_shader *assets_new_shaders(asset_manager *am, u32 count) +{ + u32 old_count = am->shaders_count; + am->shaders_count = old_count + count; + + r_shader *data = (r_shader*) p_alloc(count * sizeof(r_shader)); + assets_add_allocation(am, data); + + am->shaders = (r_shader**) p_realloc(am->shaders, am->shaders_count * sizeof(r_shader*)); + for(u32 i = 0; i < count; i++) + am->shaders[old_count + i] = data + i; + + return data; +} + +r_mesh *assets_new_models(asset_manager *am, u32 count) +{ + u32 old_count = am->models_count; + am->models_count = old_count + count; + + r_mesh *data = (r_mesh*) p_alloc(count * sizeof(r_mesh)); + assets_add_allocation(am, data); + + am->models = (r_mesh**) p_realloc(am->models, am->models_count * sizeof(r_mesh*)); + for(u32 i = 0; i < count; i++) + am->models[old_count + i] = data + i; + + return data; +} + +r_texture *assets_new_textures(asset_manager *am, u32 count) +{ + u32 old_count = am->textures_count; + am->textures_count = old_count + count; + + r_texture *data = (r_texture*) p_alloc(count * sizeof(r_texture)); + assets_add_allocation(am, data); + + am->textures = (r_texture**) p_realloc(am->textures, am->textures_count * sizeof(r_texture*)); + for(u32 i = 0; i < count; i++) + am->textures[old_count + i] = data + i; + + return data; +} + +r_material *assets_new_materials(asset_manager *am, u32 count) +{ + u32 old_count = am->materials_count; + am->materials_count = old_count + count; + + r_material *data = (r_material*) p_alloc(count * sizeof(r_material)); + assets_add_allocation(am, data); + + am->materials = (r_material**) p_realloc(am->materials, am->materials_count * sizeof(r_material*)); + for(u32 i = 0; i < count; i++) + am->materials[old_count + i] = data + i; + + return data; +} + +r_object *assets_new_objects(asset_manager *am, u32 count) +{ + u32 old_count = am->objects_count; + am->objects_count = old_count + count; + + r_object *data = (r_object*) p_alloc(count * sizeof(r_object)); + assets_add_allocation(am, data); + + am->objects = (r_object**) p_realloc(am->objects, am->objects_count * sizeof(r_object*)); + for(u32 i = 0; i < count; i++) + am->objects[old_count + i] = data + i; + + return data; +} + + + + + + + + + + +Buffer read_file_into_buffer(const char *filename) +{ + Buffer empty = { .size = 0, .data = NULL }; + Buffer result; + p_file file; + bool success; + + success = p_file_init(&file, filename); + if(success) { + result.size = p_file_size(&file); + result.data = (u8*)p_alloc(result.size); + success = p_file_read(&file, &result, result.size); + + p_file_deinit(&file); + } + + if(!success) + return empty; + + return result; +} diff --git a/code/assets.h b/code/assets.h new file mode 100644 index 0000000..b6a6223 --- /dev/null +++ b/code/assets.h @@ -0,0 +1,49 @@ +#ifndef _PIUMA_ASSETS_H_ +#define _PIUMA_ASSETS_H_ + +#include "lib/types.h" +#include "render/render.h" +#include "platform.h" + + +struct asset_manager +{ + r_shader **shaders; + u32 shaders_count; + + r_mesh **models; + u32 models_count; + + r_texture **textures; + u32 textures_count; + + r_material **materials; + u32 materials_count; + + r_object **objects; + u32 objects_count; + + void **allocations; + u32 allocations_count; +}; + + +// @TODO: return pointer to loaded asset, instead of copying data into *result +bool assets_load_object_obj(asset_manager *am, const char *filename, r_object *result); // Models, textures, materials get inserted in the gamestate arrays +bool assets_load_object_gltf(asset_manager *am, const char *filename, r_object *result); +bool assets_load_audio(asset_manager *am, const char *name, p_audio_buffer *result); +bool assets_load_cubemap(asset_manager *am, const char *px, const char *nx, const char *py, const char *ny, const char *pz, const char *nz, r_cubemap *result); + + +void assets_init(asset_manager *am); +void assets_free(asset_manager *am); + +r_shader *assets_new_shaders (asset_manager *am, u32 count); +r_mesh *assets_new_models (asset_manager *am, u32 count); +r_texture *assets_new_textures (asset_manager *am, u32 count); +r_material *assets_new_materials(asset_manager *am, u32 count); +r_object *assets_new_objects (asset_manager *am, u32 count); + +// @Feature: remove/free single assets + +#endif diff --git a/code/audio.cpp b/code/audio.cpp new file mode 100644 index 0000000..69b3fbe --- /dev/null +++ b/code/audio.cpp @@ -0,0 +1,156 @@ +#include "audio.h" +#include "enginestate.h" +#include + + +void audio_cb(p_audio_buffer *buffer); + + +void audio_init() +{ + audio_player *player = &engine.audio; + + player->track_count = 0; + player->track_capacity = 32; + player->tracks = (audio_track *) p_alloc(player->track_capacity * sizeof(audio_track)); + player->next_id = 0; + player->sample_rate = p_audio_sample_rate(); + + p_audio_register_data_callback(audio_cb); +} + +track_id audio_add_track(p_audio_buffer *data, u32 flags) +{ + // @Correctness: fix track if it has a different sample rate + audio_player *player = &engine.audio; + audio_track track; + + track.id = player->next_id; + player->next_id++; + track.data = *data; + track.progress = 0; + track.playing = !(flags & AUDIO_PAUSED); + track.loop = !!(flags & AUDIO_LOOP); + track.free_on_finish = !!(flags & AUDIO_FREE_ON_FINISH); + track.volume = 1.0; + + if (player->track_count + 1 < player->track_capacity) + { + u64 index = player->track_count; + player->tracks[index] = track; + player->track_count++; + } + else + { + u32 replace_index = 0; + for(u32 i = 0; i < player->track_count; i++) + { + audio_track *current = &player->tracks[i]; + if(track.loop == current->loop) + { + replace_index = i; + break; + } + } + + player->tracks[replace_index] = track; + } + + return track.id; +} + +void audio_remove_track(track_id id) +{ + audio_player *player = &engine.audio; + for(int i = 0; i < player->track_count; i++) + { + audio_track *current = &player->tracks[i]; + if(id == current->id) + { + u32 move_count = player->track_count - (i + 1); + if(current->free_on_finish) + p_free(current->data.samples); + memmove(current, current + 1, move_count * sizeof(audio_track)); + player->track_count--; + break; + } + } +} + +void audio_pause_track(track_id id) +{ + audio_track *track = audio_track_from_id(id); + if(track) + track->playing = false; +} + +void audio_play_track(track_id id) +{ + audio_track *track = audio_track_from_id(id); + if(track) + track->playing = true; +} + +void audio_change_track_volume(track_id id, f32 volume) +{ + audio_track *track = audio_track_from_id(id); + if(track) + track->volume = volume; +} + +audio_track *audio_track_from_id(track_id id) +{ + audio_player *player = &engine.audio; + for(int i = 0; i < player->track_count; i++) + { + audio_track *current = &player->tracks[i]; + if(id == current->id) + return current; + } + return NULL; +} + + +// @Correctness: audio cb modifies the array of tracks asynchronously. All r/w operations on tracks should be protected by a mutex. +void audio_cb(p_audio_buffer *buffer) +{ + audio_player *player = &engine.audio; + + buffer->size = minimum(buffer->size, 2048); // Low latency + + for(u64 i = 0; i < buffer->size; i++) + { + buffer->samples[i].left = 0; + buffer->samples[i].right = 0; + } + + for(int track_i = 0; track_i < player->track_count; track_i++) + { + audio_track *current = &player->tracks[track_i]; + if(current->playing) + { + u64 remaining = current->data.size - current->progress; + u64 copy_count = minimum(remaining, buffer->size); + if(current->loop) + copy_count = buffer->size; + + for(u64 i = 0; i < copy_count; i++) + { + buffer->samples[i].left += current->volume * current->data.samples[current->progress].left; + buffer->samples[i].right += current->volume * current->data.samples[current->progress].right; + current->progress = (current->progress + 1) % current->data.size; + } + + if(!current->loop && current->progress == 0) // current->progress == 0 because we finished and then we looped around + { + // Finished playing. Remove track + u32 move_count = player->track_count - (track_i + 1); + if(current->free_on_finish) + p_free(current->data.samples); // @Bug: double free when the same sound is played 2 times. Find a way to signal we reached the end of the track so the user code can make its own free (if needed) + memmove(current, current + 1, move_count * sizeof(audio_track)); + track_i--; + player->track_count--; + } + } + } +} diff --git a/code/audio.h b/code/audio.h new file mode 100644 index 0000000..c1d68b1 --- /dev/null +++ b/code/audio.h @@ -0,0 +1,47 @@ +#ifndef _PIUMA_AUDIO_H_ +#define _PIUMA_AUDIO_H_ + +#include "lib/types.h" +#include "platform.h" + +typedef u64 track_id; + +struct audio_track +{ + track_id id; + p_audio_buffer data; + u64 progress; + bool playing; + bool loop; + bool free_on_finish; + + f32 volume; +}; + +struct audio_player +{ + audio_track *tracks; + u32 track_count; + u32 track_capacity; + track_id next_id; + + u32 sample_rate; +}; + +enum audio_flags +{ + AUDIO_NONE = 0, + AUDIO_LOOP = 1, + AUDIO_PAUSED = 2, + AUDIO_FREE_ON_FINISH = 4 +}; + +void audio_init(); +track_id audio_add_track(p_audio_buffer *data, u32 flags); +void audio_remove_track(track_id id); +void audio_pause_track(track_id id); +void audio_play_track(track_id id); +void audio_change_track_volume(track_id id, f32 volume); +audio_track *audio_track_from_id(track_id id); + +#endif diff --git a/code/camera.cpp b/code/camera.cpp new file mode 100644 index 0000000..fe97f75 --- /dev/null +++ b/code/camera.cpp @@ -0,0 +1,107 @@ +#include "camera.h" +#include + + +void r_camera_base_look_at(r_camera_base *c, v3 target) +{ + c->direction = normalize(target - c->position); +} + +m4 r_camera_base_view(r_camera_base *c) +{ + return r_view_matrix(c->position, c->direction, c->up); +} + + +void r_camera_fps_look_at(r_camera_fps *c, v3 target) +{ + v3 direction = normalize(target - c->position); + c->pitch = degrees(asin(direction.z)); + direction = normalize(v3{direction.x, direction.y, 0}); + c->yaw = degrees(asin(direction.y)); + if(direction.x < 0) + c->yaw = 180 - c->yaw; +} + +m4 r_camera_fps_view(r_camera_fps *c) +{ + v3 up; + v3 direction; + + up = r_camera_up_vector; + direction = r_camera_fps_direction(c); + + return r_view_matrix(c->position, direction, up); +} + +v3 r_camera_fps_direction(r_camera_fps *c) +{ + v3 direction; + direction.x = cos(radians(c->yaw)) * cos(radians(c->pitch)); + direction.y = sin(radians(c->yaw)) * cos(radians(c->pitch)); + direction.z = sin(radians(c->pitch)); + return direction; +} + + +m4 r_view_matrix(v3 position, v3 direction, v3 up) +{ + v3 right = normalize(cross(up, -direction)); + v3 camera_up = normalize(cross(-direction, right)); + + m4 result = m4_identity; + + // new X axis + result.E[0][0] = right.x; + result.E[0][1] = right.y; + result.E[0][2] = right.z; + + // new Y axis + result.E[1][0] = camera_up.x; + result.E[1][1] = camera_up.y; + result.E[1][2] = camera_up.z; + + // new Z axis + result.E[2][0] = -direction.x; + result.E[2][1] = -direction.y; + result.E[2][2] = -direction.z; + + // translation + result.E[0][3] = -dot(right , position); + result.E[1][3] = -dot(camera_up, position); + result.E[2][3] = -dot(-direction, position); + + return result; +} + +m4 r_perspective_matrix(f32 fov, f32 aspect_ratio, f32 near_plane, f32 far_plane) +{ + m4 result = m4_zero; + + f32 tanf2 = tan(radians(fov) / 2.0); + + result.E[0][0] = 1.0 / (aspect_ratio * tanf2); + result.E[1][1] = 1.0 / tanf2; + result.E[2][2] = - (far_plane + near_plane) / (far_plane - near_plane); + + result.E[2][3] = -2.0 * far_plane * near_plane / (far_plane - near_plane); + result.E[3][2] = -1.0; + + return result; +} + +m4 r_orthographic_matrix(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far) +{ + m4 result = m4_zero; + + result.E[0][0] = 2 / (right - left); + result.E[1][1] = 2 / (top - bottom); + result.E[2][2] = -2 / (far - near); + result.E[3][3] = 1; + + result.E[0][3] = - (right + left) / (right - left); + result.E[1][3] = - (top + bottom) / (top - bottom); + result.E[2][3] = - (far + near) / (far - near); + + return result; +} diff --git a/code/camera.h b/code/camera.h new file mode 100644 index 0000000..bca3729 --- /dev/null +++ b/code/camera.h @@ -0,0 +1,38 @@ +#ifndef _PIUMA_CAMERA_H_ +#define _PIUMA_CAMERA_H_ + +#include "lib/types.h" +#include "lib/math.h" + + +static const v3 r_camera_up_vector = {0,0,1}; + +struct r_camera_base +{ + v3 position; + v3 direction; + v3 up; +}; + +void r_camera_base_look_at(r_camera_base *c, v3 target); +m4 r_camera_base_view(r_camera_base *c); + + +struct r_camera_fps +{ + v3 position; + // Forward = +y, Right = +x, Up = +z + f32 yaw; + f32 pitch; +}; + +void r_camera_fps_look_at(r_camera_fps *c, v3 target); +m4 r_camera_fps_view(r_camera_fps *c); +v3 r_camera_fps_direction(r_camera_fps *c); + + +m4 r_view_matrix(v3 position, v3 direction, v3 up); +m4 r_perspective_matrix(f32 fov, f32 aspect_ratio, f32 near_plane, f32 far_plane); +m4 r_orthographic_matrix(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far); + +#endif diff --git a/code/debug/log_viewer.cpp b/code/debug/log_viewer.cpp new file mode 100644 index 0000000..d3757d2 --- /dev/null +++ b/code/debug/log_viewer.cpp @@ -0,0 +1,73 @@ +#include "log_viewer.h" + +#include "../lib/math.h" +#include +#include "../gui/gui.h" + +LogViewer LogViewer::Init(Logger *logger) +{ + LogViewer viewer; + viewer.logger = logger; + return viewer; +} + +void LogViewer::Print_New_Messages_On_Console() +{ + u64 message_count = Logger_MessageCount(logger); + if(message_count <= 0) + return; + + u64 last_message_index = message_count - 1; + LogEntry *last_message = Logger_MessageAt(logger, last_message_index); + u64 id_diff = last_message->id - next_id_to_print_on_console; + u64 index_start = (last_message_index > id_diff) ? (last_message_index - id_diff) : 0; + + u64 max_id = next_id_to_print_on_console; + for(int i = index_start; i < message_count; i++) + { + LogEntry *entry = Logger_MessageAt(logger, i); + if(entry->id >= next_id_to_print_on_console) + { + // printf("%lu ", entry->id); + // if (entry->level >= LOG_DEBUG) printf("DEBUG"); + // else if(entry->level >= LOG_INFO) printf("INFO"); + // else if(entry->level >= LOG_WARNING) printf("WARNING"); + // else if(entry->level >= LOG_ERROR) printf("ERROR"); + // printf(": "); + fprintf((entry->level < LOG_INFO ? stderr : stdout), "%s\n", entry->message); + + max_id = maximum(max_id, entry->id + 1); + } + } + next_id_to_print_on_console = max_id; +} + +void LogViewer::Draw_New_Messages_On_GUI() +{ + u64 messages_to_draw = 5; + + u64 message_count = Logger_MessageCount(logger); + if(message_count <= 0) + return; + + Rect r = { .position = {0, (f32)global_gui_state.default_context.height}, .size = {0,0} }; + messages_to_draw = minimum(messages_to_draw, message_count); + for(u64 i = 0; i < messages_to_draw; i++) + { + u64 index = message_count - 1 - i; + LogEntry *entry = Logger_MessageAt(logger, index); + char id_string[24]; sprintf(id_string, "%lu: ", entry->id); + + v2 id_size = gui_text_compute_size(id_string); + v2 message_size = gui_text_compute_size(entry->message); + + r.position.y -= maximum(id_size.y, message_size.y); + r.position.x = 0; + r.size = id_size; + gui_text(r, id_string); + + r.position.x += id_size.x; + r.size = message_size; + gui_text(r, entry->message); + } +} diff --git a/code/debug/log_viewer.h b/code/debug/log_viewer.h new file mode 100644 index 0000000..66725a2 --- /dev/null +++ b/code/debug/log_viewer.h @@ -0,0 +1,20 @@ +#ifndef _PIUMA_LOG_VIEWER_H_ +#define _PIUMA_LOG_VIEWER_H_ + +#include "logger.h" + +struct LogViewer +{ + Logger *logger = NULL; + + LogId next_id_to_print_on_console = 0; + + + + static LogViewer Init(Logger *logger); + + void Print_New_Messages_On_Console(); + void Draw_New_Messages_On_GUI(); +}; + +#endif diff --git a/code/debug/logger.cpp b/code/debug/logger.cpp new file mode 100644 index 0000000..ddcafe6 --- /dev/null +++ b/code/debug/logger.cpp @@ -0,0 +1,94 @@ +#include "logger.h" +#include "../platform.h" +#include +#include +#include +#include + +struct Logger global_logger; + +void Logger_Init(struct Logger *logger) +{ + assert(logger != NULL); + + u32 initial_capacity = 512; + + logger->message_queue = Queue_Alloc(p_alloc, struct LogEntry, initial_capacity); +} + +void Logger_Deinit(struct Logger *logger) +{ + assert(logger != NULL); + + Logger_Clear(logger); + Queue_Free(p_free, logger->message_queue); +} + +void Logger_Clear(struct Logger *logger) +{ + assert(logger != NULL); + + while(Queue_Size(logger->message_queue) > 0) + { + Logger_RemoveOldestMessage(logger); + } +} + +void Logger_AddMessage(struct Logger *logger, LogSourceInfo source_info, LogLevel level, const char *format_message, ...) +{ + assert(logger != NULL); + + // Build formatted message + char *message; + va_list var_args; + va_start(var_args, format_message); + int bytes_size = vsnprintf(NULL, 0, format_message, var_args) + 1; + va_end(var_args); + message = (char *) p_alloc(bytes_size * sizeof(char)); + va_start(var_args, format_message); + int written = vsnprintf(message, bytes_size, format_message, var_args) + 1; + va_end(var_args); + + // Add log entry to queue, checking if we have enough space + struct LogEntry entry = { + .level = level, + .message = message, + .id = logger->last_id++, + .source = source_info + }; + + // fprintf(stderr, message); + // fprintf(stderr, "\n"); + + if(Queue_Size(logger->message_queue) >= QUEUE_HEADER_PTR(logger->message_queue)->capacity) + { + logger->overflow_count++; + Logger_RemoveOldestMessage(logger); + } + Queue_Push(logger->message_queue, entry); +} + +void Logger_RemoveOldestMessage(struct Logger *logger) +{ + assert(logger != NULL); + assert(Queue_Size(logger->message_queue) > 0); + + struct LogEntry entry = Queue_Pop(logger->message_queue); + p_free(entry.message); +} + + +u64 Logger_MessageCount(struct Logger *logger) +{ + assert(logger != NULL); + + return Queue_Size(logger->message_queue); +} + +struct LogEntry *Logger_MessageAt(struct Logger *logger, u64 index) +{ + assert(logger != NULL); + assert(Queue_Size(logger->message_queue) > index); + + return &Queue_At(logger->message_queue, index); +} diff --git a/code/debug/logger.h b/code/debug/logger.h new file mode 100644 index 0000000..8f25e6e --- /dev/null +++ b/code/debug/logger.h @@ -0,0 +1,59 @@ +#ifndef _PIUMA_LOGGER_H_ +#define _PIUMA_LOGGER_H_ + +#include "../lib/types.h" +#include "../lib/queue.h" + + +typedef u8 LogLevel; +#define LOG_ERROR 0 +#define LOG_WARNING 50 +#define LOG_INFO 100 +#define LOG_DEBUG 150 + +struct LogSourceInfo +{ + const char *filename; + u32 line; + const char *function; +}; + +typedef u64 LogId; +struct LogEntry +{ + LogLevel level; + char *message; + + LogId id; + LogSourceInfo source; +}; + +struct Logger +{ + QUEUE_TYPE(struct LogEntry) message_queue = NULL; + + LogId last_id = 0; + u64 overflow_count = 0; // Number of times the buffer was full, but we added a message anyway removing the oldest one +}; + +void Logger_Init(struct Logger *logger); +void Logger_Deinit(struct Logger *logger); + +void Logger_Clear(struct Logger *logger); +void Logger_AddMessage(struct Logger *logger, LogSourceInfo source_info, LogLevel level, const char *format_message, ...); +void Logger_RemoveOldestMessage(struct Logger *logger); + +u64 Logger_MessageCount(struct Logger *logger); +struct LogEntry *Logger_MessageAt(struct Logger *logger, u64 index); + + +extern struct Logger global_logger; + + +#define LOG_INIT() Logger_Init(&global_logger) +#define LOG_DEINIT() Logger_Deinit(&global_logger) +#define LOG_CLEAR() Logger_Clear(&global_logger) +#define LOG(level, /*format_message,*/ ...) Logger_AddMessage(&global_logger, LogSourceInfo{.filename = __FILE__, .line = __LINE__, .function = __func__}, level, /*format_message,*/ __VA_ARGS__) + + +#endif diff --git a/code/enginestate.cpp b/code/enginestate.cpp new file mode 100644 index 0000000..f0ccc5a --- /dev/null +++ b/code/enginestate.cpp @@ -0,0 +1,39 @@ +#include "enginestate.h" +#include "platform.h" +#include "assets.h" +#include "debug/logger.h" + +engine_state engine; + + +bool enginestate_init() +{ + u32 status; + + assets_init(&engine.am); + + engine.input.mouse_sensitivity = 1.4; + engine.input.movement_sensitivity = 7; + engine.input.init = true; + engine.input.target = INPUT_TARGET_PLAYER; + engine.input.player_movement = {0, 0}; + engine.input.camera_rotation = {0, 0, 0}; + engine.input.camera_movement = {0, 0, 0}; + + audio_init(); + + engine.time = 0; + engine.delta_t = 0; + + engine.gui_scaling = 14; + + engine.world = phy_create_world({0, 0, -9.81}); + + return true; +} + +void enginestate_deinit() +{ + phy_destroy_world(engine.world); + // @Correctness: complete this +} diff --git a/code/enginestate.h b/code/enginestate.h new file mode 100644 index 0000000..6c1eac8 --- /dev/null +++ b/code/enginestate.h @@ -0,0 +1,56 @@ +#ifndef _PIUMA_ENGINESTATE_H_ +#define _PIUMA_ENGINESTATE_H_ + +#include "lib/types.h" +#include "lib/math.h" +#include "render/render.h" +#include "audio.h" +#include "camera.h" +#include "assets.h" +#include "physics/physics.h" + + +enum input_target +{ + INPUT_TARGET_PLAYER, + INPUT_TARGET_CAMERA, + INPUT_TARGET_EDITOR +}; + +struct es_input +{ + f32 mouse_sensitivity; + f32 movement_sensitivity; + bool init; + + enum input_target target; + + v2s player_movement; + + v3 camera_rotation; + v3 camera_movement; +}; + + +struct engine_state +{ + es_input input; + audio_player audio; + + asset_manager am; + + f64 time; + f64 delta_t; + + f32 gui_scaling; + + phy_world *world; +}; + +extern engine_state engine; + +bool enginestate_init(); +void enginestate_deinit(); + + +#endif diff --git a/code/file_formats/wavefront.cpp b/code/file_formats/wavefront.cpp new file mode 100644 index 0000000..961b030 --- /dev/null +++ b/code/file_formats/wavefront.cpp @@ -0,0 +1,1323 @@ +#include "wavefront.h" + +#include +#include +#include +#include +#include +#include "../debug/logger.h" + + +wf_alloc_t wf_alloc = malloc; +wf_realloc_t wf_realloc = realloc; +wf_free_t wf_free = free; + + +#define WF_FREE_OPTIONAL(x) { if(x) wf_free(x); } + + +struct wf_parser +{ + char *data; + u64 size; + + u64 cursor; + + u64 line; + u64 line_start; + + const char *name; // Used for debug messages. It's not needed otherwise. +}; + + +enum wf_token_type +{ + WF_NONE, + + WF_STRING, + WF_INTEGER, + WF_REAL, + + WF_SLASH, + + WF_EOF +}; + +#define CASE_STRING(x) case x: return #x; +const char *wf_token_type_string(wf_token_type tt) +{ + switch(tt) + { + CASE_STRING(WF_NONE) + CASE_STRING(WF_STRING) + CASE_STRING(WF_INTEGER) + CASE_STRING(WF_REAL) + CASE_STRING(WF_SLASH) + CASE_STRING(WF_EOF) + default: return "UNKNOWN"; + } +} + +struct wf_string +{ + char *data; + u32 size; +}; + +struct wf_token +{ + wf_token_type type; + + union + { + wf_string string; + s64 integer; + f64 real; + }; + + u64 line; + u64 line_start; + u64 start; + u64 size; +}; + + +#define WF_PRINT_ERROR(parser, message, ...) \ +{ \ + u64 column = (parser)->cursor - (parser)->line_start + 1; \ + \ + u64 tmp_cursor = (parser)->cursor; \ + while(tmp_cursor < (parser)->size && !wf_isnewline((parser)->data[tmp_cursor])) \ + tmp_cursor++; \ + u64 line_lenght = tmp_cursor - (parser)->line_start; \ + LOG(LOG_ERROR, "Wavefront parsing error %s %lu,%lu: \n" message "\nLine: \"%.*s\"", (parser)->name, (parser)->line, column, ##__VA_ARGS__, (int)line_lenght, (parser)->data + (parser)->line_start); \ +} + +#define WF_PRINT_ERROR_TOKEN(parser, token, message, ...) \ +{ \ + u64 column = (token)->start - (token)->line_start + 1; \ + \ + u64 tmp_cursor = (token)->line_start; \ + while(tmp_cursor < (parser)->size && !wf_isnewline((parser)->data[tmp_cursor])) \ + tmp_cursor++; \ + u64 line_lenght = tmp_cursor - (token)->line_start; \ + LOG(LOG_ERROR, "Wavefront parsing error %s %lu,%lu: \n" message "\nLine: \"%.*s\"", (parser)->name, (token)->line, column, ##__VA_ARGS__, (int)line_lenght, (parser)->data + (token)->line_start); \ +} + + + +static void wf_parser_init(wf_parser *p, char *data, u64 size, const char *name) +{ + p->data = data; + p->size = size; + p->cursor = 0; + p->line = 1; + p->line_start = 0; + p->name = name; +} + +static bool wf_isnewline(char c) +{ + // 10 '\n' line feed --- 13 '\r' carriage return + // For simplicity, we consider only \n as newline. + // \n will be eaten as normal whitespace. + return c == 10; +} + +static void wf_eat_newline(wf_parser *p) +{ + assert(p->cursor < p->size); + assert(wf_isnewline(p->data[p->cursor])); + + p->cursor++; + while(p->cursor < p->size) + { + char c = p->data[p->cursor]; + if(c != 13) + break; + p->cursor++; + } + + p->line++; + p->line_start = p->cursor; +} + +static void wf_skip_to_next_line(wf_parser *p) +{ + while(p->cursor < p->size) + { + char c = p->data[p->cursor]; + + if(wf_isnewline(c)) + { + wf_eat_newline(p); + break; + } + + p->cursor++; + } +} + +// Puts cursor at the newline (end of comment, not start of next token) +static void wf_eat_comment(wf_parser *p) +{ + assert(p->cursor < p->size); + assert(p->data[p->cursor] == '#'); + wf_skip_to_next_line(p); +} + +static void wf_eat_whitespace_and_comments(wf_parser *p) +{ + while(p->cursor < p->size) + { + char c = p->data[p->cursor]; + + if(c == '#') // Comment + { + wf_eat_comment(p); + continue; + } + + if(wf_isnewline(c)) + { + wf_eat_newline(p); + continue; + } + + if(!isspace(c)) + break; + + p->cursor++; + } +} + +static s64 wf_parse_integer(wf_parser *p) +{ + assert(p->cursor < p->size); + assert(isdigit(p->data[p->cursor])); + + s64 integer = 0; + while(p->cursor < p->size) + { + char c = p->data[p->cursor]; + + if(!isdigit(c)) + break; + + integer = integer * 10 + (c - '0'); + + p->cursor++; + } + + return integer; +} + +static f64 wf_parse_decimal_part(wf_parser *p) +{ + assert(p->cursor < p->size); + assert(isdigit(p->data[p->cursor])); + + f64 decimal = 0; + f64 factor = 0.1; + while(p->cursor < p->size) + { + char c = p->data[p->cursor]; + + if(!isdigit(c)) + break; + + decimal += factor * (c - '0'); + factor *= 0.1; + + p->cursor++; + } + + return decimal; +} + +static wf_string wf_parse_string(wf_parser *p) +{ + assert(p->cursor < p->size); + + wf_string s; + + s.data = p->data + p->cursor; + s.size = 0; + + while(p->cursor < p->size) + { + char c = p->data[p->cursor]; + if(isspace(c) || c == '#') + break; + p->cursor++; + } + s.size = p->data + p->cursor - s.data; + + return s; +} + +#define WF_PARSE_FORCE_STRING 1 + +static wf_token wf_parse_next(wf_parser *p, u32 flags = 0) +{ + wf_token token; + token.type = WF_NONE; + + wf_eat_whitespace_and_comments(p); + if(p->cursor >= p->size) + { + token.type = WF_EOF; + return token; + } + + token.line = p->line; + token.line_start = p->line_start; + token.start = p->cursor; + token.size = 0; + char c = p->data[p->cursor]; + + if(flags & WF_PARSE_FORCE_STRING) + { + token.type = WF_STRING; + token.string = wf_parse_string(p); + } + else if(c == '-' || isdigit(c)) // Integer or real + { + s64 sign = 1; + if(c == '-') + { + sign = -1; + p->cursor++; + if(p->cursor >= p->size) + { + WF_PRINT_ERROR(p, "End of file reached while parsing number."); + token.type = WF_NONE; + return token; + } + + c = p->data[p->cursor]; + if(!isdigit(c)) + { + WF_PRINT_ERROR(p, "Unexpected character after '-' while parsing number."); + token.type = WF_NONE; + return token; + } + } + // Parse integer part first, then if it's a real, we convert it and add the decimal part + token.type = WF_INTEGER; + token.integer = sign * wf_parse_integer(p); + if(p->cursor < p->size) + { + if(p->data[p->cursor] == '.') // Real + { + token.type = WF_REAL; + token.real = token.integer; + p->cursor++; + // We are parsing a real number. We should have at least 1 digit after the '.' + if(p->cursor >= p->size) + { + WF_PRINT_ERROR(p, "End of file reached while parsing real number."); + token.type = WF_NONE; + return token; + } + if(!isdigit(p->data[p->cursor])) + { + WF_PRINT_ERROR(p, "Unexpected character after '.' while parsing real number."); + token.type = WF_NONE; + return token; + } + token.real += sign * wf_parse_decimal_part(p); + } + } + } + else if(isalpha(c)) // Text + { + token.type = WF_STRING; + token.string = wf_parse_string(p); + } + else if(c == '/') + { + token.type = WF_SLASH; + p->cursor++; + } + else + { + WF_PRINT_ERROR(p, "Unrecognized token (starting with \'%c\').", c); + } + + token.size = p->cursor - token.start; + + return token; +} + +static wf_token wf_peek_next(wf_parser *p, u32 flags = 0) +{ + wf_parser copy = *p; + return wf_parse_next(©, flags); +} + +static bool wf_string_equal(wf_string *wfs, const char *s) +{ + u64 i = 0; + while(i < wfs->size && s[i] != 0) + { + if(wfs->data[i] != s[i]) + return false; + i++; + } + if(i != wfs->size || s[i] != 0) + return false; + + return true; +} + +bool wf_parse_obj(char *data, u64 size, const char *name, wf_obj_file *return_obj) +{ + wf_obj_file result; + wf_object *curr_object; + wf_parser p; + wf_token token; + + wf_parser_init(&p, data, size, name); + + { + u64 size = strlen(name); + result.name = (char*)wf_alloc(size + 1); + memcpy(result.name, name, size); + result.name[size] = 0; // zero-terminated string + } + + { + result.mtllib_names = NULL; + result.mtllib_names_count = 0; + + result.objects = NULL; + result.objects_count = 0; + } + + wf_size vertices_capacity; + wf_size texture_coords_capacity; + wf_size normals_capacity; + wf_size faces_capacity; + { + result.objects_count = 1; + result.objects = (wf_object*)wf_alloc(1 * sizeof(wf_object)); + curr_object = result.objects; + + curr_object->name = NULL; + curr_object->material_name = NULL; + + vertices_capacity = 32; + texture_coords_capacity = 32; + normals_capacity = 32; + faces_capacity = 32; + + curr_object->vertices = (v4*)wf_alloc(vertices_capacity * sizeof(v4)); + curr_object->texture_coords = (v3*)wf_alloc(texture_coords_capacity * sizeof(v3)); + curr_object->normals = (v3*)wf_alloc(normals_capacity * sizeof(v3)); + curr_object->faces = (wf_face*)wf_alloc(faces_capacity * sizeof(wf_face)); + curr_object->vertices_count = 0; + curr_object->texture_coords_count = 0; + curr_object->normals_count = 0; + curr_object->faces_count = 0; + } + + + token = wf_parse_next(&p); + while(token.type != WF_EOF && token.type != WF_NONE) + { + // At the start of the line we expect to find a string/command, + // followed by its parameters. + if(token.type != WF_STRING) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected string at start of line. Found: %s \"%.*s\"", wf_token_type_string(token.type), (int)token.size, p.data + token.start); + goto error_cleanup_obj; + } + + if(wf_string_equal(&token.string, "o")) // Object name + { + bool prev_object_empty = true; + prev_object_empty &= (curr_object->name == NULL); + prev_object_empty &= (curr_object->material_name == NULL); + prev_object_empty &= (curr_object->vertices_count == 0); + prev_object_empty &= (curr_object->texture_coords_count == 0); + prev_object_empty &= (curr_object->normals_count == 0); + prev_object_empty &= (curr_object->faces_count == 0); + + if(prev_object_empty) + { + // Use previously allocated space + } + else + { + // Resize the old object to the smallest size possible + if(vertices_capacity != curr_object->vertices_count) + curr_object->vertices = (v4*)wf_realloc(curr_object->vertices, curr_object->vertices_count * sizeof(v4)); + if(texture_coords_capacity != curr_object->texture_coords_count) + curr_object->texture_coords = (v3*)wf_realloc(curr_object->texture_coords, curr_object->texture_coords_count * sizeof(v3)); + if(normals_capacity != curr_object->normals_count) + curr_object->normals = (v3*)wf_realloc(curr_object->normals, curr_object->normals_count * sizeof(v3)); + if(faces_capacity != curr_object->faces_count) + curr_object->faces = (wf_face*)wf_realloc(curr_object->faces, curr_object->faces_count * sizeof(wf_face)); + + // Allocate space for new object + result.objects_count++; + result.objects = (wf_object*)wf_realloc(result.objects, result.objects_count * sizeof(wf_object)); + curr_object = result.objects + (result.objects_count - 1); + + curr_object->name = NULL; + curr_object->material_name = NULL; + + vertices_capacity = 32; + texture_coords_capacity = 32; + normals_capacity = 32; + faces_capacity = 32; + + curr_object->vertices = (v4*)wf_alloc(vertices_capacity * sizeof(v4)); + curr_object->texture_coords = (v3*)wf_alloc(texture_coords_capacity * sizeof(v3)); + curr_object->normals = (v3*)wf_alloc(normals_capacity * sizeof(v3)); + curr_object->faces = (wf_face*)wf_alloc(faces_capacity * sizeof(wf_face)); + curr_object->vertices_count = 0; + curr_object->texture_coords_count = 0; + curr_object->normals_count = 0; + curr_object->faces_count = 0; + } + + token = wf_parse_next(&p, WF_PARSE_FORCE_STRING); + if(token.type != WF_STRING) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected object name after \"o\". Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_obj; + } + curr_object->name = (char*)wf_alloc(token.string.size + 1); + memcpy(curr_object->name, token.string.data, token.string.size); + curr_object->name[token.string.size] = 0; // zero-terminated string + + wf_skip_to_next_line(&p); + } + else if(wf_string_equal(&token.string, "v")) // Vertices + { + // A vertex is specified by 3 or 4 real numbers (x y z [w]) + v4 vertex; + bool error = false; + + if(!error) + { + token = wf_parse_next(&p); + if(token.type == WF_REAL) + vertex.x = token.real; + else + error = true; + } + + if(!error) + { + token = wf_parse_next(&p); + if(token.type == WF_REAL) + vertex.y = token.real; + else + error = true; + } + + if(!error) + { + token = wf_parse_next(&p); + if(token.type == WF_REAL) + vertex.z = token.real; + else + error = true; + } + + // w optional + if(!error) + { + wf_token peeked = wf_peek_next(&p); + if(peeked.type == WF_REAL) + { + token = wf_parse_next(&p); + vertex.w = token.real; + } + else + { + vertex.w = 1.0; // Default value if omitted + } + } + + if(error) + { + // @Correctness: This prints the line after the one that has the actual error (instead of the correct one). + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected 3 or 4 real numbers when reading vertex data. Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_obj; + } + + // @Correctness: Right now, we skip everything we find after reading the last component. + // We should report an error if there is any data before the next line. + wf_skip_to_next_line(&p); + + if(vertices_capacity < curr_object->vertices_count + 1) + { + vertices_capacity *= 2; + curr_object->vertices = (v4*)wf_realloc(curr_object->vertices, vertices_capacity * sizeof(v4)); + } + curr_object->vertices[curr_object->vertices_count] = vertex; + curr_object->vertices_count++; + } + else if(wf_string_equal(&token.string, "vt")) // Texture coordinates + { + // A texture coordinate is specified by 1, 2 or 3 real numbers (u [v] [w]) + v3 texture_coord; + bool error = false; + + if(!error) + { + token = wf_parse_next(&p); + if(token.type == WF_REAL) + texture_coord.u = token.real; + else + error = true; + } + + // v optional + if(!error) + { + wf_token peeked = wf_peek_next(&p); + if(peeked.type == WF_REAL) + { + token = wf_parse_next(&p); + texture_coord.v = token.real; + } + else + { + texture_coord.v = 0.0; // Default value if omitted + texture_coord.w_ = 0.0; // Default value if omitted + } + } + + // w optional + if(!error) + { + wf_token peeked = wf_peek_next(&p); + if(peeked.type == WF_REAL) + { + token = wf_parse_next(&p); + texture_coord.w_ = token.real; + } + else + { + texture_coord.w_ = 0.0; // Default value if omitted + } + } + + if(error) + { + // @Correctness: This prints the line after the one that has the actual error (instead of the correct one). + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected 1, 2 or 3 real numbers when reading texture coordinates. Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_obj; + } + + // @Correctness: Right now, we skip everything we find after reading the last component. + // We should report an error if there is any data before the next line. + wf_skip_to_next_line(&p); + + if(texture_coords_capacity < curr_object->texture_coords_count + 1) + { + texture_coords_capacity *= 2; + curr_object->texture_coords = (v3*)wf_realloc(curr_object->texture_coords, texture_coords_capacity * sizeof(v3)); + } + curr_object->texture_coords[curr_object->texture_coords_count] = texture_coord; + curr_object->texture_coords_count++; + } + else if(wf_string_equal(&token.string, "vn")) // Normals + { + // A normal is specified by 3 real numbers (x y z) + v3 normal; + bool error = false; + + if(!error) + { + token = wf_parse_next(&p); + if(token.type == WF_REAL) + normal.x = token.real; + else + error = true; + } + + if(!error) + { + token = wf_parse_next(&p); + if(token.type == WF_REAL) + normal.y = token.real; + else + error = true; + } + + if(!error) + { + token = wf_parse_next(&p); + if(token.type == WF_REAL) + normal.z = token.real; + else + error = true; + } + + if(error) + { + // @Correctness: This prints the line after the one that has the actual error (instead of the correct one). + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected 3 real numbers when reading normal data. Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_obj; + } + + wf_skip_to_next_line(&p); + + if(normals_capacity < curr_object->normals_count + 1) + { + normals_capacity *= 2; + curr_object->normals = (v3*)wf_realloc(curr_object->normals, normals_capacity * sizeof(v3)); + } + curr_object->normals[curr_object->normals_count] = normal; + curr_object->normals_count++; + } + else if(wf_string_equal(&token.string, "f")) // Faces + { + // A face is specified by 3 or more vertices/texture_coords/normal. Only the vertex index is mandatory. + // For now we read only 3, ignoring the rest. + wf_face face; + + u32 count = 0; + token = wf_peek_next(&p); + while(token.type == WF_INTEGER) + { + token = wf_parse_next(&p); // We peeked _token_, so now we have to actually advance the parser. + + wf_face_index fi; + fi.vertex = 0; + fi.texture = 0; + fi.normal = 0; + + + fi.vertex = token.integer; + + // Check for "/" separator (optional) + token = wf_peek_next(&p); + if(token.type == WF_SLASH) + { + token = wf_parse_next(&p); + // Check for texture coordinate index (optional) + token = wf_peek_next(&p); + if(token.type != WF_INTEGER && token.type != WF_SLASH) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected texture coordinate index after vertex index. Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_obj; + } + + if(token.type == WF_INTEGER) + { + token = wf_parse_next(&p); + fi.texture = token.integer; + token = wf_peek_next(&p); + } + + // Check for "/" separator (optional) + if(token.type == WF_SLASH) + { + token = wf_parse_next(&p); + // Check for normal index (optional) + token = wf_peek_next(&p); + if(token.type != WF_INTEGER) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected normal index after texture coordinate index. Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_obj; + } + if(!isdigit(p.data[p.cursor])) + { + WF_PRINT_ERROR(&p, "Expected normal index after texture coordinate index. Found nothing."); + goto error_cleanup_obj; + } + + token = wf_parse_next(&p); + fi.normal = token.integer; + } + + } + + if(count < 3) + face.indices[count] = fi; + + count++; + token = wf_peek_next(&p); + } + + if(count < 3) + { + WF_PRINT_ERROR(&p, "Expected 3 or more indices numbers when reading face data. Found only %u.", count); + goto error_cleanup_obj; + } + + // @Correctness: Right now, we skip everything we find after reading the last component. + // We should report an error if there is any data before the next line. + wf_skip_to_next_line(&p); + + if(faces_capacity < curr_object->faces_count + 1) + { + faces_capacity *= 2; + curr_object->faces = (wf_face*)wf_realloc(curr_object->faces, faces_capacity * sizeof(wf_face)); + } + curr_object->faces[curr_object->faces_count] = face; + curr_object->faces_count++; + } + else if(wf_string_equal(&token.string, "mtllib")) // Material library file name + { + token = wf_parse_next(&p, WF_PARSE_FORCE_STRING); + if(token.type != WF_STRING) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected file name after \"mtllib\". Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_obj; + } + + if(result.mtllib_names == NULL) + result.mtllib_names = (char**)wf_alloc((result.mtllib_names_count + 1) * sizeof(char*)); + else + result.mtllib_names = (char**)wf_realloc(result.mtllib_names, (result.mtllib_names_count + 1) * sizeof(char*)); + result.mtllib_names[result.mtllib_names_count] = (char*)wf_alloc(token.string.size + 1); + memcpy(result.mtllib_names[result.mtllib_names_count], token.string.data, token.string.size); + result.mtllib_names[result.mtllib_names_count][token.string.size] = 0; // zero-terminated + result.mtllib_names_count++; + + wf_skip_to_next_line(&p); + } + else if(wf_string_equal(&token.string, "usemtl")) // Material name + { + token = wf_parse_next(&p, WF_PARSE_FORCE_STRING); + if(token.type != WF_STRING) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected material name after \"usemtl\". Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_obj; + } + curr_object->material_name = (char*)wf_alloc(token.string.size + 1); + memcpy(curr_object->material_name, token.string.data, token.string.size); + curr_object->material_name[token.string.size] = 0; // zero-terminated string + + wf_skip_to_next_line(&p); + } + else + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Unrecognized command \"%.*s\". Skipping to next line.", (int)token.string.size, token.string.data); + wf_skip_to_next_line(&p); + } + + token = wf_parse_next(&p); + } + + // Resize the object to the smallest size possible + { + if(vertices_capacity != curr_object->vertices_count) + curr_object->vertices = (v4*)wf_realloc(curr_object->vertices, curr_object->vertices_count * sizeof(v4)); + if(texture_coords_capacity != curr_object->texture_coords_count) + curr_object->texture_coords = (v3*)wf_realloc(curr_object->texture_coords, curr_object->texture_coords_count * sizeof(v3)); + if(normals_capacity != curr_object->normals_count) + curr_object->normals = (v3*)wf_realloc(curr_object->normals, curr_object->normals_count * sizeof(v3)); + if(faces_capacity != curr_object->faces_count) + curr_object->faces = (wf_face*)wf_realloc(curr_object->faces, curr_object->faces_count * sizeof(wf_face)); + } + + *return_obj = result; + return true; + +error_cleanup_obj: + wf_cleanup_obj(&result); + return false; +} + + +bool wf_parse_mtl(char *data, u64 size, const char *name, wf_mtl_file *return_mtl) +{ + wf_mtl_file result; + wf_material *curr_material; + wf_parser p; + wf_token token; + + wf_parser_init(&p, data, size, name); + + { + u64 size = strlen(name); + result.name = (char*)wf_alloc(size + 1); + memcpy(result.name, name, size); + result.name[size] = 0; // zero-terminated string + } + + { + result.materials = NULL; + result.materials_count = 0; + } + + + token = wf_parse_next(&p); + while(token.type != WF_EOF && token.type != WF_NONE) + { + // At the start of the line we expect to find a string/command, + // followed by its parameters. + if(token.type != WF_STRING) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected string at start of line. Found: %s \"%.*s\"", wf_token_type_string(token.type), (int)token.size, p.data + token.start); + goto error_cleanup_mtl; + } + + if(wf_string_equal(&token.string, "newmtl")) // Material name + { + // Initialize new material + { + result.materials_count++; + if(result.materials == NULL) + result.materials = (wf_material*)wf_alloc(result.materials_count * sizeof(wf_material)); + else + result.materials = (wf_material*)wf_realloc(result.materials, result.materials_count * sizeof(wf_material)); + curr_material = result.materials + (result.materials_count - 1); + } + + { + curr_material->name = NULL; + curr_material->Ka = v3{0, 0, 0}; + curr_material->Kd = v3{0, 0, 0}; + curr_material->Ke = v3{0, 0, 0}; + curr_material->Ks = v3{0, 0, 0}; + curr_material->Ns = 0; + curr_material->d = 1.0; + curr_material->Ni = 1.0; + curr_material->illum = 0; + curr_material->map_Ka = NULL; + curr_material->map_Kd = NULL; + curr_material->map_Ke = NULL; + curr_material->map_Ks = NULL; + curr_material->map_Ns = NULL; + curr_material->map_d = NULL; + curr_material->bump = NULL; + curr_material->disp = NULL; + curr_material->decal = NULL; + } + + token = wf_parse_next(&p, WF_PARSE_FORCE_STRING); + if(token.type != WF_STRING) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected material name after \"newmtl\". Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_mtl; + } + curr_material->name = (char*)wf_alloc(token.string.size + 1); + memcpy(curr_material->name, token.string.data, token.string.size); + curr_material->name[token.string.size] = 0; // zero-terminated string + + wf_skip_to_next_line(&p); + } + else if(wf_string_equal(&token.string, "Ka")) + { + // A color is specified by 3 real numbers (x y z) + v3 color; + bool error = false; + + if(!error) + { + token = wf_parse_next(&p); + if(token.type == WF_REAL) + color.r = token.real; + else + error = true; + } + + if(!error) + { + token = wf_parse_next(&p); + if(token.type == WF_REAL) + color.g = token.real; + else + error = true; + } + + if(!error) + { + token = wf_parse_next(&p); + if(token.type == WF_REAL) + color.b = token.real; + else + error = true; + } + + if(error) + { + // @Correctness: This prints the line after the one that has the actual error (instead of the correct one). + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected 3 real numbers when reading normal data. Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_mtl; + } + + wf_skip_to_next_line(&p); + + curr_material->Ka = color; + } + else if(wf_string_equal(&token.string, "Kd")) + { + // A color is specified by 3 real numbers (x y z) + v3 color; + bool error = false; + + if(!error) + { + token = wf_parse_next(&p); + if(token.type == WF_REAL) + color.r = token.real; + else + error = true; + } + + if(!error) + { + token = wf_parse_next(&p); + if(token.type == WF_REAL) + color.g = token.real; + else + error = true; + } + + if(!error) + { + token = wf_parse_next(&p); + if(token.type == WF_REAL) + color.b = token.real; + else + error = true; + } + + if(error) + { + // @Correctness: This prints the line after the one that has the actual error (instead of the correct one). + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected 3 real numbers when reading normal data. Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_mtl; + } + + wf_skip_to_next_line(&p); + + curr_material->Kd = color; + } + + else if(wf_string_equal(&token.string, "Ke")) + { + // A color is specified by 3 real numbers (x y z) + v3 color; + bool error = false; + + if(!error) + { + token = wf_parse_next(&p); + if(token.type == WF_REAL) + color.r = token.real; + else + error = true; + } + + if(!error) + { + token = wf_parse_next(&p); + if(token.type == WF_REAL) + color.g = token.real; + else + error = true; + } + + if(!error) + { + token = wf_parse_next(&p); + if(token.type == WF_REAL) + color.b = token.real; + else + error = true; + } + + if(error) + { + // @Correctness: This prints the line after the one that has the actual error (instead of the correct one). + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected 3 real numbers when reading normal data. Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_mtl; + } + + wf_skip_to_next_line(&p); + + curr_material->Ke = color; + } + else if(wf_string_equal(&token.string, "Ks")) + { + // A color is specified by 3 real numbers (x y z) + v3 color; + bool error = false; + + if(!error) + { + token = wf_parse_next(&p); + if(token.type == WF_REAL) + color.r = token.real; + else + error = true; + } + + if(!error) + { + token = wf_parse_next(&p); + if(token.type == WF_REAL) + color.g = token.real; + else + error = true; + } + + if(!error) + { + token = wf_parse_next(&p); + if(token.type == WF_REAL) + color.b = token.real; + else + error = true; + } + + if(error) + { + // @Correctness: This prints the line after the one that has the actual error (instead of the correct one). + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected 3 real numbers when reading normal data. Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_mtl; + } + + wf_skip_to_next_line(&p); + + curr_material->Ks = color; + } + else if(wf_string_equal(&token.string, "Ns")) + { + token = wf_parse_next(&p); + if(token.type != WF_REAL) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected real number after \"Ks\". Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_mtl; + } + + curr_material->Ns = token.real; + + wf_skip_to_next_line(&p); + } + else if(wf_string_equal(&token.string, "d")) + { + token = wf_parse_next(&p); + if(token.type != WF_REAL) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected real number after \"d\". Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_mtl; + } + + curr_material->d = token.real; + + wf_skip_to_next_line(&p); + } + else if(wf_string_equal(&token.string, "Ni")) + { + token = wf_parse_next(&p); + if(token.type != WF_REAL) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected real number after \"Ni\". Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_mtl; + } + + curr_material->Ni = token.real; + + wf_skip_to_next_line(&p); + } + else if(wf_string_equal(&token.string, "illum")) + { + token = wf_parse_next(&p); + if(token.type != WF_INTEGER) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected integer number after \"illum\". Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_mtl; + } + + curr_material->illum = token.integer; + + wf_skip_to_next_line(&p); + } + else if(wf_string_equal(&token.string, "map_Ka")) + { + token = wf_parse_next(&p, WF_PARSE_FORCE_STRING); + if(token.type != WF_STRING) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected texture name after \"map_Ka\". Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_mtl; + } + curr_material->map_Ka = (char*)wf_alloc(token.string.size + 1); + memcpy(curr_material->map_Ka, token.string.data, token.string.size); + curr_material->map_Ka[token.string.size] = 0; // zero-terminated string + + wf_skip_to_next_line(&p); + } + else if(wf_string_equal(&token.string, "map_Kd")) + { + token = wf_parse_next(&p, WF_PARSE_FORCE_STRING); + if(token.type != WF_STRING) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected texture name after \"map_Kd\". Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_mtl; + } + curr_material->map_Kd = (char*)wf_alloc(token.string.size + 1); + memcpy(curr_material->map_Kd, token.string.data, token.string.size); + curr_material->map_Kd[token.string.size] = 0; // zero-terminated string + + wf_skip_to_next_line(&p); + } + else if(wf_string_equal(&token.string, "map_Ke")) + { + token = wf_parse_next(&p, WF_PARSE_FORCE_STRING); + if(token.type != WF_STRING) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected texture name after \"map_Ke\". Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_mtl; + } + curr_material->map_Ke = (char*)wf_alloc(token.string.size + 1); + memcpy(curr_material->map_Ke, token.string.data, token.string.size); + curr_material->map_Ke[token.string.size] = 0; // zero-terminated string + + wf_skip_to_next_line(&p); + } + else if(wf_string_equal(&token.string, "map_Ks")) + { + token = wf_parse_next(&p, WF_PARSE_FORCE_STRING); + if(token.type != WF_STRING) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected texture name after \"map_Ks\". Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_mtl; + } + curr_material->map_Ks = (char*)wf_alloc(token.string.size + 1); + memcpy(curr_material->map_Ks, token.string.data, token.string.size); + curr_material->map_Ks[token.string.size] = 0; // zero-terminated string + + wf_skip_to_next_line(&p); + } + else if(wf_string_equal(&token.string, "map_Ns")) + { + token = wf_parse_next(&p, WF_PARSE_FORCE_STRING); + if(token.type != WF_STRING) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected texture name after \"map_Ns\". Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_mtl; + } + curr_material->map_Ns = (char*)wf_alloc(token.string.size + 1); + memcpy(curr_material->map_Ns, token.string.data, token.string.size); + curr_material->map_Ns[token.string.size] = 0; // zero-terminated string + + wf_skip_to_next_line(&p); + } + else if(wf_string_equal(&token.string, "map_d")) + { + token = wf_parse_next(&p, WF_PARSE_FORCE_STRING); + if(token.type != WF_STRING) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected texture name after \"map_d\". Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_mtl; + } + curr_material->map_d = (char*)wf_alloc(token.string.size + 1); + memcpy(curr_material->map_d, token.string.data, token.string.size); + curr_material->map_d[token.string.size] = 0; // zero-terminated string + + wf_skip_to_next_line(&p); + } + else if(wf_string_equal(&token.string, "bump")) + { + token = wf_parse_next(&p, WF_PARSE_FORCE_STRING); + if(token.type != WF_STRING) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected texture name after \"bump\". Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_mtl; + } + curr_material->bump = (char*)wf_alloc(token.string.size + 1); + memcpy(curr_material->bump, token.string.data, token.string.size); + curr_material->bump[token.string.size] = 0; // zero-terminated string + + wf_skip_to_next_line(&p); + } + else if(wf_string_equal(&token.string, "disp")) + { + token = wf_parse_next(&p, WF_PARSE_FORCE_STRING); + if(token.type != WF_STRING) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected texture name after \"disp\". Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_mtl; + } + curr_material->disp = (char*)wf_alloc(token.string.size + 1); + memcpy(curr_material->disp, token.string.data, token.string.size); + curr_material->disp[token.string.size] = 0; // zero-terminated string + + wf_skip_to_next_line(&p); + } + else if(wf_string_equal(&token.string, "decal")) + { + token = wf_parse_next(&p, WF_PARSE_FORCE_STRING); + if(token.type != WF_STRING) + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Expected texture name after \"decal\". Found: \"%.*s\".", (int)token.size, p.data + token.start); + goto error_cleanup_mtl; + } + curr_material->decal = (char*)wf_alloc(token.string.size + 1); + memcpy(curr_material->decal, token.string.data, token.string.size); + curr_material->decal[token.string.size] = 0; // zero-terminated string + + wf_skip_to_next_line(&p); + } + else + { + WF_PRINT_ERROR_TOKEN(&p, &token, "Unrecognized command \"%.*s\". Skipping to next line.", (int)token.string.size, token.string.data); + wf_skip_to_next_line(&p); + } + + token = wf_parse_next(&p); + } + + *return_mtl = result; + return true; + +error_cleanup_mtl: + wf_cleanup_mtl(&result); + return false; +} + + + +void wf_cleanup_obj(wf_obj_file *obj_file) +{ + // @Performance: Batch allocation for fast alloc/free + for(wf_size i = 0; i < obj_file->objects_count; i++) + { + WF_FREE_OPTIONAL(obj_file->objects[i].name); + wf_free(obj_file->objects[i].vertices); + wf_free(obj_file->objects[i].texture_coords); + wf_free(obj_file->objects[i].normals); + wf_free(obj_file->objects[i].faces); + WF_FREE_OPTIONAL(obj_file->objects[i].material_name); + } + + for(wf_size i = 0; i < obj_file->mtllib_names_count; i++) + wf_free(obj_file->mtllib_names[i]); + WF_FREE_OPTIONAL(obj_file->mtllib_names); + wf_free(obj_file->name); + wf_free(obj_file->objects); +} + +void wf_cleanup_mtl(wf_mtl_file *mtl_file) +{ + // @Performance: Batch allocation for fast alloc/free + for(wf_size i = 0; i < mtl_file->materials_count; i++) + { + WF_FREE_OPTIONAL(mtl_file->materials[i].name); + WF_FREE_OPTIONAL(mtl_file->materials[i].map_Ka); + WF_FREE_OPTIONAL(mtl_file->materials[i].map_Kd); + WF_FREE_OPTIONAL(mtl_file->materials[i].map_Ke); + WF_FREE_OPTIONAL(mtl_file->materials[i].map_Ks); + WF_FREE_OPTIONAL(mtl_file->materials[i].map_Ns); + WF_FREE_OPTIONAL(mtl_file->materials[i].map_d); + WF_FREE_OPTIONAL(mtl_file->materials[i].bump); + WF_FREE_OPTIONAL(mtl_file->materials[i].disp); + WF_FREE_OPTIONAL(mtl_file->materials[i].decal); + } + + wf_free(mtl_file->name); + wf_free(mtl_file->materials); +} diff --git a/code/file_formats/wavefront.h b/code/file_formats/wavefront.h new file mode 100644 index 0000000..32dc012 --- /dev/null +++ b/code/file_formats/wavefront.h @@ -0,0 +1,121 @@ +#ifndef _PIUMA_WAVEFRONT_H_ +#define _PIUMA_WAVEFRONT_H_ + +#include "../lib/types.h" +#include "../lib/math.h" + +typedef void *(*wf_alloc_t)(u64 size); +typedef void *(*wf_realloc_t)(void *ptr, u64 size); +typedef void (*wf_free_t)(void *ptr); + +/* Reassing wf_alloc and wf_free with your own alloc/free functions if you don't + * want to use malloc and free. + */ +extern wf_alloc_t wf_alloc; +extern wf_realloc_t wf_realloc; +extern wf_free_t wf_free; + +typedef u32 wf_index; +typedef u32 wf_size; + + +// Indices are 1-based in Wavefront. 0 is used to signal the index was omitted. +struct wf_face_index +{ + wf_index vertex; + wf_index texture; + wf_index normal; +}; + +struct wf_face +{ + wf_face_index indices[3]; +}; + +struct wf_object +{ + char *name; + // @Feature: groups are not supported yet + char *material_name; + + // Vertices + v4 *vertices; + wf_size vertices_count; + // Texture coordinates + v3 *texture_coords; + wf_size texture_coords_count; + // Vertex normals + v3 *normals; + wf_size normals_count; + // Faces + wf_face *faces; + wf_size faces_count; +}; + +struct wf_obj_file +{ + char *name; + + char **mtllib_names; + wf_size mtllib_names_count; + + wf_object *objects; + wf_size objects_count; +}; + + + + +struct wf_material +{ + char *name; + v3 Ka; // ambient color + v3 Kd; // diffuse color + v3 Ke; // emissive color + v3 Ks; // specular color + f32 Ns; // specular exposure + f32 d; // dissolved - transparency where 0.0 is fully transparent and 1.0 is opaque + f32 Ni; // optical density - refractive index + int illum; // illumination model + /* illum is described at https://en.wikipedia.org/wiki/Wavefront_.obj_file#Basic_materials as follows: + * 0. Color on and Ambient off + * 1. Color on and Ambient on + * 2. Highlight on + * 3. Reflection on and Ray trace on + * 4. Transparency: Glass on, Reflection: Ray trace on + * 5. Reflection: Fresnel on and Ray trace on + * 6. Transparency: Refraction on, Reflection: Fresnel off and Ray trace on + * 7. Transparency: Refraction on, Reflection: Fresnel on and Ray trace on + * 8. Reflection on and Ray trace off + * 9. Transparency: Glass on, Reflection: Ray trace off + * 10. Casts shadows onto invisible surfaces + */ + + char *map_Ka; // ambient texture map name + char *map_Kd; // diffuse texture map name + char *map_Ke; // emissive texture map name + char *map_Ks; // specular color texture map name + char *map_Ns; // specular highlight texture map name + char *map_d; // alpha texture map name + char *bump; // bump map name + char *disp; // displacement map name + char *decal; // stencil decal texture map name + + // @Feature: Texture options are not supported yet +}; + +struct wf_mtl_file +{ + char *name; + + wf_material *materials; + wf_size materials_count; +}; + + +bool wf_parse_obj(char *data, u64 size, const char *name, wf_obj_file *return_obj); +bool wf_parse_mtl(char *data, u64 size, const char *name, wf_mtl_file *return_mtl); +void wf_cleanup_obj(wf_obj_file *obj_file); +void wf_cleanup_mtl(wf_mtl_file *mtl_file); + +#endif diff --git a/code/gui/gui.cpp b/code/gui/gui.cpp new file mode 100644 index 0000000..d0c12c0 --- /dev/null +++ b/code/gui/gui.cpp @@ -0,0 +1,807 @@ +#include "gui.h" +#include "../render/2d.h" +#include "../render/render.h" +#include "../lib/math.h" +#include "../lib/color.h" +#include "text_draw.h" +#include "../debug/logger.h" +#include "stdio.h" +#include "string.h" +#include "../platform.h" +#include "../lib/hashing.h" + +Gui_State global_gui_state; + +void gui_button_draw_inner_text(Gui_Context *ctx, Rect r, const char *text, v4 color, Rect *actual_drawn_rect = NULL); + +bool gui_init() +{ + gui_context_init(&global_gui_state.default_context); + global_gui_state.selected_context = &global_gui_state.default_context; + bool success = gui_text_draw_init(); + return success; +} + +void gui_deinit() +{ + gui_text_draw_deinit(); +} + +void gui_context_init(Gui_Context *ctx) +{ + ctx->width = 100; + ctx->height = 100; + + ctx-> last_frame_time = 0; + ctx->current_frame_time = 0; + + ctx-> active = NULL; + ctx-> hot = NULL; + ctx->possibly_hot = NULL; + ctx->active_start_time = 0; + ctx-> hot_start_time = 0; + ctx->active_status = 0; + + ctx->text_cursor_position = 0; + ctx->text_length = 0; + + ctx->windows = NULL; + ctx->window_count = 0; + ctx->window_capacity = 0; + ctx->current_window = NULL; + + ctx->id_count = 0; + ctx->id_capacity = 8; + ctx->id_stack = (Gui_Id*)p_alloc(sizeof(Gui_Id) * ctx->id_capacity); + + ctx->input.pointer_position = {0, 0}; + ctx->input.absolute_pointer_position = {0, 0}; + ctx->input.mouse_pressed = false; + ctx->input.mouse_pressed_this_frame = false; + ctx->input.mouse_released_this_frame = false; + ctx->input.text_cursor_move = 0; + ctx->input.text[0] = '\0'; + + ctx->input.absolute_pointer_position_last_frame = {0, 0}; + + + ctx->style.font_size = 12; + ctx->style.animation_base_time = 0.100; + + ctx->style.text_color = v4{1.0, 1.0, 1.0, 1.0}; + ctx->style.text_align = GUI_ALIGN_CENTER; + + + ctx->style.button_color = v4{0.4f, 0.4f, 0.4f, 1.0f}*0.8f; + ctx->style.button_color_hovered = v4{0.3f, 0.3f, 0.3f, 1.0f}*0.9f; + ctx->style.button_color_pressed = v4{0.1f, 0.1f, 0.1f, 1.0f}; + ctx->style.button_text_color = v4{1.0f, 1.0f, 1.0f, 1.0f}; + ctx->style.button_text_color_hovered = v4{1.0f, 1.0f, 1.0f, 1.0f}; + ctx->style.button_text_color_pressed = v4{1.0f, 1.0f, 1.0f, 1.0f}; + ctx->style.button_radius = 3; + + ctx->style.slider_fill_color = {0.0f, 0.3f, 0.9f, 1.0f}; + + ctx->style.window_background_color = {0.01,0.01,0.01, 0.98}; + ctx->style.window_border_color = {1.0,0.06,0.0,1.0}; + ctx->style.window_background_color_inactive = {0.05,0.05,0.05, 0.95}; + ctx->style.window_border_color_inactive = {0.3,0.3,0.3, 1.0}; + ctx->style.window_corner_radius = 5; + ctx->style.window_titlebar_color = ctx->style.window_border_color; + ctx->style.window_titlebar_color_inactive = {0.1,0.1,0.1,0.1}; +} + +void gui_context_select(Gui_Context *ctx) +{ + global_gui_state.selected_context = ctx; +} + +void gui_frame_begin(Gui_Context *ctx, f64 curr_time) +{ + ctx-> last_frame_time = ctx->current_frame_time; + ctx->current_frame_time = curr_time; +} + +void gui_frame_begin(f64 curr_time) +{ + gui_frame_begin(&global_gui_state.default_context, curr_time); +} + +void gui_frame_end(Gui_Context *ctx) +{ + // Render windows + for(u32 i = 0; i < ctx->window_count; i++) + { + Gui_Window *w = &ctx->windows[i]; + if(w->still_open) + { + Rect r_uv = Rect{0,0,1,-1}; // y is flipped when rendering framebuffer's textures + r_2d_immediate_rectangle(w->r, v4{1,1,1,1}, r_uv, &w->framebuffer.color_texture); + w->still_open = false; // Will be set to true if still open + } + } + // @Performance: cleanup unused windows + + // Fix state + if(ctx->hot != ctx->possibly_hot) + { + ctx->hot = ctx->possibly_hot; + ctx->hot_start_time = ctx->current_frame_time; + } + + ctx->input.mouse_pressed_this_frame = false; + ctx->input.mouse_released_this_frame = false; + + ctx->input.absolute_pointer_position_last_frame = ctx->input.absolute_pointer_position; + + ctx->input.text[0] = '\0'; + ctx->input.text_cursor_move = 0; + + ctx->possibly_hot = NULL; +} + +void gui_frame_end() +{ + gui_frame_end(&global_gui_state.default_context); +} + +void gui_handle_event(Gui_Context *ctx, Event *e) +{ + switch(e->type) + { + case EVENT_MOUSE_MOVE: { + if(!e->mouse_move.relative) + { + ctx->input.pointer_position = e->mouse_move.position; + ctx->input.absolute_pointer_position = e->mouse_move.position; + } + } break; + case EVENT_KEY: { + switch(e->key.key_code) + { + case KEY_MOUSE_LEFT: { + ctx->input.mouse_pressed = e->key.pressed; + if(e->key.pressed) + ctx->input.mouse_pressed_this_frame = true; + else + ctx->input.mouse_released_this_frame = true; + } break; + case KEY_ARROW_LEFT: { + if(e->key.pressed) + ctx->input.text_cursor_move--; + } break; + case KEY_ARROW_RIGHT: { + if(e->key.pressed) + ctx->input.text_cursor_move++; + } break; + default: { + } break; + } + } break; + case EVENT_RESIZE: { + + } break; + case EVENT_TEXT: { + strcat(ctx->input.text, e->text.data); + } break; + default: { + } break; + } +} + +void gui_handle_event(Event *e) +{ + gui_handle_event(&global_gui_state.default_context, e); +} + +// ### Widgets ### +// Text +void gui_text(Gui_Context *ctx, Rect r, const char *text) +{ + // @Feature: Clip text to Rect r + gui_text_draw(r, text, ctx->style.font_size, ctx->style.text_color); +} + +void gui_text(Rect r, const char *text) +{ + gui_text(&global_gui_state.default_context, r, text); +} + +void gui_text_aligned(Gui_Context *ctx, Rect r, const char *text, Gui_Text_Align alignment) +{ + // @Cleanup: this should not depend on setting state. We should have a function that gets alignment as an argument + Gui_Text_Align old_alignment = ctx->style.text_align; + ctx->style.text_align = alignment; + gui_button_draw_inner_text(ctx, r, text, ctx->style.text_color); + ctx->style.text_align = old_alignment; +} + +void gui_text_aligned(Rect r, const char *text, Gui_Text_Align alignment) +{ + gui_text_aligned(&global_gui_state.default_context, r, text, alignment); +} + + +v2 gui_text_compute_size(Gui_Context *ctx, const char *text) +{ + return gui_text_draw_size(text, ctx->style.font_size); +} + +v2 gui_text_compute_size(const char *text) +{ + return gui_text_compute_size(&global_gui_state.default_context, text); +} + +// Button +bool gui_button(Gui_Context *ctx, Rect r, const char *text) +{ + Gui_Id widget_id = gui_id_from_pointer(ctx, text); + bool behaviuor = gui_button_behaviuor(ctx, widget_id, r); + + // Compute colors + v4 button_color = ctx->style.button_color; + v4 text_color = ctx->style.button_text_color; + { + if(ctx->hot == widget_id) + { + f64 delta_t = (ctx->current_frame_time - ctx->hot_start_time); + f32 interpolation = clamp(0, 1, delta_t / ctx->style.animation_base_time); + button_color = lerp(ctx->style.button_color, ctx->style.button_color_hovered, interpolation); + text_color = lerp(ctx->style.button_text_color, ctx->style.button_text_color_hovered, interpolation); + } + if(ctx->active == widget_id) + { + f64 delta_t = (ctx->current_frame_time - ctx->active_start_time); + f32 interpolation = clamp(0, 1, delta_t / ctx->style.animation_base_time); + button_color = lerp(ctx->style.button_color_hovered, ctx->style.button_color_pressed, interpolation * 0.4 + 0.6); + text_color = lerp(ctx->style.button_text_color_hovered, ctx->style.button_text_color_pressed, interpolation * 0.4 + 0.6); + } + } + + // Draw button and text + r_2d_immediate_rounded_rectangle(r, ctx->style.button_radius, button_color); + gui_button_draw_inner_text(ctx, r, text, text_color); + + return behaviuor; +} + +bool gui_button(Rect r, const char *text) +{ + return gui_button(&global_gui_state.default_context, r, text); +} + +void gui_button_draw_inner_text(Gui_Context *ctx, Rect r, const char *text, v4 color, Rect *actual_drawn_rect) +{ + v2 text_size = gui_text_draw_size(text, ctx->style.font_size); + // Alignment (center, left, right) + v2 text_position = r.position + (r.size - text_size) * v2{0.5, 0.5}; + if(ctx->style.text_align == GUI_ALIGN_LEFT) + text_position = r.position + (r.size - text_size) * v2{0, 0.5}; + if(ctx->style.text_align == GUI_ALIGN_RIGHT) + text_position = r.position + (r.size - text_size) * v2{1, 0.5}; + // Draw + Rect text_rect = { .position = text_position, .size = text_size }; + // @Feature: Clip text to Rect r + gui_text_draw(text_rect, text, ctx->style.font_size, color); + + if(actual_drawn_rect) + *actual_drawn_rect = text_rect; +} + +// Slider +bool gui_slider(Gui_Context *ctx, Rect r, f32 min, f32 max, f32 *value) +{ + Gui_Id widget_id = gui_id_from_pointer(ctx, value); + bool behaviour = gui_button_behaviuor(ctx, widget_id, r); + + if(ctx->active == widget_id) + { + f32 pointer_ratio = (ctx->input.pointer_position.x - r.position.x) / r.size.x; + *value = clamp(0.0f, 1.0f, pointer_ratio) * (max - min) + min; + } + + // Colors + v4 button_color = ctx->style.button_color; + v4 text_color = ctx->style.button_text_color; + { + f64 delta_t = (ctx->current_frame_time - ctx->hot_start_time); + f32 interpolation = sin(10 * delta_t) * 0.5 + 0.5; + if(ctx->hot == widget_id) + { + button_color = lerp(ctx->style.button_color, ctx->style.button_color_hovered, interpolation); + text_color = lerp(ctx->style.button_text_color, ctx->style.button_text_color_hovered, interpolation); + } + if(ctx->active == widget_id) + { + button_color = lerp(ctx->style.button_color_hovered, ctx->style.button_color_pressed, interpolation * 0.4 + 0.6); + text_color = lerp(ctx->style.button_text_color_hovered, ctx->style.button_text_color_pressed, interpolation * 0.4 + 0.6); + } + } + + // Draw + f32 border = 2; + f32 radius = ctx->style.button_radius; + + // Draw background + v4 background_color = ctx->style.button_color; + r_2d_immediate_rounded_rectangle(r, radius, background_color); // Background + + // Draw fill + f32 ratio = (*value - min) / (max - min); + Rect fill_r = r; + fill_r.position += v2{border, border}; + fill_r.size = v2{maximum(0, fill_r.size.x - 2*border), maximum(0, fill_r.size.y - 2*border)}; + f32 fill_radius = maximum(0, radius - border); + fill_r.size.x = fill_r.size.x * ratio; + r_2d_immediate_rounded_rectangle(fill_r, fill_radius, ctx->style.slider_fill_color); + + // Draw border + v4 border_color = ctx->style.button_color_pressed; + Rect border_r = r; + border_r.position += v2{border, border} * 0.5; + border_r.size = v2{maximum(0, border_r.size.x - border), maximum(0, border_r.size.y - border)}; + f32 border_radius = maximum(0, radius - border*0.5); + r_2d_immediate_rounded_rectangle_outline(border_r, border_radius, border_color, border); + + // Draw value text + char text[64]; + sprintf(text, "%f", *value); + gui_button_draw_inner_text(ctx, r, text, text_color); + + return behaviour || ctx->active == widget_id; +} + +bool gui_slider(Rect r, f32 min, f32 max, f32 *value) +{ + return gui_slider(&global_gui_state.default_context, r, min, max, value); +} + + +// Images +bool gui_image(Gui_Context *ctx, Rect r, r_texture *texture) +{ + Gui_Id widget_id = gui_id_from_pointer(ctx, texture); + + v4 color = {1,1,1,1}; + r_2d_immediate_rectangle(r, color, {0,0,1,1}, texture); + + return gui_button_behaviuor(ctx, widget_id, r);; +} + +bool gui_image(Rect r, r_texture *texture) +{ + return gui_image(&global_gui_state.default_context, r, texture); +} + +bool gui_image(Gui_Context *ctx, Rect r, const u8 *bmp, u32 width, u32 height, u32 channels, u32 flags) +{ + r_texture texture = r_texture_create((u8*)bmp, {width, height}, flags | R_TEXTURE_DONT_OWN); + bool result = gui_image(ctx, r, &texture); + r_texture_destroy(&texture); + return result; +} + +bool gui_image(Rect r, const u8 *bmp, u32 width, u32 height, u32 channels, u32 flags) +{ + return gui_image(&global_gui_state.default_context, r, bmp, width, height, channels, flags); +} + + +// Text input +bool gui_text_input(Gui_Context *ctx, Rect r, char *text, u64 max_size) +{ + Gui_Id widget_id = gui_id_from_pointer(ctx, text); + bool behaviour = gui_text_input_behaviuor(ctx, widget_id, r); + bool edited = false; + + // Cursor, mouse click, input from keyboard/os + if(ctx->active == widget_id && ctx->input.mouse_pressed_this_frame) + { + ctx->text_length = strlen(text); + ctx->text_cursor_position = ctx->text_length; + } + + // Move cursors between UTF8 codepoints (not bytes) + if(ctx->input.text_cursor_move != 0) + { + while(ctx->input.text_cursor_move > 0) + { + if(text[ctx->text_cursor_position] == '\0') + { + ctx->input.text_cursor_move = 0; + break; + } + ctx->text_cursor_position += utf8_bytes_to_next_valid_codepoint(text, ctx->text_cursor_position); + ctx->input.text_cursor_move--; + } + while(ctx->input.text_cursor_move < 0) + { + if(ctx->text_cursor_position == 0) + { + ctx->input.text_cursor_move = 0; + break; + } + ctx->text_cursor_position -= utf8_bytes_to_prev_valid_codepoint(text, ctx->text_cursor_position); + ctx->input.text_cursor_move++; + } + } + + if(ctx->active == widget_id && ctx->input.text[0] != 0) + { + // @Bug: Should iterate on utf8 codepoints. If we don't, there's the possibility + // of inserting half of a multi-byte codepoint. + for(char *c = ctx->input.text; *c != 0; c++) + { + if(*c == 0x08) // Backspace + { + if(ctx->text_cursor_position > 0) + { + u32 codepoint_bytes = utf8_bytes_to_prev_valid_codepoint(text, ctx->text_cursor_position); + u64 from_index = ctx->text_cursor_position; + u64 to_index = ctx->text_cursor_position - codepoint_bytes; + memmove(text + to_index, text + from_index, ctx->text_length + 1 - from_index); + ctx->text_length -= codepoint_bytes; + ctx->text_cursor_position -= codepoint_bytes; + } + continue; + } + + if(*c == 0x7F) // Delete + { + if(ctx->text_cursor_position < ctx->text_length) + { + u32 codepoint_bytes = utf8_bytes_to_next_valid_codepoint(text, ctx->text_cursor_position); + u64 from_index = ctx->text_cursor_position + codepoint_bytes; + u64 to_index = ctx->text_cursor_position; + memmove(text + to_index, text + from_index, ctx->text_length + 1 - from_index); + ctx->text_length -= codepoint_bytes; + } + continue; + } + + if(ctx->text_length < max_size - 1) // Leave space for 0 terminator + { + memmove(text + ctx->text_cursor_position + 1, text + ctx->text_cursor_position, ctx->text_length + 1 - ctx->text_cursor_position); + text[ctx->text_cursor_position] = *c; + ctx->text_length += 1; + ctx->text_cursor_position += 1; + } + } + + edited = true; + } + + r_2d_immediate_rounded_rectangle(r, ctx->style.button_radius, ctx->style.button_color); + Rect text_rect; + gui_button_draw_inner_text(ctx, r, text, ctx->style.button_text_color, &text_rect); + + if(ctx->active == widget_id) + { + // Draw cursor + f64 delta_t = ctx->current_frame_time - ctx->active_start_time; + f32 u = clamp(0, 1, sin(delta_t * 5) * 0.7 + 0.6); + v4 cursor_color = ctx->style.button_text_color; + cursor_color *= lerp(0, cursor_color.a, u); + + char replaced = text[ctx->text_cursor_position]; + text[ctx->text_cursor_position] = 0; + v2 cursor_position; + v2 text_size = gui_text_draw_size(text, ctx->style.font_size, &cursor_position); + text[ctx->text_cursor_position] = replaced; + + Rect cursor_rect = + { + .position = text_rect.position + cursor_position - v2{0, ctx->style.font_size}, + .size = ctx->style.font_size * v2{0.1, 0.9} + }; + r_2d_immediate_rectangle(cursor_rect, cursor_color); + } + + return edited; +} + +bool gui_text_input(Rect r, char *text, u64 max_size) +{ + return gui_text_input(&global_gui_state.default_context, r, text, max_size); +} + + + +// Windows +bool gui_window_start(Gui_Context *ctx, Rect r, Gui_Id id) +{ + gui_id_stack_push(ctx, id); + Gui_Window *window = gui_window_by_id(ctx, r, id); + window->still_open = true; + gui_window_update_rect(ctx, window, r); + u32 window_index = window - ctx->windows; + + bool hovered = gui_is_hovered(ctx, id, r); + if(hovered && ctx->input.mouse_pressed_this_frame) + { + // Bring window on top + u32 move_count = ctx->window_count - 1 - window_index; + if(move_count > 0) + { + Gui_Window tmp = *window; + memmove(ctx->windows + window_index, ctx->windows + window_index + 1, sizeof(Gui_Window) * move_count); + + ctx->windows[ctx->window_count - 1] = tmp; + window_index = ctx->window_count - 1; + window = &ctx->windows[window_index]; + } + } + + ctx->current_window = window; + ctx->input.pointer_position = ctx->input.absolute_pointer_position - window->r.position; + + ctx->old_framebuffer = r_render_state.current_framebuffer; + r_framebuffer_select(&window->framebuffer); + + + bool is_inactive = window_index != ctx->window_count-1; + v4 background_color = is_inactive ? ctx->style.window_background_color_inactive : +ctx->style.window_background_color; + v4 border_color = is_inactive ? ctx->style.window_border_color_inactive : +ctx->style.window_border_color; + r_clear({0,0,0,0}); + Rect background_rect = {0.5, 0.5, floor(r.w)-1.0, floor(r.h)-1.0}; + r_2d_immediate_rounded_rectangle(background_rect, ctx->style.window_corner_radius, background_color); + r_2d_immediate_rounded_rectangle_outline(background_rect, ctx->style.window_corner_radius, border_color, 1.0); + + return true; +} + +bool gui_window_start(Rect r, Gui_Id id) +{ + return gui_window_start(&global_gui_state.default_context, r, id); +} + +void gui_window_end(Gui_Context *ctx) +{ + gui_id_stack_pop(ctx); + ctx->current_window = NULL; + ctx->input.pointer_position = ctx->input.absolute_pointer_position; + + r_framebuffer_select(ctx->old_framebuffer); +} + +void gui_window_end() +{ + return gui_window_end(&global_gui_state.default_context); +} + + +bool gui_window_titlebar(Gui_Context *ctx, Rect r, const char *title, bool *close, v2 *move) +{ + Gui_Id widget_id = gui_id_from_pointer(ctx, title); + bool behaviour = gui_button_behaviuor(ctx, widget_id, r); + if(close) + *close = false; + if(move) + *move = {0,0}; + + // Background + v4 titlebar_color = ctx->style.window_titlebar_color_inactive; + if(ctx->current_window && ctx->current_window - ctx->windows == ctx->window_count - 1) + { + titlebar_color = ctx->style.window_titlebar_color; + } + r_2d_immediate_rounded_rectangle(r, ctx->style.window_corner_radius, titlebar_color); + + // Title + v2 title_size = gui_text_compute_size(title); + Rect title_r = r; + title_r.size = title_size; + title_r.position = r.position + (r.size - title_r.size) / 2; + gui_text(title_r, title); + + // Exit button + f32 smallest_side = minimum(r.w, r.h); + f32 exit_size = smallest_side; + Gui_Style exit_style = ctx->style; + exit_style.button_color = v4{0.8f, 0.8f, 0.8f, 1.0f}*0.0f; + exit_style.button_color_hovered = v4{1.0f, 0.0f, 0.0f, 1.0f}*1.0f; + exit_style.button_color_pressed = v4{0.8f, 0.0f, 0.0f, 1.0f}; + exit_style.button_text_color = v4{1.0f, 1.0f, 1.0f, 1.0f}; + exit_style.button_text_color_hovered = v4{1.0f, 1.0f, 1.0f, 1.0f}; + exit_style.button_text_color_pressed = v4{1.0f, 1.0f, 1.0f, 1.0f}; + exit_style.button_radius = ctx->style.window_corner_radius; + Gui_Style old_style = ctx->style; + ctx->style = exit_style; + Rect exit_button_r = {r.x + r.w - exit_size, r.y, exit_size, exit_size}; + if(gui_button(ctx, exit_button_r, "⨯")) + { + if(close) + *close = true; + behaviour = true; + } + ctx->style = old_style; + + // Move + if(ctx->active == widget_id && !ctx->input.mouse_pressed_this_frame) + { + if(move) + { + *move = ctx->input.absolute_pointer_position - ctx->input.absolute_pointer_position_last_frame; + } + } + + return behaviour || ctx->active == widget_id; +} + +bool gui_window_titlebar(Rect r, const char *title, bool *close, v2 *move) +{ + return gui_window_titlebar(&global_gui_state.default_context, r, title, close, move); +} + + + +Gui_Window *gui_window_by_id(Gui_Context *ctx, Rect r, Gui_Id id) +{ + Gui_Window *window = NULL; + for(u32 i = 0; i < ctx->window_count; i++) + { + if(ctx->windows[i].id == id) + { + window = &ctx->windows[i]; + break; + } + } + + if(!window) + { + if(ctx->window_count >= ctx->window_capacity) + { + if(ctx->window_capacity == 0) + ctx->window_capacity = 1; + ctx->window_capacity *= 2; + ctx->windows = (Gui_Window*) p_realloc(ctx->windows, sizeof(Gui_Window) * ctx->window_capacity); + } + + window = &ctx->windows[ctx->window_count]; + ctx->window_count++; + + window->id = id; + window->r = r; + window->framebuffer = r_framebuffer_create(V2S(r.size), 0); + } + + return window; +} + +void gui_window_update_rect(Gui_Context *ctx, Gui_Window *window, Rect r) +{ + if(window->r.size != r.size) + { + r_framebuffer_update_size(&window->framebuffer, V2S(r.size)); + } + window->r = r; +} + + +// Helpers +bool gui_is_hovered(Gui_Context *ctx, Gui_Id widget_id, Rect r) +{ + if(is_inside(r, ctx->input.pointer_position)) + { + s32 current_window_index = -1; // We use -1 to indicate we are not in a window. When we iterate over windows we do a +1 and start from 0, aka the first window. If we used 0, we would start from 1 and skip over window index 0. + + // The ctx->windows array is sorted from back to front. If we are inside a window, only the following windows in the array can overlap up. The ones before are covered by the current window. + if(ctx->current_window) + current_window_index = ctx->current_window - ctx->windows; + + // Am I a window? If so, we start checking from us. If ctx->current_window is set and widget_id is a window, it means we are a subwindow. + // Subwindow are not supported yet though (20 September 2023), so this should be a bug in the user code. Yeah we don't check to prevent this, but anyways. + for(s32 i = current_window_index + 1; i < ctx->window_count; i++) + { + Gui_Id window_id = ctx->windows[i].id; + if(widget_id == window_id) + { + current_window_index = i; + break; + } + } + + // Iterate over windows that cover the current one + for(u32 i = current_window_index + 1; i < ctx->window_count; i++) + { + Gui_Id window_id = ctx->windows[i].id; + Rect window_rect = ctx->windows[i].r; + if(is_inside(window_rect, ctx->input.absolute_pointer_position)) + { + return false; + } + } + return true; + } + return false; +} + +bool gui_button_behaviuor(Gui_Context *ctx, Gui_Id widget_id, Rect r) +{ + bool behaviour = false; + if(gui_is_hovered(ctx, widget_id, r)) + { + if(!ctx->active || ctx->active == widget_id || !(ctx->active_status & GUI_WIDGET_STATUS_PREVENT_HOT)) + ctx->possibly_hot = widget_id; + + if(ctx->hot == widget_id && ctx->input.mouse_pressed_this_frame) + { + ctx->active = widget_id; + ctx->active_start_time = ctx->current_frame_time; + ctx->active_status = GUI_WIDGET_STATUS_PREVENT_HOT; + } + + if(ctx->active == widget_id && ctx->input.mouse_released_this_frame) + { + behaviour = true; + } + } + + if(ctx->active == widget_id && ctx->input.mouse_released_this_frame) + { + ctx->active = NULL; + ctx->active_status = 0; + } + + return behaviour; +} + + +bool gui_text_input_behaviuor(Gui_Context *ctx, Gui_Id widget_id, Rect r) +{ + bool behaviour = false; + if(gui_is_hovered(ctx, widget_id, r)) + { + if(!ctx->active || ctx->active == widget_id || !(ctx->active_status & GUI_WIDGET_STATUS_PREVENT_HOT)) + ctx->possibly_hot = widget_id; + + if(ctx->hot == widget_id && ctx->input.mouse_pressed_this_frame) + { + ctx->active = widget_id; + ctx->active_start_time = ctx->current_frame_time; + ctx->active_status = 0; + } + + if(ctx->active == widget_id && ctx->input.mouse_released_this_frame) + { + behaviour = true; + } + } + + if(ctx->active == widget_id && ctx->input.mouse_released_this_frame) + { + // ctx->active = NULL; + // ctx->active_status = 0; + } + + return behaviour; +} + +Gui_Id gui_id_from_pointer(Gui_Context *ctx, const void* ptr) +{ + u32 seed = 0xFFFFFFFF; + if(ctx->id_count) + seed = ctx->id_stack[ctx->id_count - 1]; + return hash_crc32(&ptr, sizeof(void*), seed); +} + +void gui_id_stack_push(Gui_Context *ctx, Gui_Id id) +{ + if(ctx->id_capacity <= ctx->id_count) + { + u32 new_capacity = maximum(ctx->id_count + 1, ctx->id_capacity * 2); + ctx->id_stack = (Gui_Id*)p_realloc(ctx->id_stack, sizeof(Gui_Id) * new_capacity); + ctx->id_capacity = new_capacity; + } + + ctx->id_stack[ctx->id_count] = id; + ctx->id_count++; +} + +void gui_id_stack_pop(Gui_Context *ctx) +{ + if(ctx->id_count > 0) + ctx->id_count--; +} diff --git a/code/gui/gui.h b/code/gui/gui.h new file mode 100644 index 0000000..d01b2dc --- /dev/null +++ b/code/gui/gui.h @@ -0,0 +1,195 @@ +#ifndef _PIUMA_GUI_H_ +#define _PIUMA_GUI_H_ + +#include "../lib/types.h" +#include "../lib/math.h" +#include "../lib/geometry.h" +#include "../render/2d.h" +#include "../lib/text.h" +#include "../lib/event.h" + + + +typedef u32 Gui_Id; + + +struct Gui_Input +{ + v2 pointer_position; + v2 absolute_pointer_position; + bool mouse_pressed; + bool mouse_pressed_this_frame; + bool mouse_released_this_frame; + s64 text_cursor_move; + char text[32]; + + v2 absolute_pointer_position_last_frame; +}; + +enum Gui_Text_Align +{ + GUI_ALIGN_CENTER, + GUI_ALIGN_LEFT, + GUI_ALIGN_RIGHT +}; + +struct Gui_Style +{ + f32 font_size; + f32 animation_base_time; + + v4 text_color; + Gui_Text_Align text_align; + + v4 button_color; + v4 button_color_hovered; + v4 button_color_pressed; + v4 button_text_color; + v4 button_text_color_hovered; + v4 button_text_color_pressed; + f32 button_radius; + + v4 slider_fill_color; + + v4 window_background_color; + v4 window_border_color; + v4 window_background_color_inactive; + v4 window_border_color_inactive; + f32 window_corner_radius; + v4 window_titlebar_color; + v4 window_titlebar_color_inactive; +}; + +struct Gui_Window +{ + Gui_Id id; + Rect r; + r_framebuffer framebuffer; + bool still_open; +}; + +enum Gui_Widget_Status_Flags : u8 +{ + GUI_WIDGET_STATUS_NONE, + GUI_WIDGET_STATUS_PREVENT_HOT +}; + +struct Gui_Context +{ + u32 width; + u32 height; + + f64 last_frame_time; + f64 current_frame_time; + + Gui_Id active; // Are we interacting with the widget (pressed for buttons, ready to receive text for text inputs) + Gui_Id hot; // Hovered the previous frame + Gui_Id possibly_hot; // Might become hot this frame + f64 active_start_time; + f64 hot_start_time; + u8 active_status; + + // Text input state + u64 text_cursor_position; + u64 text_length; + + // Windows + Gui_Window *windows; + u32 window_count; + u32 window_capacity; + Gui_Window *current_window; + r_framebuffer *old_framebuffer; + + // ID + Gui_Id *id_stack; + u32 id_count; + u32 id_capacity; + + Gui_Input input; + Gui_Style style; +}; + +struct Gui_State +{ + Gui_Context default_context; + Gui_Context *selected_context; +}; + + +extern Gui_State global_gui_state; + + + + +bool gui_init(); +void gui_deinit(); + +void gui_context_init(Gui_Context *ctx); +//@Correctness: gui_context_deinit + +void gui_context_select(Gui_Context *ctx); // Set implicit Gui_Context + +void gui_frame_begin(Gui_Context *ctx, f64 curr_time); +void gui_frame_begin(f64 curr_time); +void gui_frame_end(Gui_Context *ctx); +void gui_frame_end(); + +void gui_handle_event(Gui_Context *ctx, Event *e); +void gui_handle_event(Event *e); + +// ### Widgets ### +// Text +void gui_text(Gui_Context *ctx, Rect r, const char *text); +void gui_text(Rect r, const char *text); +void gui_text_aligned(Gui_Context *ctx, Rect r, const char *text, Gui_Text_Align alignment); +void gui_text_aligned(Rect r, const char *text, Gui_Text_Align alignment); + +v2 gui_text_compute_size(Gui_Context *ctx, const char *text); +v2 gui_text_compute_size(const char *text); + +// Button +bool gui_button(Gui_Context *ctx, Rect r, const char *text); +bool gui_button(Rect r, const char *text); + +// Slider +bool gui_slider(Gui_Context *ctx, Rect r, f32 min, f32 max, f32 *value); +bool gui_slider(Rect r, f32 min, f32 max, f32 *value); + +// Checkbox +// Option buttons +// Combo box +// Tooltips + +// Images +bool gui_image(Gui_Context *ctx, Rect r, r_texture *texture); +bool gui_image(Rect r, r_texture *texture); +bool gui_image(Gui_Context *ctx, Rect r, const u8 *bmp, u32 width, u32 height, u32 channels, u32 flags = 0); +bool gui_image(Rect r, const u8 *bmp, u32 width, u32 height, u32 channels, u32 flags = 0); + +// Text input +bool gui_text_input(Gui_Context *ctx, Rect r, char *text, u64 max_size); +bool gui_text_input(Rect r, char *text, u64 max_size); + +// Windows +bool gui_window_start(Gui_Context *ctx, Rect r, Gui_Id id); // You have to provide some kind of unique id to identify windows +bool gui_window_start(Rect r, Gui_Id id); +void gui_window_end(Gui_Context *ctx); +void gui_window_end(); + +bool gui_window_titlebar(Gui_Context *ctx, Rect r, const char *title, bool *close, v2 *move); +bool gui_window_titlebar(Rect r, const char *title, bool *close, v2 *move); + +Gui_Window *gui_window_by_id(Gui_Context *ctx, Rect r, Gui_Id id); // Rect r might be needed for creation +void gui_window_update_rect(Gui_Context *ctx, Gui_Window *window, Rect r); + +// Helpers +bool gui_is_hovered (Gui_Context *ctx, Gui_Id widget_id, Rect r); +bool gui_button_behaviuor (Gui_Context *ctx, Gui_Id widget_id, Rect r); +bool gui_text_input_behaviuor(Gui_Context *ctx, Gui_Id widget_id, Rect r); +bool gui_window_behaviour (Gui_Context *ctx, Gui_Id widget_id, Rect r); + +Gui_Id gui_id_from_pointer(Gui_Context *ctx, const void* ptr); +void gui_id_stack_push(Gui_Context *ctx, Gui_Id id); +void gui_id_stack_pop(Gui_Context *ctx); + +#endif diff --git a/code/gui/layout.cpp b/code/gui/layout.cpp new file mode 100644 index 0000000..847168d --- /dev/null +++ b/code/gui/layout.cpp @@ -0,0 +1,159 @@ +#include "layout.h" +#include "gui.h" + +// Grid +Rect Gui_Layout_Grid::rect() +{ + return last_rect; +} + +void Gui_Layout_Grid::row(u32 count) +{ + cursor.x = 0; + cursor.y += count; +} + +Rect Gui_Layout_Grid::cell(u32 count) +{ + count = minimum(count, max_cells_count.x); + s32 free_space_in_row = max_cells_count.x - cursor.x; + if(free_space_in_row < count) + row(); + + last_rect = rect_at(cursor, {count, 1}); + cursor.x += count; + + return last_rect; +} + +Rect Gui_Layout_Grid::rect_at(v2s cell_index, v2s size) +{ + Rect result; + v2 cell_border_v = v2{cell_border, cell_border}; + result.position = window_position + cell_border_v + (cell_border_v + cell_size) * V2(cell_index); + result.size = cell_size * V2(size) + cell_border_v * (V2(size - v2s{1,1})); + return result; +} + + + +Gui_Layout_Grid gui_layout_grid_create_by_divisions(v2 position, v2 window_size, u32 divisions_x, u32 divisions_y, f32 border) +{ + Gui_Layout_Grid layout; + layout.window_position = position; + layout.window_size = window_size; + layout.cell_size = (window_size - v2{1,1}*border) / v2{divisions_x, divisions_y} - v2{1,1}*border; + layout.cell_border = border; + layout.max_cells_count = v2s{divisions_x, divisions_y}; + + layout.cursor = {0,0}; + layout.last_rect = layout.rect_at({0,0}); + + return layout; +} + +Gui_Layout_Grid gui_layout_grid_create_by_cell_size(v2 position, v2 window_size, v2 cell_size, f32 border) +{ + Gui_Layout_Grid layout; + layout.window_position = position; + layout.window_size = window_size; + layout.cell_size = cell_size; + layout.cell_border = border; + layout.max_cells_count = V2S((window_size - v2{1,1}*border) / (cell_size + v2{1,1}*border)); + + layout.cursor = {0,0}; + layout.last_rect = layout.rect_at({0,0}); + + return layout; +} + + +// Basic +Rect Gui_Layout_Basic::rect() +{ + return last_rect; +} + +void Gui_Layout_Basic::push_rect(Rect r) +{ + cursor.x = cursor.x + r.size.x + element_distance.x; + cursor.y = cursor.y; + + last_rect = r; + next_row_y = maximum(next_row_y, cursor.y + r.size.y + element_distance.y); +} + +void Gui_Layout_Basic::row() +{ + cursor.x = window_padding.x; + cursor.y = next_row_y; + next_row_y = cursor.y + font_size + element_distance.y; +} + +Rect Gui_Layout_Basic::label_rect(const char *text) +{ + Rect r; + r.size.y = font_size; + r.size.x = gui_text_compute_size(text).x; + + f32 remaining_space_x = window_size.x - 2*window_padding.x - cursor.x; + if(cursor.x != window_padding.x && remaining_space_x < r.size.x) + row(); + + r.position = window_position + cursor; + + push_rect(r); + return r; +} + +Rect Gui_Layout_Basic::button_rect(const char *text) +{ + Rect r; + r.size.y = font_size; + r.size.x = gui_text_compute_size(text).x; + r.size += 2*button_padding; + + f32 remaining_space_x = window_size.x - 2*window_padding.x - cursor.x; + if(cursor.x != window_padding.x && remaining_space_x < r.size.x) + row(); + + r.position = window_position + cursor; + + push_rect(r); + return r; +} + +Rect Gui_Layout_Basic::button_rect(f32 length) +{ + Rect r; + r.size.y = font_size; + r.size.x = length; + r.size.y += 2*button_padding.y; // No x padding in this case. We already have an assigned length + + f32 remaining_space_x = window_size.x - 2*window_padding.x - cursor.x; + if(cursor.x != window_padding.x && remaining_space_x < r.size.x) + row(); + + r.position = window_position + cursor; + + push_rect(r); + return r; +} + +Gui_Layout_Basic gui_layout_basic_create(v2 position, v2 size, f32 font_size) +{ + Gui_Layout_Basic layout; + layout.window_position = position; + layout.window_size = size; + + layout.cursor = {0,0}; + layout.next_row_y = 0; + layout.last_rect = {0,0,0,0}; + + layout.font_size = font_size; + layout.button_padding = {0,0}; + layout.element_distance = {0,0}; + layout.window_padding = {0,0}; + + return layout; +} diff --git a/code/gui/layout.h b/code/gui/layout.h new file mode 100644 index 0000000..8f74dce --- /dev/null +++ b/code/gui/layout.h @@ -0,0 +1,57 @@ +#ifndef _PIUMA_GUI_LAYOUT_H_ +#define _PIUMA_GUI_LAYOUT_H_ + +#include "../lib/types.h" +#include "../lib/geometry.h" + +// Grid +const s32 GUI_LAYOUT_MAX_CELLS = 0x7FFFFFFF; + +struct Gui_Layout_Grid +{ + v2 window_position; + v2 window_size; + v2 cell_size; + f32 cell_border; + v2s max_cells_count; + + v2s cursor; + Rect last_rect; + + Rect rect(); // Get last rect + void row(u32 count = 1); + Rect cell(u32 count = 1); + Rect rect_at(v2s position, v2s size = {1,1}); // Does not modify cursor. You have to assign it yourself. + +}; + +Gui_Layout_Grid gui_layout_grid_create_by_divisions(v2 position, v2 window_size, u32 divisions_x, u32 divisions_y, f32 border = 0); +Gui_Layout_Grid gui_layout_grid_create_by_cell_size(v2 position, v2 window_size, v2 cell_size, f32 border = 0); + + +// Basic +struct Gui_Layout_Basic +{ + v2 window_position; + v2 window_size; + + v2 cursor = {0,0}; + f32 next_row_y = 0; + Rect last_rect = {0,0,0,0}; + + f32 font_size; + v2 button_padding = {0,0}; + v2 element_distance = {0,0}; + v2 window_padding = {0,0}; + + Rect rect(); + void push_rect(Rect r); + void row(); + Rect label_rect(const char *text = ""); + Rect button_rect(const char *text = ""); + Rect button_rect(f32 length); +}; + +Gui_Layout_Basic gui_layout_basic_create(v2 position, v2 size, f32 font_size); + +#endif diff --git a/code/gui/text_draw.cpp b/code/gui/text_draw.cpp new file mode 100644 index 0000000..449f59f --- /dev/null +++ b/code/gui/text_draw.cpp @@ -0,0 +1,548 @@ +#include "text_draw.h" +#include "../lib/color.h" +#include "../lib/math.h" +#include "../lib/geometry.h" +#include "../lib/text.h" +#include "../lib/ds.h" +#include "../platform.h" +#include "stb_truetype.h" +#include "../debug/logger.h" +#include "../assets.h" +#include "../enginestate.h" +#include +#include + + + +struct gui_glyph_info +{ + utf8_codepoint codepoint; + Rect box; // .position = offset from position for alignment, .size = size of the bitmap to draw + v2u position; // Position of the top left border in the texture + s32 advance; + u32 next; // Anything >= container_capacity is to be considered NULL + u32 previous; +}; + + +/* Glyph caching: + * We store a small number of sizes and a fairly large number of characters for each size. + * Each slot will have the same width and height, so that we can easily replace it without + * re-packing everything. This will also make the code simpler. + * We use 0xFFFFFFFF as a placeholder for empty slots. + * + */ + +struct gui_glyph_codepoint_map +{ + utf8_codepoint codepoint; + u32 index; +}; + +struct gui_glyph_texture +{ + f32 font_size; + + struct gui_glyph_codepoint_map *sorted_indices; + gui_glyph_info *info; + u32 oldest; + u32 newest; + u32 capacity; + + v2s glyph_max_size; + + r_texture *texture; +}; + +struct gui_glyph_cache +{ + gui_glyph_texture *glyphs; + u32 capacity; + u32 oldest; + + u32 max_glyphs_per_texture; +}; + +static void gui_glyph_cache_init(); +static void gui_glyph_cache_deinit(); +static gui_glyph_texture gui_glyph_texture_create(f32 font_size, u32 capacity); +static void gui_glyph_texture_destroy(gui_glyph_texture *glyphs); +static gui_glyph_texture *gui_glyph_cache_texture_for_codepoints(f32 font_size, const utf8_codepoint *codepoints, u64 count); + + + + +// Globals +static u8 *Font_File; +static stbtt_fontinfo Font_Info; +static gui_glyph_cache Glyph_Cache; + + +bool gui_text_draw_init() +{ + // @Feature: user specified font + // @Cleanup: one-line file read + p_file f; + p_file_init(&f, "assets/fonts/DejaVuSerif-Bold.ttf"); + Buffer buf; + buf.size = p_file_size(&f); + buf.data = (u8*)p_alloc(buf.size + 1); + buf.data[buf.size] = '\0'; + p_file_read(&f, &buf, buf.size); + p_file_deinit(&f); + Font_File = buf.data; + + if(!stbtt_InitFont(&Font_Info, Font_File, 0)) + { + LOG(LOG_ERROR, "Cannot load font."); + return false; + } + + gui_glyph_cache_init(); + + return true; +} + +void gui_text_draw_deinit() +{ + gui_glyph_cache_deinit(); + p_free(Font_File); +} + +v2 gui_text_draw_size(const char *text, f32 font_size, v2 *cursor_position) +{ + // UTF8 conversion + u64 text_length = utf8_codepoint_count(text); + utf8_codepoint *codepoints = (utf8_codepoint*) p_alloc(text_length * sizeof(utf8_codepoint)); + { + u64 bytes_read = 0; + text_length = utf8_from_string(text, &bytes_read, codepoints, text_length); + } + + // Compute size + v2 result = gui_utf8_text_draw_size(codepoints, text_length, font_size, cursor_position); + + p_free(codepoints); + return result; +} + +void gui_text_draw(Rect r, const char *text, f32 font_size, v4 color) +{ + // UTF8 conversion + u64 text_length = utf8_codepoint_count(text); + utf8_codepoint *codepoints = (utf8_codepoint*) p_alloc(text_length * sizeof(utf8_codepoint)); + { + u64 bytes_read = 0; + text_length = utf8_from_string(text, &bytes_read, codepoints, text_length); + } + + // Draw + gui_utf8_text_draw(r, codepoints, text_length, font_size, color); + + p_free(codepoints); +} + + +v2 gui_utf8_text_draw_size(const utf8_codepoint *text, u64 length, f32 font_size, v2 *cursor_position) +{ + f32 font_scale; + s32 font_ascent; + s32 font_descent; + s32 font_line_gap; + s32 font_baseline; + + { + font_scale = stbtt_ScaleForPixelHeight(&Font_Info, font_size); + stbtt_GetFontVMetrics(&Font_Info, &font_ascent, &font_descent, &font_line_gap); + font_baseline = font_ascent; + + font_ascent *= font_scale; + font_descent *= font_scale; + font_line_gap *= font_scale; + font_baseline *= font_scale; + } + + // Compute size + v2 size = {0, (f32)(font_ascent - font_descent)}; + { + v2 cursor = {0, (f32)(font_ascent - font_descent)}; + for(u64 i = 0; i < length; i++) + { + s32 advance, lsb; + stbtt_GetCodepointHMetrics(&Font_Info, text[i], &advance, &lsb); + + // Special characters + if(text[i] == 10) // '\n' + { + advance = 0; + cursor.x = 0; + cursor.y += font_ascent - font_descent + font_line_gap; + size.y = cursor.y; + } + + // Normal characters + cursor.x += floor(advance * font_scale); // Remember to consider kerning + size.x = maximum(size.x, cursor.x); + } + if(cursor_position) + *cursor_position = cursor; + } + + return size; +} + +void gui_utf8_text_draw(Rect r, const utf8_codepoint *text, u64 length, f32 font_size, v4 color) +{ + f32 font_scale; + s32 font_ascent; + s32 font_descent; + s32 font_line_gap; + s32 font_baseline; + + { + font_scale = stbtt_ScaleForPixelHeight(&Font_Info, font_size); + stbtt_GetFontVMetrics(&Font_Info, &font_ascent, &font_descent, &font_line_gap); + font_baseline = font_ascent; + + font_ascent *= font_scale; + font_descent *= font_scale; + font_line_gap *= font_scale; + font_baseline *= font_scale; + } + + // Compute glyphs + gui_glyph_texture *glyphs = gui_glyph_cache_texture_for_codepoints(font_size, text, length); + + // Map text to quads + v2 *vertices; + v2 *uvs; + u64 draw_count = 0; + { + vertices = (v2*) p_alloc(6 * length * sizeof(v2)); // 2 triangles, 3 vertices each = 6 vertices + uvs = (v2*) p_alloc(6 * length * sizeof(v2)); + + + v2 position = r.position; + position.y += font_baseline; + for(u64 i = 0; i < length; i++) + { + gui_glyph_codepoint_map *found = (gui_glyph_codepoint_map*) bsearch(&text[i], glyphs->sorted_indices, glyphs->capacity, sizeof(gui_glyph_codepoint_map), u32_cmp); + if(found == NULL) + { + LOG(LOG_ERROR, "Cannot find codepoint 0x%X in glyph list.", text[i]); + continue; + } + + u64 glyph_i = glyphs->sorted_indices[found - glyphs->sorted_indices].index; + gui_glyph_info *info = &glyphs->info[glyph_i]; + + // Special characters + if(text[i] == 10) // '\n' + { + position.x = r.position.x; + position.y += font_ascent - font_descent + font_line_gap; + } + + // Normal characters + // Map character to its vertices + { + Rect r; + Rect r_uv; + r.position = position + info->box.position; + r.position.x = floor(r.position.x + 0.5); + r.position.y = floor(r.position.y + 0.5); + r.size = info->box.size; + + r_uv.position = V2(info->position) / V2(glyphs->texture->size); + r_uv.size = info->box.size / V2(glyphs->texture->size); + + v2 a = r.position + v2{r.w, 0 }; + v2 b = r.position + v2{0 , 0 }; + v2 c = r.position + v2{0 , r.h}; + v2 d = r.position + v2{r.w, r.h}; + v2 a_uv = r_uv.position + v2{r_uv.w, 0 }; + v2 b_uv = r_uv.position + v2{0 , 0 }; + v2 c_uv = r_uv.position + v2{0 , r_uv.h}; + v2 d_uv = r_uv.position + v2{r_uv.w, r_uv.h}; + + vertices[6*draw_count + 0] = a; + vertices[6*draw_count + 1] = b; + vertices[6*draw_count + 2] = c; + vertices[6*draw_count + 3] = a; + vertices[6*draw_count + 4] = c; + vertices[6*draw_count + 5] = d; + uvs[6*draw_count + 0] = a_uv; + uvs[6*draw_count + 1] = b_uv; + uvs[6*draw_count + 2] = c_uv; + uvs[6*draw_count + 3] = a_uv; + uvs[6*draw_count + 4] = c_uv; + uvs[6*draw_count + 5] = d_uv; + } + + + position.x += info->advance; // Remember to consider kerning + draw_count++; + } + } + + // Render quads + r_2d_immediate_mesh(6*draw_count, vertices, color, uvs, glyphs->texture); + + p_free(vertices); + p_free(uvs); +} + + +static void gui_glyph_cache_init() +{ + Glyph_Cache.capacity = 4; + Glyph_Cache.oldest = 0; + Glyph_Cache.glyphs = (gui_glyph_texture*)p_alloc(sizeof(gui_glyph_texture) * Glyph_Cache.capacity); + memset(Glyph_Cache.glyphs, 0, sizeof(gui_glyph_texture) * Glyph_Cache.capacity); + Glyph_Cache.max_glyphs_per_texture = 256; // @Correctness: test with small values, to trigger the glyph replacement code +} + +static void gui_glyph_cache_deinit() +{ + for(u32 i = 0; i < Glyph_Cache.capacity; i++) + gui_glyph_texture_destroy(&Glyph_Cache.glyphs[i]); + p_free(Glyph_Cache.glyphs); + Glyph_Cache.glyphs = NULL; + Glyph_Cache.capacity = 0; + Glyph_Cache.oldest = 0; +} + +static gui_glyph_texture gui_glyph_texture_create(f32 font_size, u32 capacity) +{ + // Init container for glyphs and info + gui_glyph_texture glyphs; + glyphs.font_size = font_size; + glyphs.sorted_indices = (gui_glyph_codepoint_map*) p_alloc(sizeof(gui_glyph_codepoint_map) * capacity); + glyphs.info = (gui_glyph_info*) p_alloc(sizeof(gui_glyph_info) * capacity); + glyphs.oldest = 0; + glyphs.newest = capacity - 1; + glyphs.capacity = capacity; + + // Estimate max glyph size. + // @Correctness: Text draw will fail if a bigger glyph is used. + f32 font_scale = stbtt_ScaleForPixelHeight(&Font_Info, font_size); + utf8_codepoint cp[] = {' ', 'M', 'j', '{', '=', 'w', 'W'}; + glyphs.glyph_max_size = v2s{0, 0}; + for(s32 i = 0; i < sizeof(cp)/sizeof(utf8_codepoint); i++) + { + v2s top_left, bottom_right; + stbtt_GetCodepointBitmapBox(&Font_Info, cp[i], font_scale, font_scale, &top_left.x, &top_left.y, &bottom_right.x, &bottom_right.y); + v2s size = bottom_right - top_left; + + glyphs.glyph_max_size.x = maximum(glyphs.glyph_max_size.x, size.x); + glyphs.glyph_max_size.y = maximum(glyphs.glyph_max_size.y, size.y); + } + LOG(LOG_DEBUG, "Font size %f not in cache. Slot size (%d %d)", font_size, glyphs.glyph_max_size.x, glyphs.glyph_max_size.y); + + // Precompile some info data + for(u32 i = 0; i < glyphs.capacity; i++) + { + glyphs.info[i] = gui_glyph_info{ + .codepoint = 0xFFFFFFFF, + .box = {0,0,0,0}, + .position = v2u{glyphs.glyph_max_size.x * i, 0}, + .advance = 0, + .next = i + 1, // Last .next will be >= capacity (== capacity to be precise), so we will consider it to be NULL + .previous = ((i == 0) ? glyphs.capacity : (i - 1)) + }; + + glyphs.sorted_indices[i] = gui_glyph_codepoint_map{0xFFFFFFFF, i}; + } + + // Initialize texture + v2s texture_size = v2s{glyphs.glyph_max_size.x * glyphs.capacity, glyphs.glyph_max_size.y}; + u8 *texture_data = (u8*)p_alloc(sizeof(u8) * texture_size.x * texture_size.y); + memset(texture_data, 0, sizeof(u8) * texture_size.x * texture_size.y); + + glyphs.texture = assets_new_textures(&engine.am, 1); + *glyphs.texture = r_texture_create(texture_data, texture_size, R_TEXTURE_ALPHA | R_TEXTURE_NO_MIPMAP); + + return glyphs; +} + +static void gui_glyph_texture_destroy(gui_glyph_texture *glyphs) +{ + if(glyphs->sorted_indices) + p_free(glyphs->sorted_indices); + if(glyphs->info) + p_free(glyphs->info); + if(glyphs->texture) + r_texture_destroy(glyphs->texture); + glyphs->sorted_indices = NULL; + glyphs->info = NULL; + glyphs->texture = NULL; + + glyphs->font_size = 0; + glyphs->oldest = 0; + glyphs->newest = 0; + glyphs->capacity = 0; +} + +static gui_glyph_texture *gui_glyph_cache_texture_for_codepoints(f32 font_size, const utf8_codepoint *codepoints, u64 count) +{ + // Approximate font_size. We don't really want to build different bitmaps for size 12.000000 and 12.000001. + // This will also prevent floating point rounding errors from rebuilding the cache. + font_size = floor(font_size * 10) / 10; + + // Find cached texture for this size or build a new one + gui_glyph_texture *glyphs = NULL; + for(u32 i = 0; i < Glyph_Cache.capacity; i++) + { + //LOG(LOG_DEBUG, "Font size: %f - Cached: %f", font_size, Glyph_Cache.glyphs[i].font_size); + if(abs(Glyph_Cache.glyphs[i].font_size - font_size) < 0.01) + { + glyphs = &Glyph_Cache.glyphs[i]; + } + } + if(glyphs == NULL) + { + //LOG(LOG_DEBUG, "Size not matched"); + glyphs = &Glyph_Cache.glyphs[Glyph_Cache.oldest]; + Glyph_Cache.oldest = (Glyph_Cache.oldest + 1) % Glyph_Cache.capacity; + + gui_glyph_texture_destroy(glyphs); + *glyphs = gui_glyph_texture_create(font_size, Glyph_Cache.max_glyphs_per_texture); + } + + + + // Build list of unique codepoints (so that we don't render the same codepoint twice) + utf8_codepoint *unique = (utf8_codepoint*) p_alloc(count * sizeof(utf8_codepoint)); + memcpy(unique, codepoints, count * sizeof(utf8_codepoint)); + u64 unique_count = make_unique(unique, count, sizeof(utf8_codepoint), u32_cmp); + if(unique_count > glyphs->capacity) + LOG(LOG_ERROR, "Unique codepoints count > cache capacity. Some codepoints will not be rendered."); + + // Find which codepoints are not already in the cache and need to be rendered + utf8_codepoint to_render[unique_count]; + u32 to_render_count = 0; + for(u32 i = 0; i < unique_count; i++) + { + gui_glyph_codepoint_map *found = (gui_glyph_codepoint_map*) bsearch(&unique[i], glyphs->sorted_indices, glyphs->capacity, sizeof(gui_glyph_codepoint_map), u32_cmp); + if(found == NULL) + { + // Not found -> add to the list of glyphs to render + to_render[to_render_count] = unique[i]; + to_render_count++; + } + else + { + // Found -> mark it as recent, so that is does not get deleted prematurely + u32 index = glyphs->sorted_indices[found - glyphs->sorted_indices].index; + if(index == glyphs->newest) + { + // Already the newest. Do nothing. + } + else if(index == glyphs->oldest) + { + // We have no previous to fix, only next + u32 next = glyphs->info[index].next; + glyphs->info[next].previous = glyphs->capacity; // Next is the new oldest -> no previous + glyphs->oldest = next; + + // Set index as last element + glyphs->info[index].next = glyphs->capacity; + glyphs->info[index].previous = glyphs->newest; + glyphs->info[glyphs->newest].next = index; + glyphs->newest = index; + } + else + { + // We in between the list. We have both previous and next elements to fix. + u32 previous = glyphs->info[index].previous; + u32 next = glyphs->info[index].next; + glyphs->info[previous].next = next; + glyphs->info[next].previous = previous; + + // Set index as last element + glyphs->info[index].next = glyphs->capacity; + glyphs->info[index].previous = glyphs->newest; + glyphs->info[glyphs->newest].next = index; + glyphs->newest = index; + } + } + } + + p_free(unique); + + + // Get info for rendering + f32 font_scale; + s32 font_ascent; + s32 font_descent; + s32 font_line_gap; + s32 font_baseline; + { + font_scale = stbtt_ScaleForPixelHeight(&Font_Info, font_size); + stbtt_GetFontVMetrics(&Font_Info, &font_ascent, &font_descent, &font_line_gap); + font_baseline = font_ascent; + + font_ascent *= font_scale; + font_descent *= font_scale; + font_line_gap *= font_scale; + font_baseline *= font_scale; + } + + // Render glyph in its appropriate place + for(u32 i = 0; i < to_render_count; i++) + { + u32 index = glyphs->oldest; + glyphs->oldest = glyphs->info[index].next; + glyphs->info[glyphs->oldest].previous = glyphs->capacity; + + glyphs->info[index].next = glyphs->capacity; + glyphs->info[index].previous = glyphs->newest; + glyphs->info[index].codepoint = to_render[i]; + + glyphs->info[glyphs->newest].next = index; + glyphs->newest = index; + + + // Complete gui_glyph_info structure and render + gui_glyph_info *info = &glyphs->info[index]; + + v2s top_left, bottom_right; + stbtt_GetCodepointBitmapBox(&Font_Info, info->codepoint, font_scale, font_scale, &top_left.x, &top_left.y, &bottom_right.x, &bottom_right.y); + s32 advance, lsb; + stbtt_GetCodepointHMetrics(&Font_Info, info->codepoint, &advance, &lsb); + + v2s size = bottom_right - top_left; + + // Special codepoints + if(info->codepoint == 10) // '\n' + { + size = {0, 0}; + advance = 0; + } + + info->box.position = V2(top_left); + info->box.size = V2(size); + //info->position = v2u{glyphs->glyph_max_size.x * index, 0}; // Commented because it's already pre-computed. + info->advance = advance * font_scale; + + // @Correctness: needs to be premultiplied alpha + stbtt_MakeCodepointBitmap(&Font_Info, glyphs->texture->data + info->position.x, info->box.size.x, info->box.size.y, glyphs->texture->size.x, font_scale, font_scale, info->codepoint); + r_texture_update(glyphs->texture, glyphs->texture->data + info->position.x, V2S(info->box.size), V2S(info->position), glyphs->texture->size.x); + } + + // Build sorted array with indices that point to the element + u32 nonempty_count = glyphs->capacity; + for(u32 i = 0; i < glyphs->capacity; i++) + { + glyphs->sorted_indices[i] = gui_glyph_codepoint_map{glyphs->info[i].codepoint, i}; + if(glyphs->info[i].codepoint == 0xFFFFFFFF) + { + // When the cache is mostly empty, this makes the sorting way faster. + nonempty_count = i; + break; + } + } + qsort(glyphs->sorted_indices, nonempty_count, sizeof(gui_glyph_codepoint_map), u32_cmp); + + return glyphs; +} diff --git a/code/gui/text_draw.h b/code/gui/text_draw.h new file mode 100644 index 0000000..d120747 --- /dev/null +++ b/code/gui/text_draw.h @@ -0,0 +1,18 @@ +#ifndef _PIUMA_GUI_TEXT_DRAW_H_ +#define _PIUMA_GUI_TEXT_DRAW_H_ + +#include "../lib/types.h" +#include "../lib/math.h" +#include "../lib/geometry.h" +#include "../lib/text.h" + +bool gui_text_draw_init(); +void gui_text_draw_deinit(); + +v2 gui_text_draw_size(const char *text, f32 font_size, v2 *cursor_position = NULL); +void gui_text_draw(Rect r, const char *text, f32 font_size, v4 color); +v2 gui_utf8_text_draw_size(const utf8_codepoint *text, u64 length, f32 font_size, v2 *cursor_position = NULL); +void gui_utf8_text_draw(Rect r, const utf8_codepoint *text, u64 length, f32 font_size, v4 color); + + +#endif diff --git a/code/lib/all.h b/code/lib/all.h new file mode 100644 index 0000000..96b317f --- /dev/null +++ b/code/lib/all.h @@ -0,0 +1,12 @@ +#ifndef _PIUMA_LIB_ALL_H_ +#define _PIUMA_LIB_ALL_H_ + +#include "types.h" +#include "ds.h" +#include "queue.h" +#include "math.h" +#include "geometry.h" +#include "color.h" +#include "text.h" + +#endif diff --git a/code/lib/bits.h b/code/lib/bits.h new file mode 100644 index 0000000..40a9b3c --- /dev/null +++ b/code/lib/bits.h @@ -0,0 +1,26 @@ +#ifndef _PIUMA_LIB_BITS_H_ +#define _PIUMA_LIB_BITS_H_ + +#include "types.h" + +inline u32 bits_endian_swap(u32 x) +{ + return + (x << 24 ) | + (x << 8 & 0x00FF0000) | + (x >> 8 & 0x0000FF00) | + (x >> 24 ); +} + +inline u32 bits_reverse(u32 x) +{ + u32 result = 0; + for(int i = 0; i < 32; i++) + { + result = result << 1 | (x & 1); + x = x >> 1; + } + return result; +} + +#endif diff --git a/code/lib/color.h b/code/lib/color.h new file mode 100644 index 0000000..c0482ba --- /dev/null +++ b/code/lib/color.h @@ -0,0 +1,25 @@ +#ifndef _PIUMA_LIB_COLOR_H_ +#define _PIUMA_LIB_COLOR_H_ + +#include "math.h" +#include "types.h" + +inline u32 convert_color_to_ABGR_u32(v4 color) +{ + u32 r = 0xFF * color.r; + u32 g = 0xFF * color.g; + u32 b = 0xFF * color.b; + u32 a = 0xFF * color.a; + return (a << 24) | (b << 16) | (g << 8) | (r); +} + +inline v4 convert_ABGR_u32_to_color(u32 color) +{ + f32 r = (f32)((color >> 0) & 0xFF) / (f32)(0xFF); + f32 g = (f32)((color >> 8) & 0xFF) / (f32)(0xFF); + f32 b = (f32)((color >> 16) & 0xFF) / (f32)(0xFF); + f32 a = (f32)((color >> 24) & 0xFF) / (f32)(0xFF); + return v4{r, g, b, a}; +} + +#endif diff --git a/code/lib/ds.cpp b/code/lib/ds.cpp new file mode 100644 index 0000000..733dcfb --- /dev/null +++ b/code/lib/ds.cpp @@ -0,0 +1,87 @@ +#include "ds.h" +#include + +int char_cmp(const void *a, const void *b) +{ + char _a = *(char*)a; + char _b = *(char*)b; + if(_a < _b) return -1; + if(_a > _b) return +1; + return 0; +} + +int u8_cmp (const void *a, const void *b) +{ + u8 _a = *(u8*)a; + u8 _b = *(u8*)b; + if(_a < _b) return -1; + if(_a > _b) return +1; + return 0; +} + +int u32_cmp(const void *a, const void *b) +{ + u32 _a = *(u32*)a; + u32 _b = *(u32*)b; + if(_a < _b) return -1; + if(_a > _b) return +1; + return 0; +} + +int u64_cmp(const void *a, const void *b) +{ + u64 _a = *(u64*)a; + u64 _b = *(u64*)b; + if(_a < _b) return -1; + if(_a > _b) return +1; + return 0; +} + +int s32_cmp(const void *a, const void *b) +{ + s32 _a = *(s32*)a; + s32 _b = *(s32*)b; + if(_a < _b) return -1; + if(_a > _b) return +1; + return 0; +} + +int s64_cmp(const void *a, const void *b) +{ + s64 _a = *(s64*)a; + s64 _b = *(s64*)b; + if(_a < _b) return -1; + if(_a > _b) return +1; + return 0; +} + + + +u64 make_unique(void *array, u64 count, u64 element_size, compare_fn *cmp) +{ + qsort(array, count, element_size, cmp); + + // Remove duplicates + u8 *start = (u8*)array; + u8 *end = start + element_size * count; + + u64 deleted = 0; + + u8 *prev = start; + u8 *curr = start + element_size; + while(curr < end) + { + if(cmp(prev, curr) == 0) + { + deleted++; + } + else + { + prev += element_size; + memcpy(prev, curr, element_size); + } + curr += element_size; + } + + return count - deleted; +} diff --git a/code/lib/ds.h b/code/lib/ds.h new file mode 100644 index 0000000..77c99a6 --- /dev/null +++ b/code/lib/ds.h @@ -0,0 +1,29 @@ +#ifndef _PIUMA_LIB_DS_H_ +#define _PIUMA_LIB_DS_H_ + +#include "types.h" +#include "stdlib.h" + +typedef int compare_fn(const void *, const void *); + +// Basic compare functions +int char_cmp(const void *a, const void *b); +int u8_cmp (const void *a, const void *b); +int u32_cmp(const void *a, const void *b); +int u64_cmp(const void *a, const void *b); +int s32_cmp(const void *a, const void *b); +int s64_cmp(const void *a, const void *b); + +// Modifies "array" and returns the number of unique things +u64 make_unique(void *array, u64 count, u64 element_size, compare_fn *cmp); + +// @Cleanup: put this in the right place +template +void swap(T &a, T &b) +{ + T tmp = a; + a = b; + b = tmp; +} + +#endif diff --git a/code/lib/event.h b/code/lib/event.h new file mode 100644 index 0000000..6bb5f05 --- /dev/null +++ b/code/lib/event.h @@ -0,0 +1,220 @@ +#ifndef _PIUMA_LIB_EVENT_H_ +#define _PIUMA_LIB_EVENT_H_ + +#include "types.h" +#include "math.h" + +// Mouse events +// Keyboard events +// Window events (resize, hide, focus) +// Quit +// Other OS events (signals?) + + +enum Event_Type +{ + EVENT_NONE, + + EVENT_QUIT, + + EVENT_MOUSE_MOVE, + EVENT_KEY, + + EVENT_RESIZE, + EVENT_FOCUS, + EVENT_UNFOCUS, + + EVENT_TEXT, + + EVENT_COUNT +}; + + + +// @Performance: a lot of this codes are sequential in Linux. Check with other OSs if that's the case. If it is, we can have faster mapping by subtracting and adding an offset. +enum Key_Code +{ + KEY_UNKNOWN = 0, + + // @Correctness: check all ascii characters that are on a keyboard + KEY_ENTER = '\r', + KEY_ESCAPE = '\e', + KEY_BACKSPACE = '\b', + KEY_TAB = '\t', + KEY_SPACE = ' ', + KEY_EXCLAMATION = '!', + KEY_DOUBLE_QUOTE = '"', + KEY_HASH = '#', + KEY_PERCENT = '%', + KEY_DOLLAR = '$', + KEY_AMPERSAND = '&', + KEY_SINGLE_QUOTE = '\'', + KEY_LEFT_PARENTHESIS = '(', + KEY_RIGHT_PARENTHESIS = ')', + KEY_ASTERISK = '*', + KEY_PLUS = '+', + KEY_COMMA = ',', + KEY_MINUS = '-', + KEY_PERIOD = '.', + KEY_SLASH = '/', + KEY_0 = '0', + KEY_1 = '1', + KEY_2 = '2', + KEY_3 = '3', + KEY_4 = '4', + KEY_5 = '5', + KEY_6 = '6', + KEY_7 = '7', + KEY_8 = '8', + KEY_9 = '9', + KEY_COLON = ':', + KEY_SEMICOLON = ';', + KEY_LESS = '<', + KEY_EQUALS = '=', + KEY_GREATER = '>', + KEY_QUESTION = '?', + KEY_AT = '@', + KEY_LEFT_BRACKET = '[', + KEY_RIGHT_BRACKET = ']', + KEY_BACKSLASH = '\\', + KEY_CARET = '^', + KEY_UNDERSCORE = '_', + KEY_BACKQUOTE = '`', + KEY_A = 'A', + KEY_B = 'B', + KEY_C = 'C', + KEY_D = 'D', + KEY_E = 'E', + KEY_F = 'F', + KEY_G = 'G', + KEY_H = 'H', + KEY_I = 'I', + KEY_J = 'J', + KEY_K = 'K', + KEY_L = 'L', + KEY_M = 'M', + KEY_N = 'N', + KEY_O = 'O', + KEY_P = 'P', + KEY_Q = 'Q', + KEY_R = 'R', + KEY_S = 'S', + KEY_T = 'T', + KEY_U = 'U', + KEY_V = 'V', + KEY_W = 'W', + KEY_X = 'X', + KEY_Y = 'Y', + KEY_Z = 'Z', + + KEY_CAPSLOCK = 1000, + KEY_F1, + KEY_F2, + KEY_F3, + KEY_F4, + KEY_F5, + KEY_F6, + KEY_F7, + KEY_F8, + KEY_F9, + KEY_F10, + KEY_F11, + KEY_F12, + + KEY_PRINTSCREEN, + KEY_SCROLLLOCK, + KEY_PAUSE, + KEY_INSERT, + KEY_DELETE, + + KEY_HOME, + KEY_END, + KEY_PAGEUP, + KEY_PAGEDOWN, + + KEY_ARROW_UP, + KEY_ARROW_DOWN, + KEY_ARROW_LEFT, + KEY_ARROW_RIGHT, + + KEY_NUMLOCK, + KEY_PAD_DIVIDE, + KEY_PAD_MULTIPLY, + KEY_PAD_MINUS, + KEY_PAD_PLUS, + KEY_PAD_ENTER, + KEY_PAD_1, + KEY_PAD_2, + KEY_PAD_3, + KEY_PAD_4, + KEY_PAD_5, + KEY_PAD_6, + KEY_PAD_7, + KEY_PAD_8, + KEY_PAD_9, + KEY_PAD_0, + KEY_PAD_PERIOD, + + KEY_LEFT_CTRL, + KEY_RIGHT_CTRL, + KEY_LEFT_SHIFT, + KEY_RIGHT_SHIFT, + KEY_LEFT_ALT, + KEY_RIGHT_ALT, + + KEY_MOUSE_LEFT, + KEY_MOUSE_MIDDLE, + KEY_MOUSE_RIGHT, + KEY_MOUSE_WHEEL_UP, + KEY_MOUSE_WHEEL_DOWN, + KEY_MOUSE_4, + KEY_MOUSE_5, + + // @Correctness: check for more keys in: USB spec, linux libinput/x11/wayland/whatever, windows win32 +}; + +struct Event_Key +{ + bool pressed; + Key_Code key_code; +}; + + +struct Event_Mouse_Move +{ + bool relative; + union + { + v2 position; + v2 delta; + }; +}; + + +struct Event_Resize +{ + s32 width, height; +}; + + +struct Event_Text +{ + char data[16]; // @Correctness: We have a bug if the os sends text that is longer +}; + + +struct Event +{ + Event_Type type; + + union + { + Event_Key key; + Event_Mouse_Move mouse_move; + Event_Resize resize; + Event_Text text; + }; +}; + + +#endif diff --git a/code/lib/geometry.h b/code/lib/geometry.h new file mode 100644 index 0000000..4315ad2 --- /dev/null +++ b/code/lib/geometry.h @@ -0,0 +1,319 @@ +#ifndef _PIUMA_LIB_GEOMETRY_H_ +#define _PIUMA_LIB_GEOMETRY_H_ + +#include "types.h" +#include "math.h" + +// Rect +union Rect +{ + struct + { + f32 x, y; + f32 w, h; + }; + struct + { + v2 position; // Usually, the top-left corner of the rectangle + v2 size; + }; +}; + +inline bool is_inside(Rect rect, v2 p) +{ + bool in_range_x = rect.x <= p.x && p.x <= (rect.x + rect.w); + bool in_range_y = rect.y <= p.y && p.y <= (rect.y + rect.h); + return in_range_x && in_range_y; +} + +// @Feature: add Cube/Parallelepiped? Unlike Box, it has rotations +// Box +struct Box +{ + v3 min; + v3 max; +}; + +inline bool is_inside(Box b, v3 p) +{ + return + (p.x < b.min.x || p.x > b.max.x) || + (p.y < b.min.y || p.y > b.max.y) || + (p.z < b.min.z || p.z > b.max.z); +} + +inline bool overlaps(Box a, Box b) +{ + if(a.min.x > b.max.x) // no overlap + return false; + if(a.max.x < b.min.x) // no overlap + return false; + if(a.min.y > b.max.y) // no overlap + return false; + if(a.max.y < b.min.y) // no overlap + return false; + if(a.min.z > b.max.z) // no overlap + return false; + if(a.max.z < b.min.z) // no overlap + return false; + return true; +} + +inline Box box_from_point_cloud(v3 *points, u32 count) +{ + if(count == 0) + return Box{.min = {0,0,0}, .max = {0,0,0}}; + + Box box; + box.min = points[0]; + box.max = points[0]; + for(u32 i = 0; i < count; i++) + { + v3 p = points[i]; + box.min.x = minimum(box.min.x, p.x); + box.min.y = minimum(box.min.y, p.y); + box.min.z = minimum(box.min.z, p.z); + box.max.x = maximum(box.max.x, p.x); + box.max.y = maximum(box.max.y, p.y); + box.max.z = maximum(box.max.z, p.z); + } + return box; +} + +inline v3 box_closest_point(Box b, v3 point); +inline f32 box_SDF(Box b, v3 point, v3* res_closest = NULL) +{ + v3 closest = box_closest_point(b, point); + f32 sign = -1 * is_inside(b, point); + + if(res_closest) + *res_closest = closest; + return sign * distance(closest, point); +} + + +// Ray +struct Ray +{ + v3 position; + v3 direction; +}; + + +// Circle +// @Cleanup: Should Circle be merged with Sphere? +struct Circle +{ + v2 center; + f32 radius; +}; + +inline bool is_inside(Circle c, v2 p) +{ + v2 v = p - c.center; + return dot(v, v) <= square(c.radius); +} + +// Sphere +struct Sphere +{ + v3 center; + f32 radius; +}; + +inline bool is_inside(Sphere s, v3 p) +{ + v3 v = p - s.center; + return dot(v, v) <= square(s.radius); // distance² <= radius² +} + +inline f32 sphere_SDF(Sphere s, v3 point) +{ + return distance(s.center, point) - s.radius; +} + + +// Segment +struct Segment +{ + v3 a; + v3 b; +}; + + +// Plane +struct Plane +{ + v3 normal; + f32 offset; +}; + +inline f32 plane_SDF(Plane plane, v3 point) +{ + f32 projected = dot(point, plane.normal); + return plane.offset - projected; +} + + + +// Projections +inline f32 project_point_on_vector(v3 vector, v3 point) +{ + return dot(vector, point); +} + +inline v3 project_point_on_plane(Plane plane, v3 point) +{ + f32 distance = plane_SDF(plane, point); + return point - plane.normal * distance; +} + + +// Closest point +inline v3 segment_closest_point(Segment seg, v3 point) +{ + v3 ab = seg.b - seg.a; + v3 ap = point - seg.a; + f32 u = dot(ab, ap); + return seg.a + ab * clamp(0.0, 1.0, u); +} + +inline v3 box_closest_point(Box b, v3 point) +{ + v3 closest; + // Closest point on the box is the one with coords closer to the ones of the point, + // so for each axis we can clamp to the nearest point of the border. + + f32 dx1 = abs(b.min.x - point.x); + f32 dx2 = abs(b.max.x - point.x); + closest.x = (dx1 < dx2) ? b.min.x : b.max.x; + f32 dy1 = abs(b.min.y - point.y); + f32 dy2 = abs(b.max.y - point.y); + closest.y = (dy1 < dy2) ? b.min.y : b.max.y; + f32 dz1 = abs(b.min.z - point.z); + f32 dz2 = abs(b.max.z - point.z); + closest.z = (dz1 < dz2) ? b.min.z : b.max.z; + + return closest; +} + + + +// Triangles functions +inline v3 triangle_normal(v3 a, v3 b, v3 c) +{ + v3 ba = b - a; + v3 ca = c - a; + v3 orthogonal = cross(ba, ca); + return normalize(orthogonal); +} + + +// Transformations (all angles in radians) +inline m4 rotation_x(f32 angle) +{ + f32 c = cos(angle); + f32 s = sin(angle); + + m4 result = m4_identity; + result.E[1][1] = c; + result.E[1][2] = -s; + result.E[2][1] = s; + result.E[2][2] = c; + + return result; +} + +inline m4 rotation_y(f32 angle) +{ + f32 c = cos(angle); + f32 s = sin(angle); + + m4 result = m4_identity; + result.E[0][0] = c; + result.E[0][2] = s; + result.E[2][0] = -s; + result.E[2][2] = c; + + return result; +} + +inline m4 rotation_z(f32 angle) +{ + f32 c = cos(angle); + f32 s = sin(angle); + + m4 result = m4_identity; + result.E[0][0] = c; + result.E[0][1] = -s; + result.E[1][0] = s; + result.E[1][1] = c; + + return result; +} + +inline m4 rotation(f32 x, f32 y, f32 z) +{ + return rotation_z(z) * rotation_y(y) * rotation_x(x); +} + +inline m4 rotation_v3(v3 angle) +{ + return rotation(angle.x, angle.y, angle.z); +} + + +inline m4 translation(f32 x, f32 y, f32 z) +{ + m4 result = m4_identity; + + result.E[0][3] = x; + result.E[1][3] = y; + result.E[2][3] = z; + + return result; +} + +inline m4 translation_v3(v3 t) +{ + return translation(t.x, t.y, t.z); +} + +inline m4 scale(f32 x, f32 y, f32 z) +{ + m4 result = m4_zero; + + result.E[0][0] = x; + result.E[1][1] = y; + result.E[2][2] = z; + result.E[3][3] = 1.0; + + return result; +} + +inline m4 scale_v3(v3 factor) +{ + return scale(factor.x, factor.y, factor.z); +} + +inline m4 scale(f32 factor) +{ + return scale(factor, factor, factor); +} + + + + +// Primitives +// Pass array of 8 elements to fill with coordinates +inline void build_cube_vertices(v3 *vertices) +{ + for(int x = 0; x < 2; x++) + for(int y = 0; y < 2; y++) + for(int z = 0; z < 2; z++) + vertices[x*2*2 + y*2 + z] = v3{.x = x - 0.5f, .y = y - 0.5f, .z = z - 0.5f}; +} + + + +#endif diff --git a/code/lib/hashing.h b/code/lib/hashing.h new file mode 100644 index 0000000..cd00839 --- /dev/null +++ b/code/lib/hashing.h @@ -0,0 +1,97 @@ +#ifndef _PIUMA_LIB_HASHING_H_ +#define _PIUMA_LIB_HASHING_H_ + +//#include "bits.h" + +static const u32 hash_crc32_table[256] = { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; + +/* +// Code to compute the lookup table for CRC32 +// table needs 256 spaces +static const u32 hash_crc32_polynomial = 0x04C11DB7; +inline void hash_crc32_make_table(u32 *table) +{ + u32 polynomial = bits_reverse(hash_crc32_polynomial); + for(u32 byte = 0; byte <= 0xFF; byte++) + { + u32 crc = byte; + for(u8 bit = 0; bit < 8; bit++) + { + if(crc & 1) + crc = (crc >> 1) ^ polynomial; + else + crc = (crc >> 1); + } + table[byte] = crc; + } +} +*/ + +inline u32 hash_crc32(const void *data, u64 size, u32 seed = 0xFFFFFFFF) +{ + u32 crc = seed; + u8 *d = (u8*)data; + while(size--) + { + crc = hash_crc32_table[(crc & 0xFF) ^ *d] ^ (crc >> 8); + d++; + } + return ~crc; +} + +/* +// Simpler code to compute CRC32, one bit at a time. +// The version with the lookup table computes 8 bit at a time. +inline u32 hash_crc32(const void *data, u64 size, u32 seed = 0xFFFFFFFF) +{ + u8 *d = (u8*)data; + u32 crc = seed; + while(size--) + { + crc = crc ^ (*d++ << 24); + for(int i = 0; i < 8; i++) + { + if(crc & (1L<<31)) + crc = (crc << 1) ^ hash_crc32_polynomial; + else + crc = (crc << 1); + } + } + return bits_endian_swap(~crc); +} +*/ + +#endif diff --git a/code/lib/math.h b/code/lib/math.h new file mode 100644 index 0000000..dffb0fe --- /dev/null +++ b/code/lib/math.h @@ -0,0 +1,1432 @@ +#ifndef _PIUMA_LIB_MATH_H_ +#define _PIUMA_LIB_MATH_H_ + +#include "types.h" +#include + +// Vector types +union v2 +{ + struct { f32 x, y; }; + struct { f32 r, g; }; + struct { f32 u, v; }; + struct { f32 E[2]; }; +}; + +union v2s +{ + struct { s32 x, y; }; + struct { s32 r, g; }; + struct { s32 u, v; }; + struct { s32 E[2]; }; +}; + +union v2u +{ + struct { u32 x, y; }; + struct { u32 r, g; }; + struct { u32 u, v; }; + struct { u32 E[2]; }; +}; + + +union v3 +{ + struct { f32 x, y, z; }; + struct { f32 r, g, b; }; + struct { f32 u, v, w_; }; + struct { f32 E[3]; }; + + + struct + { + v2 xy; + f32 ignored0; + }; + struct + { + f32 ignored1; + v2 yz; + }; + struct + { + v2 uv; + f32 ignored2; + }; + struct + { + f32 ignored3; + v2 vw_; + }; + struct + { + f32 pitch; + f32 yaw; + f32 roll; + }; +}; + +union v3s +{ + struct { s32 x, y, z; }; + struct { s32 r, g, b; }; + struct { s32 u, v, w_; }; + struct { s32 E[3]; }; + + + struct + { + v2s xy; + s32 ignored0; + }; + struct + { + s32 ignored1; + v2s yz; + }; + struct + { + v2s uv; + s32 ignored2; + }; + struct + { + s32 ignored3; + v2s vw_; + }; + struct + { + s32 pitch; + s32 yaw; + s32 roll; + }; +}; + +union v3u +{ + struct { u32 x, y, z; }; + struct { u32 r, g, b; }; + struct { u32 u, v, w_; }; + struct { u32 E[3]; }; + + + struct + { + v2u xy; + u32 ignored0; + }; + struct + { + u32 ignored1; + v2u yz; + }; + struct + { + v2u uv; + u32 ignored2; + }; + struct + { + u32 ignored3; + v2u vw_; + }; + struct + { + u32 pitch; + u32 yaw; + u32 roll; + }; +}; + + +union v4 +{ + struct { f32 x, y, z, w; }; + struct { f32 r, g, b, a; }; + struct { f32 E[4]; }; + + struct + { + v2 xy; + f32 ignored0; + f32 ignored1; + }; + struct + { + f32 ignored2; + v2 yz; + f32 ignored3; + }; + struct + { + f32 ignored4; + f32 ignored5; + v2 zw; + }; + struct + { + v2 uv; + f32 ignored6; + f32 ignored7; + }; + struct + { + v3 xyz; + f32 ignored8; + }; + struct + { + v3 rgb; + f32 ignored9; + }; +}; + +union v4s +{ + struct { s32 x, y, z, w; }; + struct { s32 r, g, b, a; }; + struct { s32 E[4]; }; + + struct + { + v2s xy; + s32 ignored0; + s32 ignored1; + }; + struct + { + s32 ignored2; + v2s yz; + s32 ignored3; + }; + struct + { + s32 ignored4; + s32 ignored5; + v2s zw; + }; + struct + { + v2s uv; + s32 ignored6; + s32 ignored7; + }; + struct + { + v3s xyz; + s32 ignored8; + }; + struct + { + v3s rgb; + s32 ignored9; + }; +}; + +union v4u +{ + struct { u32 x, y, z, w; }; + struct { u32 r, g, b, a; }; + struct { u32 E[4]; }; + + struct + { + v2u xy; + u32 ignored0; + u32 ignored1; + }; + struct + { + u32 ignored2; + v2u yz; + u32 ignored3; + }; + struct + { + u32 ignored4; + u32 ignored5; + v2u zw; + }; + struct + { + v2u uv; + u32 ignored6; + u32 ignored7; + }; + struct + { + v3u xyz; + u32 ignored8; + }; + struct + { + v3u rgb; + u32 ignored9; + }; +}; + +// Matrix types +union m3x3 +{ + struct { v3 row[3]; }; + struct { f32 E[3][3]; }; +}; +typedef m3x3 m3; + +union m4x4 +{ + struct { v4 row[4]; }; + struct { f32 E[4][4]; }; +}; +typedef m4x4 m4; + + +static const m3 m3_zero = +{ + 0, 0, 0, + 0, 0, 0, + 0, 0, 0 +}; +static const m3 m3_identity = +{ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 +}; +static const m4 m4_zero = +{ + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +static const m4 m4_identity = +{ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 +}; + + +// Angles +static const f32 PI = 3.141592653589793238462643383; +static const f32 TAU = 6.283185307179586476925286766; + +#define radians(x) (x*TAU/360) +#define degrees(x) (x*360/TAU) + + +// Utilities +#define square(x) ((x)*(x)) +#define minimum(a, b) ((a) < (b) ? (a) : (b)) +#define maximum(a, b) ((a) > (b) ? (a) : (b)) + + +inline f32 clamp(f32 min, f32 max, f32 value) +{ + if(value < min) + return min; + if(value > max) + return max; + return value; +} + +inline f32 lerp(f32 start, f32 end, f32 u) +{ + //return (1 - u) * start + u * end; + return start + (end - start) * u; +} + +inline v3 lerp(v3 start, v3 end, f32 u) +{ + f32 x = lerp(start.x, end.x, u); + f32 y = lerp(start.y, end.y, u); + f32 z = lerp(start.z, end.z, u); + return v3{x, y, z}; +} + +inline v4 lerp(v4 start, v4 end, f32 u) +{ + f32 x = lerp(start.x, end.x, u); + f32 y = lerp(start.y, end.y, u); + f32 z = lerp(start.z, end.z, u); + f32 w = lerp(start.w, end.w, u); + return v4{x, y, z, w}; +} + +inline f32 map(f32 new_start, f32 new_end, f32 old_start, f32 old_end, f32 u) +{ + f32 old_range = old_end - old_start; + f32 new_range = new_end - new_start; + return (u - old_start) / old_range * new_range + new_start; +} + + +// Conversions +inline v2 V2(v2s a) +{ + return {(f32)a.x, (f32)a.y}; +} + +inline v2 V2(v2u a) +{ + return {(f32)a.x, (f32)a.y}; +} + +inline v2s V2S(v2 a) +{ + return {(s32)a.x, (s32)a.y}; +} + +inline v2s V2S(v2u a) +{ + return {(s32)a.x, (s32)a.y}; +} + +inline v2u V2U(v2 a) +{ + return {(u32)a.x, (u32)a.y}; +} + +inline v2u V2U(v2s a) +{ + return {(u32)a.x, (u32)a.y}; +} + +inline v3 V3(v2 a, f32 z) +{ + return {a.x, a.y, z}; +} + +inline v3 V3(v3s a) +{ + return {(f32)a.x, (f32)a.y, (f32)a.z}; +} + +inline v3 V3(v3u a) +{ + return {(f32)a.x, (f32)a.y, (f32)a.z}; +} + +inline v3 V3(f32 e[3]) +{ + return v3{e[0], e[1], e[2]}; +} + +inline v3s V3S(v2s a, s32 z) +{ + return {a.x, a.y, z}; +} + +inline v3s V3S(v3 a) +{ + return {(s32)a.x, (s32)a.y, (s32)a.z}; +} + +inline v3s V3S(v3u a) +{ + return {(s32)a.x, (s32)a.y, (s32)a.z}; +} + +inline v3u V3U(v2u a, u32 z) +{ + return {a.x, a.y, z}; +} + +inline v3u V3U(v3 a) +{ + return {(u32)a.x, (u32)a.y, (u32)a.z}; +} + +inline v3u V3U(v3s a) +{ + return {(u32)a.x, (u32)a.y, (u32)a.z}; +} + +inline v4 V4(v2 a, f32 z, f32 w) +{ + v4 result; + + result.xy = a; + result.z = z; + result.w = w; + + return result; +} + +inline v4 V4(v3 a, f32 w) +{ + v4 result; + + result.xyz = a; + result.w = w; + + return result; +} + +inline v4 V4(f32 e[4]) +{ + return v4{e[0], e[1], e[2], e[3]}; +} + +inline m3 M3(m4 m) +{ + return m3{ m.row[0].xyz, m.row[1].xyz, m.row[2].xyz }; +} + + +// Operators +inline v2 operator+(v2 a) +{ + return a; +} +inline v2 operator-(v2 a) +{ + v2 result = { -a.x, -a.y }; + return result; +} + +inline bool operator==(v2 a, v2 b) +{ + return a.x == b.x && a.y == b.y; +} + +inline bool operator!=(v2 a, v2 b) +{ + return !(a == b); +} + +inline v2 operator+(v2 a, v2 b) +{ + v2 result; + + result.x = a.x + b.x; + result.y = a.y + b.y; + + return result; +} +inline v2 operator-(v2 a, v2 b) +{ + v2 result; + + result.x = a.x - b.x; + result.y = a.y - b.y; + + return result; +} +inline v2 operator*(v2 a, v2 b) +{ + v2 result; + + result.x = a.x * b.x; + result.y = a.y * b.y; + + return result; +} +inline v2 operator/(v2 a, v2 b) +{ + v2 result; + + result.x = a.x / b.x; + result.y = a.y / b.y; + + return result; +} +inline v2 operator*(v2 a, f32 b) +{ + v2 result; + + result.x = a.x * b; + result.y = a.y * b; + + return result; +} +inline v2 operator/(v2 a, f32 b) +{ + v2 result; + + result.x = a.x / b; + result.y = a.y / b; + + return result; +} +inline v2 operator*(f32 a, v2 b) +{ + v2 result; + + result.x = a * b.x; + result.y = a * b.y; + + return result; +} + +inline v2 & operator+=(v2 &a, v2 b) +{ + a = a + b; + return a; +} +inline v2 & operator-=(v2 &a, v2 b) +{ + a = a - b; + return a; +} +inline v2 & operator*=(v2 &a, v2 b) +{ + a = a * b; + return a; +} +inline v2 & operator/=(v2 &a, v2 b) +{ + a = a / b; + return a; +} +inline v2 & operator*=(v2 &a, f32 b) +{ + a = a * b; + return a; +} +inline v2 & operator/=(v2 &a, f32 b) +{ + a = a / b; + return a; +} + +inline v2s operator+(v2s a) +{ + return a; +} +inline v2s operator-(v2s a) +{ + v2s result = { -a.x, -a.y }; + return result; +} + +inline bool operator==(v2s a, v2s b) +{ + return a.x == b.x && a.y == b.y; +} + +inline bool operator!=(v2s a, v2s b) +{ + return !(a == b); +} + +inline v2s operator+(v2s a, v2s b) +{ + v2s result; + + result.x = a.x + b.x; + result.y = a.y + b.y; + + return result; +} +inline v2s operator-(v2s a, v2s b) +{ + v2s result; + + result.x = a.x - b.x; + result.y = a.y - b.y; + + return result; +} +inline v2s operator*(v2s a, v2s b) +{ + v2s result; + + result.x = a.x * b.x; + result.y = a.y * b.y; + + return result; +} +inline v2s operator/(v2s a, v2s b) +{ + v2s result; + + result.x = a.x / b.x; + result.y = a.y / b.y; + + return result; +} +inline v2s operator*(v2s a, f32 b) +{ + v2s result; + + result.x = a.x * b; + result.y = a.y * b; + + return result; +} +inline v2s operator/(v2s a, f32 b) +{ + v2s result; + + result.x = a.x / b; + result.y = a.y / b; + + return result; +} +inline v2s operator*(f32 a, v2s b) +{ + v2s result; + + result.x = a * b.x; + result.y = a * b.y; + + return result; +} + +inline v2s & operator+=(v2s &a, v2s b) +{ + a = a + b; + return a; +} +inline v2s & operator-=(v2s &a, v2s b) +{ + a = a - b; + return a; +} +inline v2s & operator*=(v2s &a, v2s b) +{ + a = a * b; + return a; +} +inline v2s & operator/=(v2s &a, v2s b) +{ + a = a / b; + return a; +} +inline v2s & operator*=(v2s &a, f32 b) +{ + a = a * b; + return a; +} +inline v2s & operator/=(v2s &a, f32 b) +{ + a = a / b; + return a; +} + +inline v2u operator+(v2u a) +{ + return a; +} +inline v2u operator-(v2u a) +{ + v2u result = { -a.x, -a.y }; + return result; +} + +inline bool operator==(v2u a, v2u b) +{ + return a.x == b.x && a.y == b.y; +} + +inline bool operator!=(v2u a, v2u b) +{ + return !(a == b); +} + +inline v2u operator+(v2u a, v2u b) +{ + v2u result; + + result.x = a.x + b.x; + result.y = a.y + b.y; + + return result; +} +inline v2u operator-(v2u a, v2u b) +{ + v2u result; + + result.x = a.x - b.x; + result.y = a.y - b.y; + + return result; +} +inline v2u operator*(v2u a, v2u b) +{ + v2u result; + + result.x = a.x * b.x; + result.y = a.y * b.y; + + return result; +} +inline v2u operator/(v2u a, v2u b) +{ + v2u result; + + result.x = a.x / b.x; + result.y = a.y / b.y; + + return result; +} +inline v2u operator*(v2u a, f32 b) +{ + v2u result; + + result.x = a.x * b; + result.y = a.y * b; + + return result; +} +inline v2u operator/(v2u a, f32 b) +{ + v2u result; + + result.x = a.x / b; + result.y = a.y / b; + + return result; +} +inline v2u operator*(f32 a, v2u b) +{ + v2u result; + + result.x = a * b.x; + result.y = a * b.y; + + return result; +} + +inline v2u & operator+=(v2u &a, v2u b) +{ + a = a + b; + return a; +} +inline v2u & operator-=(v2u &a, v2u b) +{ + a = a - b; + return a; +} +inline v2u & operator*=(v2u &a, v2u b) +{ + a = a * b; + return a; +} +inline v2u & operator/=(v2u &a, v2u b) +{ + a = a / b; + return a; +} +inline v2u & operator*=(v2u &a, f32 b) +{ + a = a * b; + return a; +} +inline v2u & operator/=(v2u &a, f32 b) +{ + a = a / b; + return a; +} + + + +inline v3 operator+(v3 a) +{ + return a; +} +inline v3 operator-(v3 a) +{ + v3 result = { -a.x, -a.y, -a.z }; + return result; +} + +inline bool operator==(v3 a, v3 b) +{ + return a.x == b.x && a.y == b.y && a.z == b.z; +} + +inline bool operator!=(v3 a, v3 b) +{ + return !(a == b); +} + +inline v3 operator+(v3 a, v3 b) +{ + v3 result; + + result.x = a.x + b.x; + result.y = a.y + b.y; + result.z = a.z + b.z; + + return result; +} +inline v3 operator-(v3 a, v3 b) +{ + v3 result; + + result.x = a.x - b.x; + result.y = a.y - b.y; + result.z = a.z - b.z; + + return result; +} +inline v3 operator*(v3 a, v3 b) +{ + v3 result; + + result.x = a.x * b.x; + result.y = a.y * b.y; + result.z = a.z * b.z; + + return result; +} +inline v3 operator/(v3 a, v3 b) +{ + v3 result; + + result.x = a.x / b.x; + result.y = a.y / b.y; + result.z = a.z / b.z; + + return result; +} +inline v3 operator*(v3 a, f32 b) +{ + v3 result; + + result.x = a.x * b; + result.y = a.y * b; + result.z = a.z * b; + + return result; +} +inline v3 operator/(v3 a, f32 b) +{ + v3 result; + + result.x = a.x / b; + result.y = a.y / b; + result.z = a.z / b; + + return result; +} +inline v3 operator*(f32 a, v3 b) +{ + v3 result; + + result.x = a * b.x; + result.y = a * b.y; + result.z = a * b.z; + + return result; +} + +inline v3 & operator+=(v3 &a, v3 b) +{ + a = a + b; + return a; +} +inline v3 & operator-=(v3 &a, v3 b) +{ + a = a - b; + return a; +} +inline v3 & operator*=(v3 &a, v3 b) +{ + a = a * b; + return a; +} +inline v3 & operator/=(v3 &a, v3 b) +{ + a = a / b; + return a; +} +inline v3 & operator*=(v3 &a, f32 b) +{ + a = a * b; + return a; +} +inline v3 & operator/=(v3 &a, f32 b) +{ + a = a / b; + return a; +} + + + +inline v4 operator+(v4 a) +{ + return a; +} +inline v4 operator-(v4 a) +{ + v4 result = { -a.x, -a.y, -a.z, -a.w }; + return result; +} + +inline bool operator==(v4 a, v4 b) +{ + return a.x == b.x && a.y == b.y && a.z == b.z && a.w == b.w; +} + +inline bool operator!=(v4 a, v4 b) +{ + return !(a == b); +} + +inline v4 operator+(v4 a, v4 b) +{ + v4 result; + + result.x = a.x + b.x; + result.y = a.y + b.y; + result.z = a.z + b.z; + result.w = a.w + b.w; + + return result; +} +inline v4 operator-(v4 a, v4 b) +{ + v4 result; + + result.x = a.x - b.x; + result.y = a.y - b.y; + result.z = a.z - b.z; + result.w = a.w - b.w; + + return result; +} +inline v4 operator*(v4 a, v4 b) +{ + v4 result; + + result.x = a.x * b.x; + result.y = a.y * b.y; + result.z = a.z * b.z; + result.w = a.w * b.w; + + return result; +} +inline v4 operator/(v4 a, v4 b) +{ + v4 result; + + result.x = a.x / b.x; + result.y = a.y / b.y; + result.z = a.z / b.z; + result.w = a.w / b.w; + + return result; +} +inline v4 operator*(v4 a, f32 b) +{ + v4 result; + + result.x = a.x * b; + result.y = a.y * b; + result.z = a.z * b; + result.w = a.w * b; + + return result; +} +inline v4 operator/(v4 a, f32 b) +{ + v4 result; + + result.x = a.x / b; + result.y = a.y / b; + result.z = a.z / b; + result.w = a.w / b; + + return result; +} +inline v4 operator*(f32 a, v4 b) +{ + v4 result; + + result.x = a * b.x; + result.y = a * b.y; + result.z = a * b.z; + result.w = a * b.w; + + return result; +} + +inline v4 & operator+=(v4 &a, v4 b) +{ + a = a + b; + return a; +} +inline v4 & operator-=(v4 &a, v4 b) +{ + a = a - b; + return a; +} +inline v4 & operator*=(v4 &a, v4 b) +{ + a = a * b; + return a; +} +inline v4 & operator/=(v4 &a, v4 b) +{ + a = a / b; + return a; +} +inline v4 & operator*=(v4 &a, f32 b) +{ + a = a * b; + return a; +} +inline v4 & operator/=(v4 &a, f32 b) +{ + a = a / b; + return a; +} + + +// Vector functions +inline f32 length(v2 a) +{ + return sqrt(square(a.x) + square(a.y)); +} + +inline f32 length(v3 a) +{ + return sqrt(square(a.x) + square(a.y) + square(a.z)); +} + +inline f32 length(v4 a) +{ + return sqrt(square(a.x) + square(a.y) + square(a.z) + square(a.w)); +} + + +inline f32 distance(v2 a, v2 b) +{ + return length(a - b); +} + +inline f32 distance(v3 a, v3 b) +{ + return length(a - b); +} + +inline f32 distance(v4 a, v4 b) +{ + return length(a - b); +} + + +inline f32 dot(v2 a, v2 b) +{ + return a.x*b.x + a.y*b.y; +} + +inline f32 dot(v3 a, v3 b) +{ + return a.x*b.x + a.y*b.y + a.z*b.z; +} + +inline f32 dot(v4 a, v4 b) +{ + return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; +} + +inline v3 cross(v3 a, v3 b) +{ + v3 result; + + result.x = a.y*b.z - a.z*b.y; + result.y = a.z*b.x - a.x*b.z; + result.z = a.x*b.y - a.y*b.x; + + return result; +} + + +inline v2 normalize(v2 a) +{ + return a / length(a); +} + +inline v3 normalize(v3 a) +{ + return a / length(a); +} + +inline v4 normalize(v4 a) +{ + return a / length(a); +} + + +// Matrix operators +inline v3 extract_column(m3 m, u8 column_index) +{ + v3 result; + + result.x = m.E[0][column_index]; + result.y = m.E[1][column_index]; + result.z = m.E[2][column_index]; + + return result; +} + +inline v4 extract_column(m4 m, u8 column_index) +{ + v4 result; + + result.x = m.E[0][column_index]; + result.y = m.E[1][column_index]; + result.z = m.E[2][column_index]; + result.w = m.E[3][column_index]; + + return result; +} + +inline v3 operator*(m3 m, v3 v) +{ + v3 result; + + result.x = dot(m.row[0], v); + result.y = dot(m.row[1], v); + result.z = dot(m.row[2], v); + + return result; +} + +inline m3 operator*(m3 a, m3 b) +{ + m3 result; + + v3 c0 = extract_column(b, 0); + v3 c1 = extract_column(b, 1); + v3 c2 = extract_column(b, 2); + + result.E[0][0] = dot(a.row[0], c0); + result.E[0][1] = dot(a.row[0], c1); + result.E[0][2] = dot(a.row[0], c2); + + result.E[1][0] = dot(a.row[1], c0); + result.E[1][1] = dot(a.row[1], c1); + result.E[1][2] = dot(a.row[1], c2); + + result.E[2][0] = dot(a.row[2], c0); + result.E[2][1] = dot(a.row[2], c1); + result.E[2][2] = dot(a.row[2], c2); + + result.E[3][0] = dot(a.row[3], c0); + result.E[3][1] = dot(a.row[3], c1); + result.E[3][2] = dot(a.row[3], c2); + + return result; +} + +inline v4 operator*(m4 m, v4 v) +{ + v4 result; + + result.x = dot(m.row[0], v); + result.y = dot(m.row[1], v); + result.z = dot(m.row[2], v); + result.w = dot(m.row[3], v); + + return result; +} + +inline m4 operator*(m4 a, m4 b) +{ + m4 result; + + v4 c0 = extract_column(b, 0); + v4 c1 = extract_column(b, 1); + v4 c2 = extract_column(b, 2); + v4 c3 = extract_column(b, 3); + + result.E[0][0] = dot(a.row[0], c0); + result.E[0][1] = dot(a.row[0], c1); + result.E[0][2] = dot(a.row[0], c2); + result.E[0][3] = dot(a.row[0], c3); + + result.E[1][0] = dot(a.row[1], c0); + result.E[1][1] = dot(a.row[1], c1); + result.E[1][2] = dot(a.row[1], c2); + result.E[1][3] = dot(a.row[1], c3); + + result.E[2][0] = dot(a.row[2], c0); + result.E[2][1] = dot(a.row[2], c1); + result.E[2][2] = dot(a.row[2], c2); + result.E[2][3] = dot(a.row[2], c3); + + result.E[3][0] = dot(a.row[3], c0); + result.E[3][1] = dot(a.row[3], c1); + result.E[3][2] = dot(a.row[3], c2); + result.E[3][3] = dot(a.row[3], c3); + + return result; +} + + +// Matrix functions +inline m3 m3_make_diagonal_matrix(f32 value) +{ + m3 result = m3_zero; + result.E[0][0] = value; + result.E[1][1] = value; + result.E[2][2] = value; + return result; +} + +inline m4 m4_make_diagonal_matrix(f32 value) +{ + m4 result = m4_zero; + result.E[0][0] = value; + result.E[1][1] = value; + result.E[2][2] = value; + result.E[3][3] = value; + return result; +} + +inline f32 determinant_of_minor(m3 m, int skip_r, int skip_c) +{ + f32 minor[2][2]; + for(int r = 0, source_r = 0; r < 2; r++, source_r++) + { + if(source_r == skip_r) + source_r++; + for(int c = 0, source_c = 0; c < 2; c++, source_c++) + { + if(source_c == skip_c) + source_c++; + minor[r][c] = m.E[source_r][source_c]; + } + } + + return minor[0][0] * minor[1][1] - minor[0][1] * minor[1][0]; +} + +inline f32 determinant(m3 m) +{ + return + + m.E[0][0] * m.E[1][1] * m.E[2][2] + + m.E[0][1] * m.E[1][2] * m.E[2][0] + + m.E[0][2] * m.E[1][0] * m.E[2][1] + - m.E[0][2] * m.E[1][1] * m.E[2][0] + - m.E[0][1] * m.E[1][0] * m.E[2][2] + - m.E[0][0] * m.E[1][2] * m.E[2][1]; +} + +inline m3 transpose(m3 m) +{ + m3 result; + + result.E[0][0] = m.E[0][0]; + result.E[0][1] = m.E[1][0]; + result.E[0][2] = m.E[2][0]; + + result.E[1][0] = m.E[0][1]; + result.E[1][1] = m.E[1][1]; + result.E[1][2] = m.E[2][1]; + + result.E[2][0] = m.E[0][2]; + result.E[2][1] = m.E[1][2]; + result.E[2][2] = m.E[2][2]; + + result.E[3][0] = m.E[0][3]; + result.E[3][1] = m.E[1][3]; + result.E[3][2] = m.E[2][3]; + + return result; +} + +inline m3 inverse(m3 m) +{ + m3 result; + + f32 one_over_det = 1.0 / determinant(m); + f32 sign = 1; + for(int r = 0; r < 3; r++) + { + for(int c = 0; c < 3; c++) + { + result.E[c][r] = sign * determinant_of_minor(m, r, c) * one_over_det; + sign = -sign; + } + } + + return result; +} + +inline f32 determinant_of_minor(m4 m, int skip_r, int skip_c) +{ + m3 minor; + for(int r = 0, source_r = 0; r < 3; r++, source_r++) + { + if(source_r == skip_r) + source_r++; + for(int c = 0, source_c = 0; c < 3; c++, source_c++) + { + if(source_c == skip_c) + source_c++; + minor.E[r][c] = m.E[source_r][source_c]; + } + } + return determinant(minor); +} + +inline f32 determinant(m4 m) +{ + f32 result = 0; + f32 sign = 1; + int r = 0; + // Laplace theorem + for(int c = 0; c < 4; c++) + { + result += m.E[r][c] * sign * determinant_of_minor(m, r, c); + sign = -sign; + } + return result; +} + +inline m4 transpose(m4 m) +{ + m4 result; + + result.E[0][0] = m.E[0][0]; + result.E[0][1] = m.E[1][0]; + result.E[0][2] = m.E[2][0]; + result.E[0][3] = m.E[3][0]; + + result.E[1][0] = m.E[0][1]; + result.E[1][1] = m.E[1][1]; + result.E[1][2] = m.E[2][1]; + result.E[1][3] = m.E[3][1]; + + result.E[2][0] = m.E[0][2]; + result.E[2][1] = m.E[1][2]; + result.E[2][2] = m.E[2][2]; + result.E[2][3] = m.E[3][2]; + + result.E[3][0] = m.E[0][3]; + result.E[3][1] = m.E[1][3]; + result.E[3][2] = m.E[2][3]; + result.E[3][3] = m.E[3][3]; + + return result; +} + +inline m4 inverse(m4 m) +{ + m4 result; + + f32 one_over_det = 1.0 / determinant(m); + f32 sign_a = 1; + for(int r = 0; r < 4; r++) + { + f32 sign_b = sign_a; + for(int c = 0; c < 4; c++) + { + result.E[c][r] = sign_b * determinant_of_minor(m, r, c) * one_over_det; + sign_b = -sign_b; + } + sign_a = -sign_a; + } + + return result; +} + +#endif diff --git a/code/lib/memory.h b/code/lib/memory.h new file mode 100644 index 0000000..735fcc9 --- /dev/null +++ b/code/lib/memory.h @@ -0,0 +1,10 @@ +#ifndef _PIUMA_LIB_MEMORY_H_ +#define _PIUMA_LIB_MEMORY_H_ + +#include + +typedef void *(*alloc_t)(size_t size); +typedef void *(*realloc_t)(void *ptr, size_t size); +typedef void (*free_t)(void *ptr); + +#endif diff --git a/code/lib/queue.h b/code/lib/queue.h new file mode 100644 index 0000000..e820ace --- /dev/null +++ b/code/lib/queue.h @@ -0,0 +1,93 @@ +#ifndef _PIUMA_LIB_QUEUE_H_ +#define _PIUMA_LIB_QUEUE_H_ + +#include "types.h" +#include + +struct queue_header +{ + u64 start; + u64 size; + + u64 capacity; +}; + + +#define QUEUE_HEADER_PTR(queue) ((queue_header *)(((u8*)queue) - sizeof(queue_header))) + +#define QUEUE_TYPE(type) type * +#define Queue_Alloc(alloc_func, type, capacity) ((type*) _queue_alloc(alloc_func, sizeof(type), capacity)) +#define Queue_Free(free_func, queue) free_func(QUEUE_HEADER_PTR(queue)) + +#define Queue_Pop(queue) (queue[_queue_pop_index((u8*)queue)]) +#define Queue_Push(queue, element) { queue[_queue_at_index((u8*)queue, QUEUE_HEADER_PTR(queue)->size)] = element; _queue_push_fix_indices((u8*)queue); } +#define Queue_At(queue, index) (queue[_queue_at_index((u8*)queue, index)]) +#define Queue_Size(queue) _queue_size((u8*)queue) +#define Queue_Capacity(queue) _queue_capacity((u8*)queue) + + + +typedef void * (*alloc_func_t)(u64); +typedef void (*free_func_t)(void *); + +inline u8 * _queue_alloc(alloc_func_t alloc_func, u64 sizeof_type, u64 capacity) +{ + u8 *data; + queue_header *header; + + data = (u8 *)alloc_func(sizeof(queue_header) + sizeof_type * capacity); + header = (queue_header *)data; + + header->capacity = capacity; + header->start = 0; + header->size = 0; + + return data + sizeof(queue_header); +} + +inline u64 _queue_pop_index(u8 *queue) +{ + queue_header *header = QUEUE_HEADER_PTR(queue); + assert(header->size > 0); + + u64 element_index = header->start; + header->start = (header->start + 1) % header->capacity; + header->size--; + + return element_index; +} + +inline void _queue_push_fix_indices(u8 *queue) +{ + queue_header *header = QUEUE_HEADER_PTR(queue); + + header->size++; + if(header->size > header->capacity) + { + // Queue is full. Remove oldest element + header->start = (header->start + 1) % header->capacity; + header->size = header->capacity; + } +} + +inline u64 _queue_at_index(u8 *queue, u64 index) +{ + queue_header *header = QUEUE_HEADER_PTR(queue); + + return (header->start + index) % header->capacity; +} + +inline u64 _queue_size(u8 *queue) +{ + queue_header *header = QUEUE_HEADER_PTR(queue); + return header->size; +} + +inline u64 _queue_capacity(u8 *queue) +{ + queue_header *header = QUEUE_HEADER_PTR(queue); + return header->capacity; +} + + +#endif diff --git a/code/lib/text.cpp b/code/lib/text.cpp new file mode 100644 index 0000000..52cd9b5 --- /dev/null +++ b/code/lib/text.cpp @@ -0,0 +1,196 @@ +#include "text.h" + +u64 utf8_codepoint_count(const char *s) +{ + u64 count = 0; + while(*s) + { + if(utf8_is_codepoint_start(s)) + count++; + s++; + } + return count; +} + +bool utf8_is_codepoint_start(const char *s) +{ + return (*s & 0b11000000) != 0b10000000; +} + +u32 utf8_codepoint_bytes(const char *s) +{ + if(!utf8_is_codepoint_start(s)) + return 0; // Error: This byte belongs to a previous codepoint + + if((*s & 0b10000000) == 0b00000000) //1 byte codepoint + return 1; + else if((*s & 0b11100000) == 0b11000000) //2 bytes codepoint + return 2; + else if((*s & 0b11110000) == 0b11100000) //3 bytes codepoint + return 3; + else if((*s & 0b11111000) == 0b11110000) //4 bytes codepoint + return 4; + return 0; +} + +/* If bytes_read returns 0, we either reached the end of the string or there was a decoding error */ +utf8_codepoint utf8_extract_codepoint(const char *s, u64 current_index, u32 *bytes_read) +{ + s += current_index; + // UTF8: + // First byte: (0xxxxxxx = 1 byte, 110xxxxx = 2 byte, 1110xxxx = 3 byte, 11110xxx = 4 byte) + // Next bytes: 10xxxxxx + // To get a Codepoint: concatenate all the xxxx + utf8_codepoint codepoint = 0; + *bytes_read = 0; + u8 next_bytes = 0; + + if(!utf8_is_codepoint_start(s)) + { + // Error: This byte belongs to a previous codepoint + return 0; + } + + if((*s & 0b10000000) == 0b00000000) //1 byte codepoint + { + codepoint = *s; + next_bytes = 0; + } + else if((*s & 0b11100000) == 0b11000000) //2 bytes codepoint + { + codepoint = (*s & 0b00011111); + next_bytes = 1; + } + else if((*s & 0b11110000) == 0b11100000) //3 bytes codepoint + { + codepoint = (*s & 0b00001111); + next_bytes = 2; + } + else if((*s & 0b11111000) == 0b11110000) //4 bytes codepoint + { + codepoint = (*s & 0b00000111); + next_bytes = 3; + } + + for(u8 i = 0; i < next_bytes; i++) + { + s++; + if(*s == 0) + { + // Error: End of string reached before completing codepoint + return 0; + } + if((*s & 0b11000000) != 0b10000000) + { + // Error: Byte prefix does not match with the expected one. Broken codepoint + return 0; + } + codepoint = codepoint << 6; + codepoint |= (*s & 0b00111111); + } + *bytes_read = next_bytes + 1; + + return codepoint; +} + +u32 utf8_bytes_to_next_valid_codepoint(const char *s, u64 current_index) +{ + s += current_index; + u64 bytes = 1; + while(*(s + bytes)) + { + if(utf8_is_codepoint_start(s + bytes)) + break; + bytes++; + } + return bytes; +} + +u32 utf8_bytes_to_prev_valid_codepoint(const char *s, u64 current_index) +{ + s += current_index; + u64 bytes = 0; + while(bytes < current_index) + { + bytes++; + if(utf8_is_codepoint_start(s - bytes)) + break; + } + return bytes; +} + + +u64 utf8_from_string(const char *s, u64 *bytes_read, utf8_codepoint *result, u64 result_size) +{ + u64 decoded = 0; + bytes_read = 0; + while(*s && decoded < result_size) + { + u32 read = 0; + result[decoded] = utf8_extract_codepoint(s, 0, &read); + if(read == 0) + { + bytes_read = 0; + break; + } + + s += read; + bytes_read += read; + decoded++; + } + + return decoded; +} + +u64 utf8_to_string(utf8_codepoint *codepoints, u64 count, char *result, u64 result_size) +{ + result_size--; // Reserve space for zero-terminator + u64 i = 0; + u64 result_i = 0; + for(i = 0; i < count; i++) + { + utf8_codepoint cp = codepoints[i]; + if((cp & 0xFFFFFF80) == 0) // 1 byte + { + if(result_i + 1 >= result_size) // Not enought space left + break; + + result[result_i++] = cp & 0b01111111; + } + else if((cp & 0xFFFFF800) == 0) // 2 bytes + { + if(result_i + 2 >= result_size) // Not enought space left + break; + + result[result_i++] = 0b11000000 | ((cp >> 6) & 0b00011111); + result[result_i++] = 0b10000000 | ((cp ) & 0b00111111); + } + else if((cp & 0xFFFF0000) == 0) // 3 bytes + { + if(result_i + 3 >= result_size) // Not enought space left + break; + + result[result_i++] = 0b11100000 | ((cp >> 12) & 0b00001111); + result[result_i++] = 0b10000000 | ((cp >> 6) & 0b00111111); + result[result_i++] = 0b10000000 | ((cp ) & 0b00111111); + } + else if((cp & 0xFFE00000) == 0) // 4 bytes + { + if(result_i + 4 >= result_size) // Not enought space left + break; + + result[result_i++] = 0b11110000 | ((cp >> 18) & 0b00000111); + result[result_i++] = 0b10000000 | ((cp >> 12) & 0b00111111); + result[result_i++] = 0b10000000 | ((cp >> 6) & 0b00111111); + result[result_i++] = 0b10000000 | ((cp ) & 0b00111111); + } + else + { + // Invalid codepoint + break; + } + } + + result[result_i] = 0; + return i; +} diff --git a/code/lib/text.h b/code/lib/text.h new file mode 100644 index 0000000..ea185c2 --- /dev/null +++ b/code/lib/text.h @@ -0,0 +1,22 @@ +#ifndef _PIUMA_LIB_TEXT_H_ +#define _PIUMA_LIB_TEXT_H_ + +#include "types.h" + +typedef u32 utf8_codepoint; + +u64 utf8_codepoint_count(const char *s); +bool utf8_is_codepoint_start(const char *s); +u32 utf8_codepoint_bytes(const char *s); +/* If bytes_read returns 0, we either reached the end of the string or there was a decoding error */ +utf8_codepoint utf8_extract_codepoint(const char *s, u64 current_index, u32 *bytes_read); +u32 utf8_bytes_to_next_valid_codepoint(const char *s, u64 current_index); +u32 utf8_bytes_to_prev_valid_codepoint(const char *s, u64 current_index); + + +/* Returns the number of codepoints read. If bytes_read returns 0, there was a decoding error */ +u64 utf8_from_string(const char *s, u64 *bytes_read, utf8_codepoint *result, u64 result_size); +/* Returns the number of codepoints written. */ +u64 utf8_to_string(utf8_codepoint *codepoints, u64 count, char *result, u64 result_size); + +#endif diff --git a/code/lib/types.h b/code/lib/types.h new file mode 100644 index 0000000..b8d0709 --- /dev/null +++ b/code/lib/types.h @@ -0,0 +1,42 @@ +#ifndef _PIUMA_LIB_TYPES_H_ +#define _PIUMA_LIB_TYPES_H_ + +#include +#include + +// Integers +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +// Real numbers +typedef float f32; +typedef double f64; + +// Binary +typedef u8 b8; +typedef u16 b16; +typedef u32 b32; +typedef u64 b64; + + +// Buffer +struct Buffer +{ + u64 size; + u8 *data; +}; + +struct String +{ + u64 size; + u8 *text; +}; + +#endif diff --git a/code/linux_platform.cpp b/code/linux_platform.cpp new file mode 100644 index 0000000..48fe698 --- /dev/null +++ b/code/linux_platform.cpp @@ -0,0 +1,1092 @@ +#include "platform.h" + +#include +#include +#include +#include +#include "unistd.h" + +#include "X11/Xlib.h" +#include "X11/extensions/XInput2.h" +#include "X11/keysymdef.h" +#include "GL/glx.h" +#include "signal.h" +#include "pulse/pulseaudio.h" +#include "semaphore.h" + +#include "lib/queue.h" +#include "debug/logger.h" + +#ifndef SWAP_INTERVAL +#define SWAP_INTERVAL -1 +#endif + +// Platform state +static Display *p_display = NULL; +static Window p_root_window; +static Window p_window; +static GLXDrawable p_glx_drawable; + + +static u32 p_width; +static u32 p_height; + +static bool p_mouse_grabbed; +static bool p_window_has_focus; +static bool p_cursor_is_inside_window; +static void p_mouse_grab_internal(bool grab); +static Cursor p_empty_cursor; +static int p_xi2_opcode; +static XIM p_xim; +static XIC p_xic; + + +static QUEUE_TYPE(Event) p_event_queue; + +static void linux_signal_handler(s32 signal) +{ + switch(signal) + { + case SIGINT: + { + Event e; + e.type = EVENT_QUIT; + if(Queue_Size(p_event_queue) >= Queue_Capacity(p_event_queue)) + LOG(LOG_ERROR, "Event queue full. Dropping oldest event after receiving signal %d", signal); + Queue_Push(p_event_queue, e); + } break; + default: + { + LOG(LOG_WARNING, "Unmanaged linux signal received: %d", signal); + } + } +} + + +static pa_threaded_mainloop *p_audio_mainloop; +static pa_mainloop_api *p_audio_mainloop_api; +static pa_context *p_audio_context; +static sem_t p_audio_context_ready; +static pa_context_state_t p_audio_context_state; +static pa_stream *p_audio_stream; +static void *p_audio_data; +static p_audio_callback p_audio_cb; +static u32 _p_audio_sample_rate; + +static void pa_context_cb(pa_context *c, void *userdata) +{ + p_audio_context_state = pa_context_get_state(c); + s32 status = sem_post(&p_audio_context_ready); + assert(status == 0); +} + +static pa_context_state_t wait_pa_context_state() +{ + s32 status; + status = sem_wait(&p_audio_context_ready); + assert(status == 0); + sem_destroy(&p_audio_context_ready); + return p_audio_context_state; +} + +static void p_audio_write_cb(pa_stream *stream, long unsigned success, void *userdata) +{ + s32 status; + void *data; + u64 n_bytes; + status = pa_stream_begin_write(stream, &data, &n_bytes); + if(status != 0 || data == NULL) + { + // Pulseadio allocation failed. Alloc our own memory + n_bytes = 16 * 1024; + if(!p_audio_data) + p_audio_data = p_alloc(n_bytes); + data = p_audio_data; + } + + p_audio_buffer buffer; + buffer.samples = (p_audio_sample*)data; + buffer.size = n_bytes / sizeof(p_audio_sample); + + if(p_audio_cb) + p_audio_cb(&buffer); + + n_bytes = buffer.size * sizeof(p_audio_sample); + pa_stream_write(stream, data, n_bytes, NULL, 0, PA_SEEK_RELATIVE); +} + +// Generic platform initialization +void p_init(bool capture_os_signals) +{ + p_width = 1280; + p_height = 720; + + p_mouse_grabbed = false; + p_window_has_focus = false; + p_cursor_is_inside_window = false; + + // Events + p_event_queue = Queue_Alloc(p_alloc, Event, 32); + + if(capture_os_signals) + { + struct sigaction sa; + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = linux_signal_handler; + int status = sigaction(SIGINT, &sa, NULL); + assert(status == 0); + } + + // Audio + { + _p_audio_sample_rate = 44100; + p_audio_data = NULL; + p_audio_cb = NULL; + + s32 status; + p_audio_mainloop = pa_threaded_mainloop_new(); + assert(p_audio_mainloop != NULL); + status = pa_threaded_mainloop_start(p_audio_mainloop); + assert(status == 0); + + pa_threaded_mainloop_lock(p_audio_mainloop); + + p_audio_mainloop_api = pa_threaded_mainloop_get_api(p_audio_mainloop); + assert(p_audio_mainloop_api != NULL); + + p_audio_context = pa_context_new(p_audio_mainloop_api, "Piuma"); + assert(p_audio_context != NULL); + status = pa_context_connect(p_audio_context, NULL, PA_CONTEXT_NOFLAGS, NULL); + assert(status == 0); + status = sem_init(&p_audio_context_ready, 0, 0); + assert(status == 0); + pa_context_set_state_callback(p_audio_context, pa_context_cb, NULL); + pa_threaded_mainloop_unlock(p_audio_mainloop); + pa_context_state_t context_state = PA_CONTEXT_UNCONNECTED; + while(context_state == PA_CONTEXT_UNCONNECTED || context_state == PA_CONTEXT_CONNECTING || context_state == PA_CONTEXT_AUTHORIZING || context_state == PA_CONTEXT_SETTING_NAME) + { + context_state = wait_pa_context_state(); + } + pa_threaded_mainloop_lock(p_audio_mainloop); + assert(context_state == PA_CONTEXT_READY); + + pa_sample_spec ss; + ss.format = PA_SAMPLE_FLOAT32LE; + ss.rate = _p_audio_sample_rate; + ss.channels = 2; + p_audio_stream = pa_stream_new(p_audio_context, "Piuma audio", &ss, NULL); + assert(p_audio_stream != NULL); + + pa_stream_set_write_callback(p_audio_stream, p_audio_write_cb, NULL); + status = pa_stream_connect_playback(p_audio_stream, NULL, NULL, PA_STREAM_NOFLAGS, NULL, NULL); + + pa_threaded_mainloop_unlock(p_audio_mainloop); + } +} + +void p_deinit() +{ + Queue_Free(p_free, p_event_queue); + + // Audio + pa_stream_disconnect(p_audio_stream); + pa_context_disconnect(p_audio_context); + + pa_threaded_mainloop_stop(p_audio_mainloop); + pa_threaded_mainloop_free(p_audio_mainloop); + + if(p_audio_data) + p_free(p_audio_data); +} + + +// Memory +void * p_alloc(u64 size) +{ + return malloc(size); +} + +void * p_realloc(void *ptr, u64 new_size) +{ + return realloc(ptr, new_size); +} + +void p_free(void *ptr) +{ + free(ptr); +} + + +// File IO +bool p_file_init(p_file *file, const char *filename, b32 flags) +{ + bool create_flag_set = flags & P_FILE_CREATE_IF_NOT_EXISTS; + + + file->handle = fopen(filename, "rb+"); + if(create_flag_set && file->handle == NULL) // @Robustness: check errno + { + file->handle = fopen(filename, "wb+"); + } + + if(file->handle == NULL) + return false; + return true; +} + +u64 p_file_size(p_file *file) +{ + assert(file->handle != NULL); + + int status; + u64 saved_pos = ftell(file->handle); + + status = fseek(file->handle, 0, SEEK_END); + assert(status == 0); + u64 size = ftell(file->handle); + + // Restore saved state + status = fseek(file->handle, saved_pos, SEEK_SET); + assert(status == 0); + + return size; +} + +bool p_file_read(p_file *file, Buffer *buf, u64 max_size) +{ + assert(file->handle != NULL); + + int status; + u64 read; + + status = fseek(file->handle, 0, SEEK_SET); + assert(status == 0); + + read = fread(buf->data, 1, max_size, file->handle); + buf->size = read; + + if(feof(file->handle) || read == max_size) + { + // File completely read successfully + return true; + } + return false; +} + +bool p_file_write(p_file *file, u8 *data, u64 size) +{ + assert(file->handle != NULL); + + int status; + u64 written; + + status = fseek(file->handle, 0, SEEK_SET); + assert(status == 0); + + written = fwrite(data, 1, size, file->handle); + if(written == size) + { + return true; + } + return false; +} + +void p_file_deinit(p_file *file) +{ + assert(file->handle != NULL); + + int res = fclose(file->handle); + assert(res == 0); +} + + +// Timers +f64 p_time() // Returns seconds +{ + f64 result; + timespec ts; + + clock_gettime(CLOCK_MONOTONIC_RAW, &ts); + result = ((f64)ts.tv_sec) + ((f64)ts.tv_nsec * 1e-9); + + return result; +} + +void p_wait(f64 milliseconds) +{ + int status; + + u64 useconds = milliseconds * 1000; + status = usleep(useconds); + + assert(status == 0); +} + + + +// Windowing +/* +Related documentation available at: + - X11: https://x.org/releases/current/doc/libX11/libX11/libX11.html + - GLX: https://khronos.org/registry/OpenGL/specs/gl/glx1.4.pdf + +Before starting to code, I suggest you take a look to the X11 documentation first +and then take a look to at least the GLX intro. +When you need to use a function from the X11 docs, look in the GLX spec to see if +a replacement function is provided. +*/ + +static int p_fb_config_attributes[] = +{ + // GLX_BUFFER_SIZE, 32, // RGBA 8-bit each + GLX_DOUBLEBUFFER, True, + GLX_DEPTH_SIZE, 24, + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PIXMAP_BIT, + GLX_X_RENDERABLE, True, + GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, + None +}; + +static int p_gl_attributes[] = +{ + GLX_CONTEXT_MAJOR_VERSION_ARB, 4, + GLX_CONTEXT_MINOR_VERSION_ARB, 3, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, + None +}; + +static u32 p_x11_event_mask = ExposureMask | FocusChangeMask | + //ResizeRedirectMask | + KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask; + +void p_window_open() +{ + int p_default_screen; + XVisualInfo *p_visual_info; + Colormap p_color_map; + + GLXFBConfig p_fb_config; + GLXWindow p_glx_window; + GLXContext p_glx_context; + + /* Open a connection to X11 server*/ + { + p_display = XOpenDisplay(NULL); + assert(p_display != NULL); + + p_default_screen = XDefaultScreen(p_display); + p_root_window = XDefaultRootWindow(p_display); + } + + + /* Get a FBConfig with the right parameters */ + { + int n_fb_config; + GLXFBConfig *fb_config_list; + + fb_config_list = glXChooseFBConfig(p_display, p_default_screen, p_fb_config_attributes, &n_fb_config); + assert(fb_config_list != NULL); + p_fb_config = *fb_config_list; // Get first matching FBConfig + + XFree(fb_config_list); + } + + /* Create a window */ + { + // @Robustness: manage errors for CreateWindow and MapWindow + + // Visual info struct + p_visual_info = glXGetVisualFromFBConfig(p_display, p_fb_config); + assert(p_visual_info != NULL); + + p_color_map = XCreateColormap(p_display, p_root_window, p_visual_info->visual, AllocNone); + + // X11 Window struct + XSetWindowAttributes win_attr; + win_attr.colormap = p_color_map; + win_attr.event_mask = ExposureMask | FocusChangeMask | + //ResizeRedirectMask | + KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask | + EnterWindowMask | LeaveWindowMask; + + p_window = XCreateWindow(p_display, p_root_window, 10, 10, p_width, p_height, 0, p_visual_info->depth, InputOutput, p_visual_info->visual, CWColormap | CWEventMask, &win_attr); + + // GLX Window struct + p_glx_window = glXCreateWindow(p_display, p_fb_config, p_window, NULL); + + XMapWindow(p_display, p_window); + + XFree(p_visual_info); + + p_window_has_focus = true; + } + + // Create empty cursor for the times when mouse is grabbed + { + char data[1] = {0}; + Pixmap pixmap; + XColor color; + + pixmap = XCreateBitmapFromData(p_display, p_window, data, 1, 1); + color.red = color.green = color.blue = 0; + + p_empty_cursor = XCreatePixmapCursor(p_display, pixmap, pixmap, &color, &color, 0, 0); + + XFreePixmap(p_display, pixmap); + } + + // Initialize XInput2 + { + // http://who-t.blogspot.com/2009/05/xi2-recipes-part-1.html + int event, error; + int major, minor; + + if(!XQueryExtension(p_display, "XInputExtension", &p_xi2_opcode, &event, &error)) + { + LOG(LOG_WARNING, "X Input extension not available."); + } + + major = 2; + minor = 2; + if(XIQueryVersion(p_display, &major, &minor) == BadRequest) + { + LOG(LOG_WARNING, "XInput2 not available. Server supports %d.%d", major, minor); + } + } + + // XIM (X Input Method) + { + p_xim = XOpenIM(p_display, NULL, NULL, NULL); + if(p_xim == NULL) + LOG(LOG_ERROR, "Cannot open XIM (input method)"); + p_xic = XCreateIC(p_xim, XNInputStyle, (XIMPreeditNothing | XIMStatusNothing), XNClientWindow, p_window, NULL); + if(p_xic == NULL) + LOG(LOG_ERROR, "Cannot create XIC (input context)"); + + XSetICFocus(p_xic); + //void XUnsetICFocus(XIC ic); + //char *XmbResetIC(XIC ic); + } + + /* GLX extensions */ + + /* OpenGL Context and Drawable*/ + { + // @Robustness: Check GLX extension string to see if this function is available. + p_glx_context = glXCreateContextAttribsARB(p_display, p_fb_config, NULL, GL_TRUE, p_gl_attributes); + + glXMakeCurrent(p_display, p_window, p_glx_context); + + p_glx_drawable = glXGetCurrentDrawable(); + assert(p_glx_drawable != None); + } + + /* VSync */ + { + // @Robustness: Check GLX extension string to see if this function is available. + glXSwapIntervalEXT(p_display, p_glx_drawable, SWAP_INTERVAL); + } + + XFlush(p_display); +} + +void p_window_name(char *name) +{ + XStoreName(p_display, p_window, name); + XFlush(p_display); +} + +void p_window_resize(u32 width, u32 height, bool fullscreen) +{ + // @Feature: fullscreen support + XResizeWindow(p_display, p_window, width, height); + XFlush(p_display); // Does not resize without flush +} + +// You shouldn't call this for each frame. Cache the data or just look at system events. +void p_window_dimensions(u32 *width, u32 *height) +{ + Window root; + s32 x, y; + u32 border_w, border_h; + // Other parameters cannot be null + XGetGeometry(p_display, p_glx_drawable, &root, &x, &y, &p_width, &p_height, &border_w, &border_h); + *width = p_width; + *height = p_height; +} + +void p_window_close() +{ + XDestroyIC(p_xic); + XCloseIM(p_xim); + XCloseDisplay(p_display); + p_display = NULL; +} + + +// Graphics +void p_graphics_swap_interval(s32 frames) +{ + // -1 for Adaptive Sync, 0 for no interval, >0 to wait 'frames' frames between each swap + glXSwapIntervalEXT(p_display, p_glx_drawable, frames); +} + +void p_graphics_swap() +{ + glXSwapBuffers(p_display, p_window); +} + + +#define CASE_STRING(str) case str: return #str; +static const char * x11_event_type_str(s32 type) +{ + switch(type) + { + CASE_STRING(MotionNotify) + CASE_STRING(ButtonPress) + CASE_STRING(ButtonRelease) + CASE_STRING(ColormapNotify) + CASE_STRING(EnterNotify) + CASE_STRING(LeaveNotify) + CASE_STRING(Expose) + CASE_STRING(GraphicsExpose) + CASE_STRING(NoExpose) + CASE_STRING(FocusIn) + CASE_STRING(FocusOut) + CASE_STRING(KeymapNotify) + CASE_STRING(KeyPress) + CASE_STRING(KeyRelease) + CASE_STRING(PropertyNotify) + CASE_STRING(ResizeRequest) + CASE_STRING(CirculateNotify) + CASE_STRING(ConfigureNotify) + CASE_STRING(DestroyNotify) + CASE_STRING(GravityNotify) + CASE_STRING(MapNotify) + CASE_STRING(ReparentNotify) + CASE_STRING(UnmapNotify) + CASE_STRING(CirculateRequest) + CASE_STRING(ConfigureRequest) + CASE_STRING(MapRequest) + CASE_STRING(ClientMessage) + CASE_STRING(MappingNotify) + CASE_STRING(SelectionClear) + CASE_STRING(SelectionNotify) + CASE_STRING(SelectionRequest) + CASE_STRING(VisibilityNotify) + default: return "Unknown X11 type"; + } +} + +static Key_Code map_x11_button(u32 button) +{ + switch(button) + { + case 1: return KEY_MOUSE_LEFT; + case 2: return KEY_MOUSE_MIDDLE; + case 3: return KEY_MOUSE_RIGHT; + case 4: return KEY_MOUSE_WHEEL_UP; + case 5: return KEY_MOUSE_WHEEL_DOWN; + case 8: return KEY_MOUSE_4; + case 9: return KEY_MOUSE_5; + } + + return KEY_UNKNOWN; +} + +static Key_Code map_x11_keycode(u32 keycode) +{ + //LOG(LOG_DEBUG, "Keycode %u", keycode); + // @Performance: a lot of this codes are sequential. Check with other OSs if that's the case. If it is, we can have faster mapping by subtracting and adding an offset. + KeySym keysym = XKeycodeToKeysym(p_display, keycode, 0); + switch(keysym) + { + case XK_Return: return KEY_ENTER; + case XK_ISO_Enter: return KEY_ENTER; + case XK_Escape: return KEY_ESCAPE; + case XK_BackSpace: return KEY_BACKSPACE; + case XK_Tab: return KEY_TAB; + case XK_space: return KEY_SPACE; + case XK_exclam: return KEY_EXCLAMATION; + case XK_quotedbl: return KEY_DOUBLE_QUOTE; + case XK_numbersign: return KEY_HASH; + case XK_percent: return KEY_PERCENT; + case XK_dollar: return KEY_DOLLAR; + case XK_ampersand: return KEY_AMPERSAND; + case XK_apostrophe: return KEY_SINGLE_QUOTE; + case XK_parenleft: return KEY_LEFT_PARENTHESIS; + case XK_parenright: return KEY_RIGHT_PARENTHESIS; + case XK_asterisk: return KEY_ASTERISK; + case XK_plus: return KEY_PLUS; + case XK_comma: return KEY_COMMA; + case XK_minus: return KEY_MINUS; + case XK_period: return KEY_PERIOD; + case XK_slash: return KEY_SLASH; + case XK_0: return KEY_0; + case XK_1: return KEY_1; + case XK_2: return KEY_2; + case XK_3: return KEY_3; + case XK_4: return KEY_4; + case XK_5: return KEY_5; + case XK_6: return KEY_6; + case XK_7: return KEY_7; + case XK_8: return KEY_8; + case XK_9: return KEY_9; + case XK_colon: return KEY_COLON; + case XK_semicolon: return KEY_SEMICOLON; + case XK_less: return KEY_LESS; + case XK_equal: return KEY_EQUALS; + case XK_greater: return KEY_GREATER; + case XK_question: return KEY_QUESTION; + case XK_at: return KEY_AT; + case XK_bracketleft: return KEY_LEFT_BRACKET; + case XK_bracketright: return KEY_RIGHT_BRACKET; + case XK_backslash: return KEY_BACKSLASH; + case XK_asciicircum: return KEY_CARET; + case XK_underscore: return KEY_UNDERSCORE; + case XK_grave: return KEY_BACKQUOTE; + case XK_a: return KEY_A; + case XK_b: return KEY_B; + case XK_c: return KEY_C; + case XK_d: return KEY_D; + case XK_e: return KEY_E; + case XK_f: return KEY_F; + case XK_g: return KEY_G; + case XK_h: return KEY_H; + case XK_i: return KEY_I; + case XK_j: return KEY_J; + case XK_k: return KEY_K; + case XK_l: return KEY_L; + case XK_m: return KEY_M; + case XK_n: return KEY_N; + case XK_o: return KEY_O; + case XK_p: return KEY_P; + case XK_q: return KEY_Q; + case XK_r: return KEY_R; + case XK_s: return KEY_S; + case XK_t: return KEY_T; + case XK_u: return KEY_U; + case XK_v: return KEY_V; + case XK_w: return KEY_W; + case XK_x: return KEY_X; + case XK_y: return KEY_Y; + case XK_z: return KEY_Z; + + case XK_Caps_Lock: return KEY_CAPSLOCK; + case XK_F1: return KEY_F1; + case XK_F2: return KEY_F2; + case XK_F3: return KEY_F3; + case XK_F4: return KEY_F4; + case XK_F5: return KEY_F5; + case XK_F6: return KEY_F6; + case XK_F7: return KEY_F7; + case XK_F8: return KEY_F8; + case XK_F9: return KEY_F9; + case XK_F10: return KEY_F10; + case XK_F11: return KEY_F11; + case XK_F12: return KEY_F12; + + case XK_Print: return KEY_PRINTSCREEN; + case XK_Scroll_Lock: return KEY_SCROLLLOCK; + case XK_Pause: return KEY_PAUSE; + case XK_Insert: return KEY_INSERT; + case XK_Delete: return KEY_DELETE; + + case XK_Home: return KEY_HOME; + case XK_End: return KEY_END; + case XK_Page_Up: return KEY_PAGEUP; + case XK_Page_Down: return KEY_PAGEDOWN; + + case XK_Up: return KEY_ARROW_UP; + case XK_Down: return KEY_ARROW_DOWN; + case XK_Left: return KEY_ARROW_LEFT; + case XK_Right: return KEY_ARROW_RIGHT; + + case XK_Num_Lock: return KEY_NUMLOCK; + case XK_KP_Divide: return KEY_PAD_DIVIDE; + case XK_KP_Multiply: return KEY_PAD_MULTIPLY; + case XK_KP_Subtract: return KEY_PAD_MINUS; + case XK_KP_Add: return KEY_PAD_PLUS; + case XK_KP_Enter: return KEY_PAD_ENTER; + case XK_KP_1: return KEY_PAD_1; + case XK_KP_2: return KEY_PAD_2; + case XK_KP_3: return KEY_PAD_3; + case XK_KP_4: return KEY_PAD_4; + case XK_KP_5: return KEY_PAD_5; + case XK_KP_6: return KEY_PAD_6; + case XK_KP_7: return KEY_PAD_7; + case XK_KP_8: return KEY_PAD_8; + case XK_KP_9: return KEY_PAD_9; + case XK_KP_0: return KEY_PAD_0; + case XK_KP_Separator: return KEY_PAD_PERIOD; + case XK_KP_End: return KEY_PAD_1; + case XK_KP_Down: return KEY_PAD_2; + case XK_KP_Page_Down: return KEY_PAD_3; + case XK_KP_Left: return KEY_PAD_4; + case XK_KP_Begin: return KEY_PAD_5; + case XK_KP_Right: return KEY_PAD_6; + case XK_KP_Home: return KEY_PAD_7; + case XK_KP_Up: return KEY_PAD_8; + case XK_KP_Page_Up: return KEY_PAD_9; + case XK_KP_Insert: return KEY_PAD_0; + case XK_KP_Delete: return KEY_PAD_PERIOD; + + case XK_Control_L: return KEY_LEFT_CTRL; + case XK_Control_R: return KEY_RIGHT_CTRL; + case XK_Shift_L: return KEY_LEFT_SHIFT; + case XK_Shift_R: return KEY_RIGHT_SHIFT; + case XK_Alt_L: return KEY_LEFT_ALT; + case XK_Alt_R: return KEY_RIGHT_ALT; + default: { + LOG(LOG_WARNING, "Keycode not mapped: %u 0x%X - sym %u 0x%X", keycode, keycode, keysym, keysym); + return KEY_UNKNOWN; + } + } +} + +static void p_events_process() +{ + while(Queue_Size(p_event_queue) < Queue_Capacity(p_event_queue) && XEventsQueued(p_display, QueuedAfterReading)) + { + XEvent e; + XNextEvent(p_display, &e); + + //if(e.type != 6) LOG(LOG_DEBUG, "X11 event: %d - %s", e.type, x11_event_type_str(e.type)); + // @Feature: xinput2, gamepads + switch(e.type) + { + case MotionNotify: + { + XMotionEvent m = e.xmotion; + Event event; + + event.type = EVENT_MOUSE_MOVE; + event.mouse_move.relative = false; + event.mouse_move.position.x = m.x; + event.mouse_move.position.y = m.y; + + Queue_Push(p_event_queue, event); + + // Do things + bool is_grab_position = (m.x == p_width/2) && (m.y == p_height/2); + if(p_mouse_grabbed && p_window_has_focus && !is_grab_position) + XWarpPointer(p_display, None, p_window, 0, 0, 0, 0, p_width / 2, p_height / 2); + } break; + + case GenericEvent: + { + bool is_xi2_cookie = (e.xcookie.extension == p_xi2_opcode); + if(is_xi2_cookie) + { + if(XGetEventData(p_display, &e.xcookie)) + { + static int a = 0; + switch(e.xcookie.evtype) + { + case XI_RawMotion: + { + if(p_mouse_grabbed && p_window_has_focus) + { + XIRawEvent *re = (XIRawEvent *)e.xcookie.data; + if((1 < re->valuators.mask_len * 8) && XIMaskIsSet(re->valuators.mask, 0) && XIMaskIsSet(re->valuators.mask, 1)) + { + Event event; + + event.type = EVENT_MOUSE_MOVE; + event.mouse_move.relative = true; + event.mouse_move.position.x = re->raw_values[0]; + event.mouse_move.position.y = re->raw_values[1]; + + Queue_Push(p_event_queue, event); + } + } + } break; + case XI_RawButtonPress: + { + } break; + case XI_RawButtonRelease: + { + } break; + } + } + XFreeEventData(p_display, &e.xcookie); + } + } break; + + + + case ButtonPress: + { + XButtonEvent b = e.xbutton; + + Event event; + + event.type = EVENT_KEY; + event.key.pressed = true; + event.key.key_code = map_x11_button(b.button); + + Queue_Push(p_event_queue, event); + } break; + case ButtonRelease: + { + XButtonEvent b = e.xbutton; + Event event; + + event.type = EVENT_KEY; + event.key.pressed = false; + event.key.key_code = map_x11_button(b.button); + + Queue_Push(p_event_queue, event); + } break; + + + case KeyPress: + { + XKeyEvent k = e.xkey; + + Event event; + + event.type = EVENT_KEY; + event.key.pressed = true; + event.key.key_code = map_x11_keycode(k.keycode); + + Queue_Push(p_event_queue, event); + + if(!XFilterEvent(&e, p_window)) + { + char text[64]; + KeySym keysym; + static Status status = 0; + int text_length = Xutf8LookupString(p_xic, &k, text, 64, &keysym, &status); + //LOG(LOG_DEBUG, "X11 UTF8 lookup (length %d): %.*s, (%u)", text_length, text_length, text, text[0]); + if(text_length > 0) + { + Event event; + + event.type = EVENT_TEXT; + memcpy(event.text.data, text, text_length); + event.text.data[text_length] = '\0'; + + if(event.text.data[0] == '\r') + event.text.data[0] = '\n'; + + Queue_Push(p_event_queue, event); + } + } + } break; + case KeyRelease: + { + XKeyEvent k = e.xkey; + + // Check for repeated keypress + if(XEventsQueued(p_display, QueuedAfterReading)) + { + XEvent next_e; + XPeekEvent(p_display, &next_e); + if(next_e.type == KeyPress && next_e.xkey.time == k.time && next_e.xkey.keycode == k.keycode) + { + // Repeated keypress. Key wasn't actually released. + // It's the thing that the OS does when keep a key pressed and it prints multiple characters. + // Repeated keypresses are sent as a KeyRelease, immediatly followed by a KeyPress with the same time. + // So we eat both this KeyRelease and the next KeyPress + XNextEvent(p_display, &e); + + // We still use this event for text input + if(!XFilterEvent(&e, p_window)) + { + XKeyEvent k = e.xkey; + char text[64]; + KeySym keysym; + static Status status = 0; + int text_length = Xutf8LookupString(p_xic, &k, text, 64, &keysym, &status); + //LOG(LOG_DEBUG, "X11 UTF8 lookup (length %d): %.*s", text_length, text_length, text); + if(text_length > 0) + { + Event event; + + event.type = EVENT_TEXT; + memcpy(event.text.data, text, text_length); + event.text.data[text_length] = '\0'; + + if(event.text.data[0] == '\r') + event.text.data[0] = '\n'; + + Queue_Push(p_event_queue, event); + } + } + + break; + } + } + + Event event; + + event.type = EVENT_KEY; + event.key.pressed = false; + event.key.key_code = map_x11_keycode(k.keycode); + + Queue_Push(p_event_queue, event); + } break; + + + + case FocusIn: + { + //XFocusChangeEvent fc = e.xfocus; + Event event; + event.type = EVENT_FOCUS; + Queue_Push(p_event_queue, event); + + // Grab pointer if it was grabbed before (before leaving the window, now we are coming back to it + p_window_has_focus = true; + if(p_mouse_grabbed && p_cursor_is_inside_window) + p_mouse_grab_internal(p_mouse_grabbed); + } break; + case FocusOut: + { + //XFocusChangeEvent fc = e.xfocus; + Event event; + event.type = EVENT_UNFOCUS; + Queue_Push(p_event_queue, event); + + // Ungrab pointer when leaving the window (minimize, alt+tab, ...) + p_window_has_focus = false; + if(p_mouse_grabbed) + p_mouse_grab_internal(false); + } break; + + case EnterNotify: + { + // Add to event queue -> no + // Grab pointer if needed + p_cursor_is_inside_window = true; + if(p_mouse_grabbed && p_window_has_focus) + p_mouse_grab_internal(p_mouse_grabbed); + } break; + case LeaveNotify: + { + // Add to event queue -> no + // Ungrab pointer if needed + p_cursor_is_inside_window = false; + if(p_mouse_grabbed && p_window_has_focus) + p_mouse_grab_internal(false); + } break; + + + case Expose: + { + XExposeEvent ex = e.xexpose; + + Event event; + event.type = EVENT_RESIZE; + u32 new_width = ex.x + ex.width; + u32 new_height = ex.y + ex.height; + event.resize.width = new_width; + event.resize.height = new_height; + p_width = new_width; + p_height = new_height; + //LOG(LOG_DEBUG, "Expose event - x: %d y: %d width: %d height: %d count: %d", ex.x, ex.y, ex.width, ex.height, ex.count); + + Queue_Push(p_event_queue, event); + } break; + + + + default: + LOG(LOG_DEBUG, "Unrecognized X11 event: %d - %s", e.type, x11_event_type_str(e.type)); + // Discard unrecognized event + } + } +} + + +// Input +static void p_mouse_grab_internal(bool grab) +{ + if(grab) + { + // Set event mask + XIEventMask xi2_event_mask; + unsigned char mask[4] = { 0, 0, 0, 0 }; + + xi2_event_mask.deviceid = XIAllMasterDevices; + xi2_event_mask.mask_len = sizeof(mask); + xi2_event_mask.mask = mask; + XISetMask(mask, XI_RawMotion); + XISetMask(mask, XI_RawButtonPress); + XISetMask(mask, XI_RawButtonRelease); + + XISelectEvents(p_display, p_root_window, &xi2_event_mask, 1); + + // Cursor grabbing and hiding + #if 1 // In XInput 2.0 we will not get raw events if we grab the pointer. In XInput 2.2 we get raw events even when we grab the pointer, as expected. + unsigned event_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask | EnterWindowMask | LeaveWindowMask; + + XGrabPointer(p_display, p_window, False, event_mask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); + #endif + + + XDefineCursor(p_display, p_window, p_empty_cursor); + + XWarpPointer(p_display, None, p_window, 0, 0, 0, 0, p_width/2, p_height/2); + } + else + { + // Clear event mask + XIEventMask event_mask; + unsigned char mask[1] = { 0 }; + + event_mask.deviceid = XIAllMasterDevices; + event_mask.mask_len = 0; + event_mask.mask = mask; + + XISelectEvents(p_display, p_root_window, &event_mask, 1); + + // Undo Cursor grabbing and hiding + XUngrabPointer(p_display, CurrentTime); + XDefineCursor(p_display, p_window, None); + } +} + +void p_mouse_grab(bool grab) +{ + if(grab != p_mouse_grabbed) + { + p_mouse_grabbed = grab; + p_mouse_grab_internal(grab); + } +} + +bool p_next_event(Event *e) +{ + // If not in cache, process events until you find one + if(Queue_Size(p_event_queue) == 0) + { + p_events_process(); + } + + // If an event was found, return it + if(Queue_Size(p_event_queue) > 0) + { + *e = Queue_Pop(p_event_queue); + return true; + } + + return false; +} + + +// Audio +void p_audio_register_data_callback(p_audio_callback cb) +{ + p_audio_cb = cb; +} + +u32 p_audio_sample_rate() +{ + return _p_audio_sample_rate; +} diff --git a/code/linux_platform.h b/code/linux_platform.h new file mode 100644 index 0000000..c8ec81e --- /dev/null +++ b/code/linux_platform.h @@ -0,0 +1,12 @@ +/* +=== PLATFORM IMPLEMENTATION === +Here we define the structs that were left incomplete in platform.h + +=============================== +*/ +#include + +struct p_file +{ + FILE *handle; +}; diff --git a/code/main.cpp b/code/main.cpp new file mode 100644 index 0000000..cf0a843 --- /dev/null +++ b/code/main.cpp @@ -0,0 +1,315 @@ +#include +#include +#include + +#include "platform.h" +#include "render/render.h" +#include "enginestate.h" +#include "lib/types.h" +#include "lib/math.h" +#include "gui/gui.h" +#include "debug/logger.h" +#include "debug/log_viewer.h" +#include "gui/layout.h" +#include "stb_image.h" + +#include +#include +#include +#include + + +bool process_input(); // Returns true when the program needs to exit +void process_gui(); +void app_init(); +void app_deinit(); + +u32 seconds_to_duration_text(char *text, f64 seconds, bool show_millis = false) +{ + u32 written = 0; + u32 days = seconds / (24 * 3600); + seconds -= days * (24 * 3600); + u32 hours = seconds / 3600; + seconds -= hours * 3600; + u32 minutes = seconds / 60; + seconds -= minutes * 60; + + if(days) + written += sprintf(text + written, "%s%dd", (written ? " " : ""), days); + if(days || hours) + written += sprintf(text + written, "%s%dh", (written ? " " : ""), hours); + if(days || hours || minutes) + written += sprintf(text + written, "%s%dm", (written ? " " : ""), minutes); + if(days || hours || minutes || seconds) + { + if(show_millis) + written += sprintf(text + written, "%s%.3lfs", (written ? " " : ""), seconds); + else + written += sprintf(text + written, "%s%.0lfs", (written ? " " : ""), seconds); + } + + return written; +} + + +void load_image(const char *filename, u8 **data, s32 *width, s32 *height, s32 *channels) +{ + stbi_set_flip_vertically_on_load(false); + *data = stbi_load(filename, width, height, channels, 4); +} + + +int main(int argc, char *argv[]) +{ + bool status; + + LOG_INIT(); + p_init(true); + + LogViewer log_viewer = LogViewer::Init(&global_logger); + + p_window_open(); + p_window_name((char*)"Server Monitor"); + + r_init(); + gui_init(); + + log_viewer.Print_New_Messages_On_Console(); + + + app_init(); + + + f64 start = p_time(); + f64 prev_t = 0; + while(1) + { + // Engine + engine.time = p_time() - start; + engine.delta_t = engine.time - prev_t; + //LOG(LOG_INFO, "Frame time: %.3lf ms FPS: %.2lf", 1000*delta_t, 1/delta_t); + r_time_update(engine.time); + + // Input + bool exit = process_input(); + if(exit) + break; + + // GUI + r_framebuffer_select(&r_render_state.framebuffer_SCREEN); + r_clear({0,0,0,0}); + gui_frame_begin(engine.time); + + process_gui(); + + gui_frame_end(); + log_viewer.Print_New_Messages_On_Console(); + r_swap(); + prev_t = engine.time; + } + + app_deinit(); + gui_deinit(); + p_window_close(); + + LOG_DEINIT(); + p_deinit(); + return 0; +} + + + +bool process_input() +{ + // Events + Event event; + while(p_next_event(&event)) + { + gui_handle_event(&event); + + switch(event.type) + { + case EVENT_QUIT: { + return true; + }; + case EVENT_MOUSE_MOVE: + { + + } break; + case EVENT_KEY: + { + switch(event.key.key_code) + { + + default: { + // Other keys. Put default to suppress warnings + } break; + } + } break; + case EVENT_RESIZE: + { + //LOG(LOG_DEBUG, "New size: %u %u", signal.resize.width, signal.resize.height); + s32 width = event.resize.width; + s32 height = event.resize.height; + r_size_update(width, height); + + global_gui_state.default_context.width = width; + global_gui_state.default_context.height = height; + + engine.gui_scaling = minimum(width, height) * 0.03; + global_gui_state.default_context.style.font_size = engine.gui_scaling; + global_gui_state.default_context.style.button_radius = engine.gui_scaling / 10; + + } break; + case EVENT_FOCUS: + { + } break; + case EVENT_UNFOCUS: + { + } break; + } + } + + return false; +} + + + +NMClient *nmclient; +void app_init() +{ + nmclient = nm_client_new(NULL, NULL); +} + +void app_deinit() +{ +} + +void system_info_window() +{ + Gui_Layout_Grid layout = gui_layout_grid_create_by_divisions(v2{0,0}, v2{40,10}*engine.gui_scaling, 3, 6, 0.4*engine.gui_scaling); + gui_window_start(Rect{0.1*engine.gui_scaling, 0.1*engine.gui_scaling, layout.window_size.x, layout.window_size.y}, 0xabcdef01); + + // Hostname + struct utsname host_info; + uname(&host_info); + + char hostname[128] = "Server manager"; + if(host_info.nodename[0]) + strcpy(hostname, host_info.nodename); + char kernel[256]; + sprintf(kernel, "%s %s", host_info.sysname, host_info.release); + + // Clock + time_t time_now = time(NULL); + struct tm *time_info = localtime(&time_now); + char date_string[128]; + strftime(date_string, 128, "%a %e %b %Y", time_info); + char time_string[128]; + strftime(time_string, 128, "%H:%M:%S %Z", time_info); + + + gui_text_aligned(layout.cell(), hostname, GUI_ALIGN_LEFT); + gui_text_aligned(layout.cell(), time_string, GUI_ALIGN_CENTER); + gui_text_aligned(layout.cell(), date_string, GUI_ALIGN_RIGHT); + + + // Load, Memory, Uptime + struct sysinfo sys_info; + sysinfo(&sys_info); + char uptime[128] = "Uptime: "; seconds_to_duration_text(uptime + strlen("Uptime: "), sys_info.uptime); + f32 load_scale = 1.0f / (1 << SI_LOAD_SHIFT); + f32 loads[3] = { + load_scale * sys_info.loads[1], + load_scale * sys_info.loads[1], + load_scale * sys_info.loads[1] + }; + for(int i = 0; i < 3; i++) + loads[i] = round(load_scale * sys_info.loads[i] * 100) / 100; + char load[128]; sprintf(load, "Load: %.2f %.2f %.2f", loads[0], loads[1], loads[2]); + + int n_processors = get_nprocs(); + int n_processors_active = get_nprocs_conf(); + char processors[128]; sprintf(processors, "CPUs: %d/%d", n_processors_active, n_processors); + + u64 ram_total = sys_info.totalram * sys_info.mem_unit; + u64 ram_used = (sys_info.totalram - sys_info.freeram - sys_info.bufferram) * sys_info.mem_unit; + char ram[128]; sprintf(ram, "RAM: %.2f/%.2f GiB", ram_used / (1024.0*1024.0*1024.0), ram_total / (1024.0*1024.0*1024.0)); + + layout.row(2); + gui_text_aligned(layout.cell(), processors, GUI_ALIGN_LEFT); + gui_text_aligned(layout.cell(), load, GUI_ALIGN_CENTER); + gui_text_aligned(layout.cell(), uptime, GUI_ALIGN_RIGHT); + gui_text_aligned(layout.cell(), ram, GUI_ALIGN_LEFT); + + + gui_window_end(); +} + +void network_window() +{ + Gui_Layout_Grid layout = gui_layout_grid_create_by_divisions(v2{0,0}, v2{40,12}*engine.gui_scaling, 3, 7, 0.4*engine.gui_scaling); + gui_window_start(Rect{0.1*engine.gui_scaling, 11*engine.gui_scaling, layout.window_size.x, layout.window_size.y}, 0xabcdef02); + + const GPtrArray *devices = nm_client_get_devices(nmclient); + for(int i = 0; i < devices->len; i++) + { + NMDevice *device = (NMDevice*)devices->pdata[i]; + const char *device_name = nm_device_get_iface(device); + gui_button(layout.cell(), device_name); + + Gui_Context *ctx = &global_gui_state.default_context; + gui_id_stack_push(ctx, gui_id_from_pointer(ctx, device_name)); + + switch(nm_device_get_device_type(device)) + { + case NM_DEVICE_TYPE_ETHERNET: gui_button(layout.cell(), "ETHERNET"); break; + case NM_DEVICE_TYPE_WIFI: gui_button(layout.cell(), "WIFI"); break; + case NM_DEVICE_TYPE_TUN: gui_button(layout.cell(), "TAP or TUN"); break; + case NM_DEVICE_TYPE_BRIDGE: gui_button(layout.cell(), "BRIDGE"); break; + case NM_DEVICE_TYPE_VLAN: gui_button(layout.cell(), "VLAN"); break; + case NM_DEVICE_TYPE_WIREGUARD: gui_button(layout.cell(), "WIREGUARD"); break; + default: gui_button(layout.cell(), ""); break; + } + + switch(nm_device_get_state(device)) + { + case NM_DEVICE_STATE_UNKNOWN: gui_button(layout.cell(), "UNKNOWN"); break; + case NM_DEVICE_STATE_UNMANAGED: gui_button(layout.cell(), "UNMANAGED"); break; + case NM_DEVICE_STATE_UNAVAILABLE: gui_button(layout.cell(), "UNAVAILABLE"); break; + case NM_DEVICE_STATE_DISCONNECTED: gui_button(layout.cell(), "DISCONNECTED"); break; + case NM_DEVICE_STATE_PREPARE: gui_button(layout.cell(), "PREPARE"); break; + case NM_DEVICE_STATE_CONFIG: gui_button(layout.cell(), "CONFIG"); break; + case NM_DEVICE_STATE_NEED_AUTH: gui_button(layout.cell(), "NEED_AUTH"); break; + case NM_DEVICE_STATE_IP_CONFIG: gui_button(layout.cell(), "IP_CONFIG"); break; + case NM_DEVICE_STATE_IP_CHECK: gui_button(layout.cell(), "IP_CHECK"); break; + case NM_DEVICE_STATE_SECONDARIES: gui_button(layout.cell(), "SECONDARIES"); break; + case NM_DEVICE_STATE_ACTIVATED: gui_button(layout.cell(), "ACTIVATED"); break; + case NM_DEVICE_STATE_DEACTIVATING: gui_button(layout.cell(), "DEACTIVATING"); break; + case NM_DEVICE_STATE_FAILED: gui_button(layout.cell(), "FAILED"); break; + default: gui_button(layout.cell(), ""); break; + } + + gui_id_stack_pop(ctx); + layout.row(); + } + gui_window_end(); +} + +void vm_window() +{ + Gui_Layout_Grid layout = gui_layout_grid_create_by_divisions(v2{0,0}, v2{40,8}*engine.gui_scaling, 3, 7, 0.4*engine.gui_scaling); + gui_window_start(Rect{0.1*engine.gui_scaling, 24*engine.gui_scaling, layout.window_size.x, layout.window_size.y}, 0xabcdef03); + + + + gui_window_end(); +} + +void process_gui() +{ + g_main_context_iteration(NULL, false); + system_info_window(); + network_window(); + vm_window(); +} diff --git a/code/physics/base.h b/code/physics/base.h new file mode 100644 index 0000000..fa76a4e --- /dev/null +++ b/code/physics/base.h @@ -0,0 +1,17 @@ +#ifndef _PIUMA_PHYSICS_BASE_H_ +#define _PIUMA_PHYSICS_BASE_H_ + +#include "../lib/memory.h" + +/* Reassing phy_alloc and phy_free with your own alloc/free functions if you don't + * want to use malloc and free. + */ +extern alloc_t phy_alloc; +extern realloc_t phy_realloc; +extern free_t phy_free; + +#define PHY_ALLOC(type, size) (type *)phy_alloc(sizeof(type) * size) +#define PHY_REALLOC(type, ptr, size) (type *)phy_realloc(ptr, sizeof(type) * size) +#define PHY_FREE(ptr) phy_free(ptr) + +#endif diff --git a/code/physics/body.cpp b/code/physics/body.cpp new file mode 100644 index 0000000..65c6950 --- /dev/null +++ b/code/physics/body.cpp @@ -0,0 +1,28 @@ +#include "body.h" +#include "../lib/geometry.h" +#include + +Box phy_aabb_from_body(phy_body *body) +{ + Box aabb; + switch(body->shape) + { + case PHY_SHAPE_SPHERE: { + aabb.min = body->position - body->sphere.radius * v3{1,1,1}; + aabb.max = body->position + body->sphere.radius * v3{1,1,1}; + } break; + case PHY_SHAPE_BOX: { + v3 box[8]; build_cube_vertices(box); + m3 rotoscale = M3(rotation_v3(body->rotation) * scale_v3(body->box.dimensions)); + for(u32 i = 0; i < 8; i++) + box[i] = body->position + rotoscale * box[i]; + + aabb = box_from_point_cloud(box, 8); + } break; + case PHY_SHAPE_MESH: { + assert(false && "Yet to be implemented"); + } break; + default: { assert(false && "Unknown shape"); } + } + return aabb; +} diff --git a/code/physics/body.h b/code/physics/body.h new file mode 100644 index 0000000..41f145f --- /dev/null +++ b/code/physics/body.h @@ -0,0 +1,61 @@ +#ifndef _PIUMA_PHYSICS_OBJECT_H_ +#define _PIUMA_PHYSICS_OBJECT_H_ + +#include "../lib/math.h" +#include "../lib/geometry.h" + +enum phy_shape_type +{ + PHY_SHAPE_SPHERE, + PHY_SHAPE_BOX, + PHY_SHAPE_MESH, + + PHY_SHAPE_COUNT +}; + +struct phy_sphere +{ + f32 radius; +}; + +struct phy_box +{ + v3 dimensions; +}; + +struct phy_mesh +{ + // TODO +}; + + +struct phy_body +{ + v3 position; + v3 rotation; + + f32 mass; + v3 center_of_mass; + f32 gravity_multiplier; + f32 friction; + f32 bounciness; + + // Dynamics + v3 velocity; + v3 angular_velocity; + + // Collision + phy_shape_type shape; + union + { + phy_sphere sphere; + phy_box box; + phy_mesh mesh; + }; + Box aabb; +}; + + +Box phy_aabb_from_body(phy_body *body); + +#endif diff --git a/code/physics/collision.cpp b/code/physics/collision.cpp new file mode 100644 index 0000000..dcc7cb8 --- /dev/null +++ b/code/physics/collision.cpp @@ -0,0 +1,336 @@ +#include "collision.h" +#include "../lib/ds.h" +#include "../lib/geometry.h" +#include + + +void phy_collisions_broadphase(phy_world *world, phy_body_pair *pair_list, u32 max_pairs, u32 *num_pairs) +{ + // Update AABBs + for(u32 i = 0; i < world->bodies_count; i++) + { + phy_body *body = world->bodies + i; + body->aabb = phy_aabb_from_body(body); + } + + + u32 pairs_count = 0; + + for(u32 a = 0; a < world->bodies_count; a++) + { + phy_body *body_a = world->bodies + a; + for(u32 b = a + 1; b < world->bodies_count; b++) + { + phy_body *body_b = world->bodies + b; + + if(overlaps(body_a->aabb, body_b->aabb)) + { + // Add to list of possible collisions. + if(pairs_count < max_pairs) + { + pair_list[pairs_count] = phy_body_pair{ + .body_a = body_a, + .body_b = body_b + }; + } + pairs_count++; + } + } + } + + if(num_pairs) + *num_pairs = pairs_count; +} + + +// @Cleanup: put this in the right place +// @Performance: this is probably not a good way of doing this +void project_box_on_axis(phy_body *body, v3 axis, f32 *min, f32 *max) +{ + v3 points[8]; + build_cube_vertices(points); + + m4 transform = translation_v3(body->position) * rotation_v3(body->rotation) * scale_v3(body->box.dimensions); + + *min = *max = project_point_on_vector(axis, body->position); + for(int i = 0; i < 8; i++) + { + f32 projected = project_point_on_vector(axis, (transform * V4(points[i], 1)).xyz); + *min = minimum(*min, projected); + *max = maximum(*max, projected); + } +} + +f32 axis_range_distance(f32 a_min, f32 a_max, f32 b_min, f32 b_max) +{ + f32 a_range = a_max - a_min; + f32 a_center = a_min + a_range / 2; + f32 b_range = b_max - b_min; + f32 b_center = b_min + b_range / 2; + return fabs(a_center - b_center) - (a_range + b_range) / 2; +} + +bool sat_box_face_point_collision_test(phy_body *body_a, phy_body *body_b, v3 axis, f32 a_size_on_current_axis, f32 *depth, v3 *furthest_a, v3 *furthest_b) +{ + f32 a_middle = project_point_on_vector(axis, body_a->position); + f32 b_middle = project_point_on_vector(axis, body_b->position); + f32 direction = (b_middle - a_middle < 0) ? -1 : 1; + + f32 boundary = a_middle + direction * a_size_on_current_axis / 2; + f32 dist = +INFINITY; + v3 point; + + v3 points[8]; build_cube_vertices(points); + m4 transform = translation_v3(body_b->position) * rotation_v3(body_b->rotation) * scale_v3(body_b->box.dimensions); + + for(int i = 0; i < 8; i++) + { + v3 p = (transform * V4(points[i], 1)).xyz; + f32 projected = project_point_on_vector(axis, p); + f32 curr_dist = direction * (projected - boundary); + if(curr_dist < dist) + { + dist = curr_dist; + point = p; + } + } + + if(dist < 0) // Possible collision + { + v3 furthest_point_b = point; + v3 furthest_point_a = point + -dist * -direction * axis; + + *depth = -dist * direction; + *furthest_a = furthest_point_a; + *furthest_b = furthest_point_b; + return true; + } + // else Separated on this axis. No collision + return false; +} + +bool sat_box_collision_test(phy_body *body_a, phy_body *body_b, phy_collision *res_collision) +{ + // Separating axis test + // 3 + 3 = 6 face normals + // 3 * 3 = 9 right angle to pair of edges + v3 axes[15]; + m4 a_rotation = rotation_v3(body_a->rotation); + axes[0] = extract_column(a_rotation, 0).xyz; + axes[1] = extract_column(a_rotation, 1).xyz; + axes[2] = extract_column(a_rotation, 2).xyz; + m4 b_rotation = rotation_v3(body_b->rotation); + axes[3] = -extract_column(b_rotation, 0).xyz; + axes[4] = -extract_column(b_rotation, 1).xyz; + axes[5] = -extract_column(b_rotation, 2).xyz; + + for(int a = 0; a < 3; a++) + for(int b = 0; b < 3; b++) + { + (axes+6)[3*a + b] = cross(axes[a], axes[b + 3]); + } + + phy_collision collision; + collision.body_a = body_a; + collision.body_b = body_b; + collision.depth = +INFINITY; + + // Face axes: body a + for(int i = 0; i < 3; i++) + { + v3 axis = axes[i]; + f32 depth; v3 furthest_point_a, furthest_point_b; + bool collides = sat_box_face_point_collision_test(body_a, body_b, axis, body_a->box.dimensions.E[i], &depth, &furthest_point_a, &furthest_point_b); + + if(!collides) + return false; + + if(abs(depth) < abs(collision.depth)) + { + collision.depth = depth; + collision.furthest_point_a = furthest_point_a; + collision.furthest_point_b = furthest_point_b; + collision.best_separation_direction = axis; + } + } + // Face axes: body b + for(int i = 0; i < 3; i++) + { + v3 axis = axes[3 + i]; + f32 depth; v3 furthest_point_a, furthest_point_b; + bool collides = sat_box_face_point_collision_test(body_b, body_a, axis, body_b->box.dimensions.E[i], &depth, &furthest_point_b, &furthest_point_a); + + if(!collides) + return false; + + if(abs(depth) < abs(collision.depth)) + { + collision.depth = depth; + collision.furthest_point_a = furthest_point_a; + collision.furthest_point_b = furthest_point_b; + collision.best_separation_direction = -axis; + } + } + + // @TODO: edge-edge + + *res_collision = collision; + return true; +} + +void phy_collisions_detection(phy_world *world, phy_body_pair *pair_list, u32 num_pairs, phy_collision *collision_list, u32 max_collisions, u32 *num_collisions) +{ + u32 n_collisions = 0; + for(u32 i = 0; i < num_pairs; i++) + { + phy_body *body_a = pair_list[i].body_a; + phy_body *body_b = pair_list[i].body_b; + + if(body_b->shape < body_a->shape) + swap(body_b, body_a); + + + switch(body_a->shape) + { + case PHY_SHAPE_SPHERE: + { + switch(body_b->shape) + { + case PHY_SHAPE_SPHERE: + { + f32 dist = distance(body_a->position, body_b->position); + f32 radius_sum = body_a->sphere.radius + body_b->sphere.radius; + if(dist <= radius_sum) + { + phy_collision collision; + + collision.body_a = body_a; + collision.body_b = body_b; + collision.depth = radius_sum - dist; + v3 direction = normalize(body_b->position - body_a->position); + collision.furthest_point_a = body_a->position + (body_a->sphere.radius - collision.depth) * direction; + collision.furthest_point_b = body_b->position + (body_b->sphere.radius - collision.depth) * -direction; + collision.best_separation_direction = direction; + + if(n_collisions < max_collisions) + collision_list[n_collisions] = collision; + n_collisions++; + } + } break; + case PHY_SHAPE_BOX: + { + // Use box's cordinate space + m4 box_b_coord_space_transform = rotation_v3(-body_b->rotation) * translation_v3(-body_b->position); + + v4 sphere_center4 = box_b_coord_space_transform * V4(body_a->position, 1.0); + v3 sphere_center = sphere_center4.xyz / sphere_center4.w; + + Box box = { + .min = -0.5*body_b->box.dimensions, + .max = 0.5*body_b->box.dimensions + }; + + v3 closest_point; + f32 dist = box_SDF(box, sphere_center, &closest_point); + + if(dist - body_a->sphere.radius <= 0) + { + phy_collision collision; + + collision.body_a = body_a; + collision.body_b = body_b; + collision.depth = dist - body_a->sphere.radius; + v3 direction = normalize(closest_point - body_a ->position); + collision.furthest_point_a = body_a->position + (body_a->sphere.radius - collision.depth) * direction; + collision.furthest_point_b = body_a->position + body_a->sphere.radius * direction; + collision.best_separation_direction = direction; + + if(n_collisions < max_collisions) + collision_list[n_collisions] = collision; + n_collisions++; + } + + } break; + case PHY_SHAPE_MESH: + { + assert(false && "Unmanaged PHY_SHAPE pair"); + } break; + default: + { + assert(false && "Unmanaged PHY_SHAPE pair"); + } break; + } + } break; + case PHY_SHAPE_BOX: + { + switch(body_b->shape) + { + // case PHY_SHAPE_SPHERE: // Already managed + case PHY_SHAPE_BOX: + { + phy_collision collision; + bool collides = sat_box_collision_test(body_a, body_b, &collision); + if(collides) + { + if(n_collisions < max_collisions) + collision_list[n_collisions] = collision; + n_collisions++; + } + } break; + case PHY_SHAPE_MESH: + { + assert(false && "Unmanaged PHY_SHAPE pair"); + } break; + default: + { + assert(false && "Unmanaged PHY_SHAPE pair"); + } break; + } + } break; + case PHY_SHAPE_MESH: + { + switch(body_b->shape) + { + // case PHY_SHAPE_SPHERE: // Already managed + // case PHY_SHAPE_BOX: // Already managed + case PHY_SHAPE_MESH: + { + assert(false && "Unmanaged PHY_SHAPE pair"); + } break; + default: + { + assert(false && "Unmanaged PHY_SHAPE pair"); + } break; + } + } break; + default: + { + assert(false && "Unmanaged PHY_SHAPE"); + } break; + } + } + *num_collisions = n_collisions; +} + +void phy_collisions_resolution(phy_world *world, phy_collision *collision_list, u32 num_collisions) +{ + + for(u32 i = 0; i < num_collisions; i++) + { + phy_body *body_a = collision_list[i].body_a; + phy_body *body_b = collision_list[i].body_b; + v3 separation_direction = collision_list[i].best_separation_direction; + f32 depth = collision_list[i].depth; + + if(body_a->mass != 0) // Not a static body + { + body_a->position += -separation_direction * depth;//-normalize(body_a->velocity) * dot(normalize(body_a->velocity), separation_direction * depth); + body_a->velocity = {0,0,0};//length(body_a->velocity) * separation_direction * body_a->bounciness; + } + if(body_b->mass != 0) // Not a static body + { + body_b->position += separation_direction * depth;//-normalize(body_b->velocity) * dot(normalize(body_b->velocity), separation_direction * depth); + body_b->velocity = {0,0,0};//length(body_a->velocity) * -separation_direction * body_b->bounciness; + } + } +} diff --git a/code/physics/collision.h b/code/physics/collision.h new file mode 100644 index 0000000..86fe8cc --- /dev/null +++ b/code/physics/collision.h @@ -0,0 +1,35 @@ +#ifndef _PIUMA_PHYSICS_COLLISION_H_ +#define _PIUMA_PHYSICS_COLLISION_H_ + +#include "world.h" + +struct phy_body_pair +{ + phy_body *body_a; + phy_body *body_b; +}; + +struct phy_collision +{ + phy_body *body_a; + phy_body *body_b; + + f32 depth; + v3 furthest_point_a; + v3 furthest_point_b; + v3 best_separation_direction; +}; + + +// Generates possibile collision pairs and saves it in the memory pointed by pair_list, up to a maximum of max_pairs. The number of possible collisions is returned in num_pairs (might be >= max_pairs). +void phy_collisions_broadphase(phy_world *world, phy_body_pair *pair_list, u32 max_pairs, u32 *num_pairs); + +// Generates list of collisions and saves it in the memory pointed by collision_list, up to a maximum of max_pairs. The number of collisions is returned in num_collisions (might be >= max_collisions). +// Uses pair_list (with size num_pairs) as a list of possible collisions (computed with a faster algorithm). +void phy_collisions_detection(phy_world *world, phy_body_pair *pair_list, u32 num_pairs, phy_collision *collision_list, u32 max_collisions, u32 *num_collisions); + +// Modifies world state based on collision_list +void phy_collisions_resolution(phy_world *world, phy_collision *collision_list, u32 num_collisions); + + +#endif diff --git a/code/physics/physics.cpp b/code/physics/physics.cpp new file mode 100644 index 0000000..0820120 --- /dev/null +++ b/code/physics/physics.cpp @@ -0,0 +1,25 @@ +#include "physics.h" + +#include + +alloc_t phy_alloc = malloc; +realloc_t phy_realloc = realloc; +free_t phy_free = free; + + +void phy_init() +{ + phy_init(malloc, realloc, free); +} + +void phy_init(alloc_t alloc, realloc_t realloc, free_t free) +{ + phy_alloc = alloc; + phy_realloc = realloc; + phy_free = free; +} + +void phy_deinit() +{ + +} diff --git a/code/physics/physics.h b/code/physics/physics.h new file mode 100644 index 0000000..36410e8 --- /dev/null +++ b/code/physics/physics.h @@ -0,0 +1,16 @@ +#ifndef _PIUMA_PHYSICS_PHYSICS_H_ +#define _PIUMA_PHYSICS_PHYSICS_H_ + +#include "base.h" +#include "body.h" +#include "world.h" +#include "simulation.h" +#include "collision.h" + +// Initialization +void phy_init(); +void phy_init(alloc_t alloc, realloc_t realloc, free_t free); +void phy_deinit(); + + +#endif diff --git a/code/physics/simulation.cpp b/code/physics/simulation.cpp new file mode 100644 index 0000000..0b7079e --- /dev/null +++ b/code/physics/simulation.cpp @@ -0,0 +1,40 @@ +#include "base.h" +#include "simulation.h" +#include "../lib/geometry.h" +#include "collision.h" + +void phy_integrate(phy_world *world, f64 delta_t) +{ + for(u32 i = 0; i < world->bodies_count; i++) + { + phy_body *body = world->bodies + i; + + body->velocity += world->gravity * body->gravity_multiplier * delta_t; + + body->position += body->velocity * delta_t; + body->rotation += body->angular_velocity * delta_t; + } +} + + +void phy_simulate(phy_world *world, f64 delta_t) +{ + // @Performance: Grouping for optimizations (active/inactive, constraints) + + phy_integrate(world, delta_t); + + u32 max_pairs = 1000; // @Correctness: this should be dynamic and should reuse memory when possible + u32 num_pairs = 0; + phy_body_pair *pair_list = PHY_ALLOC(phy_body_pair, max_pairs); + phy_collisions_broadphase(world, pair_list, max_pairs, &num_pairs); + + u32 max_collisions = 1000; + u32 num_collisions = 0; + phy_collision *collision_list = PHY_ALLOC(phy_collision, max_collisions); + phy_collisions_detection(world, pair_list, minimum(max_pairs, num_pairs), collision_list, max_collisions, &num_collisions); + + phy_collisions_resolution(world, collision_list, minimum(max_collisions, num_collisions)); + + PHY_FREE(pair_list); + PHY_FREE(collision_list); +} diff --git a/code/physics/simulation.h b/code/physics/simulation.h new file mode 100644 index 0000000..dbea363 --- /dev/null +++ b/code/physics/simulation.h @@ -0,0 +1,9 @@ +#ifndef _PIUMA_PHYSICS_SIMULATION_H_ +#define _PIUMA_PHYSICS_SIMULATION_H_ + +#include "world.h" + +void phy_integrate(phy_world *world, f64 delta_t); +void phy_simulate(phy_world *world, f64 delta_t); + +#endif diff --git a/code/physics/world.cpp b/code/physics/world.cpp new file mode 100644 index 0000000..42789bf --- /dev/null +++ b/code/physics/world.cpp @@ -0,0 +1,72 @@ +#include "world.h" +#include "base.h" + + +phy_world *phy_create_world(v3 gravity) +{ + phy_world *world = PHY_ALLOC(phy_world, 1); + world->bodies = NULL; + world->bodies_count = 0; + world->gravity = gravity; + return world; +} + +void phy_destroy_world(phy_world *world) +{ + PHY_FREE(world->bodies); + world->bodies_count = 0; +} + +phy_body_offset phy_new_body(phy_world *world) +{ + phy_body_offset offset = world->bodies_count; + world->bodies_count++; + world->bodies = PHY_REALLOC(phy_body, world->bodies, world->bodies_count); + return offset; +} + +void phy_body_init_default(phy_body *body) +{ + body->position = {0, 0, 0}; + body->rotation = {0, 0, 0}; + body->mass = 0; + body->center_of_mass = {0, 0, 0}; + + body->velocity = {0, 0, 0}; + body->angular_velocity = {0, 0, 0}; + + body->gravity_multiplier = 1; + + body->friction = 0; + body->bounciness = 1; +} + +phy_body_offset phy_create_box(phy_world *world, v3 position, v3 rotation, f32 mass, v3 dimensions) +{ + phy_body_offset offset = phy_new_body(world); + phy_body *body = PHY_BODY(world, offset); + + phy_body_init_default(body); + body->position = position; + body->rotation = rotation; + body->mass = mass; + body->shape = PHY_SHAPE_BOX; + body->box.dimensions = dimensions; + + return offset; +} + +phy_body_offset phy_create_sphere(phy_world *world, v3 position, v3 rotation, f32 mass, f32 radius) +{ + phy_body_offset offset = phy_new_body(world); + phy_body *body = PHY_BODY(world, offset); + + phy_body_init_default(body); + body->position = position; + body->rotation = rotation; + body->mass = mass; + body->shape = PHY_SHAPE_SPHERE; + body->sphere.radius = radius; + + return offset; +} diff --git a/code/physics/world.h b/code/physics/world.h new file mode 100644 index 0000000..f96895c --- /dev/null +++ b/code/physics/world.h @@ -0,0 +1,23 @@ +#ifndef _PIUMA_PHYSICS_WORLD_H_ +#define _PIUMA_PHYSICS_WORLD_H_ + +#include "body.h" + +struct phy_world +{ + phy_body *bodies; + u32 bodies_count; + + v3 gravity; +}; + +typedef u32 phy_body_offset; +#define PHY_BODY(world, offset) (world->bodies + offset) + +phy_world *phy_create_world(v3 gravity); +void phy_destroy_world(phy_world *world); + +phy_body_offset phy_create_box(phy_world *world, v3 position, v3 rotation, f32 mass, v3 dimensions); +phy_body_offset phy_create_sphere(phy_world *world, v3 position, v3 rotation, f32 mass, f32 radius); + +#endif diff --git a/code/platform.h b/code/platform.h new file mode 100644 index 0000000..4f2c106 --- /dev/null +++ b/code/platform.h @@ -0,0 +1,92 @@ +#ifndef _PIUMA_PLATFORM_H_ +#define _PIUMA_PLATFORM_H_ +/* +=== PLATFORM INTERFACE === +Every function or struct must be implemented in the code for each platform + +========================== +*/ + +#include "lib/types.h" +#include "lib/event.h" + +// @Feature: Initialization structures with preferred initial state (window resolution, fullcreen) + +// Generic platform initialization +void p_init(bool capture_os_signals = false); +void p_deinit(); + +// Memory +void * p_alloc(u64 size); +void * p_realloc(void *ptr, u64 new_size); +void p_free(void *ptr); + +// File IO +#define P_FILE_CREATE_IF_NOT_EXISTS 1 + +struct p_file; // Defined by specific platform implementation + +bool p_file_init(p_file *file, const char *filename, b32 flags = 0); +u64 p_file_size(p_file *file); +bool p_file_read(p_file *file, Buffer *buf, u64 max_size); +bool p_file_write(p_file *file, u8 *data, u64 size); +// @Performance: add append + write at position. Maybe not needed +void p_file_deinit(p_file *file); + +// Timers +f64 p_time(); // Returns seconds +void p_wait(f64 milliseconds); + +// Windowing +// We suppose we only need 1 window. Every GL command will render to it. +void p_window_open(); +void p_window_name(char *name); +void p_window_resize(u32 width, u32 height, bool fullscreen = false); +void p_window_dimensions(u32 *width, u32 *height); +void p_window_close(); + +// Graphics +void p_graphics_swap_interval(s32 frames); // -1 for Adaptive Sync, 0 for no interval, >0 to wait 'frames' frames between each swap +void p_graphics_swap(); + +// Input (keyboard / mouse / controller) +void p_mouse_grab(bool grab); + +// Events +bool p_next_event(Event *e); + +// Audio +struct p_audio_buffer; +typedef void (*p_audio_callback)(p_audio_buffer *); + +void p_audio_register_data_callback(p_audio_callback cb); +u32 p_audio_sample_rate(); + + +// Threads +// @Feature: OS threads and syncronization + + + +// Audio structs +struct p_audio_sample +{ + f32 left; + f32 right; +}; + +struct p_audio_buffer +{ + p_audio_sample *samples; + u64 size; +}; + + + +#if defined(__linux__) + #include "linux_platform.h" +#else + #error "Platform not supported" +#endif + +#endif diff --git a/code/render/2d.cpp b/code/render/2d.cpp new file mode 100644 index 0000000..069f142 --- /dev/null +++ b/code/render/2d.cpp @@ -0,0 +1,382 @@ +#include "2d.h" +#include "state.h" +#include "../debug/logger.h" + +static const u32 PIXELS_PER_SEGMENT = 3; + +// Internal use functions +static void set_texture_in_shader(r_shader *shader, r_texture *texture, v4 color = {1,1,1,1}, u32 texture_index = 0); + +// Immediate functions +void r_2d_immediate_segment(v2 a, v2 b, v4 a_color, v4 b_color, f32 thickness) +{ + glDisable(GL_DEPTH_TEST); + + // Shader + glUseProgram(r_render_state.shader_2d.id); + glUniform1i(r_render_state.shader_2d.has_texture[0], 0); + + // Vertex buffer data + GLuint gl_VAO, gl_VBO; + glGenVertexArrays(1, &gl_VAO); + glBindVertexArray(gl_VAO); + glGenBuffers(1, &gl_VBO); + glBindBuffer(GL_ARRAY_BUFFER, gl_VBO); + + f32 data[12] = { a.x, a.y, b.x, b.y, a_color.r, a_color.g, a_color.b, a_color.a, b_color.r, b_color.g, b_color.b, b_color.a }; + glBufferData(GL_ARRAY_BUFFER, 2*sizeof(v2)+2*sizeof(v4), data, GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(v4), (void*)(2*sizeof(v2))); + + // Draw + glLineWidth(thickness); + glDrawArrays(GL_LINES, 0, 2); + + // Deinit + glDeleteBuffers(1, &gl_VBO); + glDeleteVertexArrays(1, &gl_VAO); + + glEnable(GL_DEPTH_TEST); +} + +void r_2d_immediate_polygonal_chain(u64 count, v2 *vertices, v4 color, f32 thickness) +{ + glDisable(GL_DEPTH_TEST); + + // Shader + glUseProgram(r_render_state.shader_2d.id); + glUniform1i(r_render_state.shader_2d.has_texture[0], 0); + + // Vertex buffer data + GLuint gl_VAO, gl_VBO; + glGenVertexArrays(1, &gl_VAO); + glBindVertexArray(gl_VAO); + glGenBuffers(1, &gl_VBO); + glBindBuffer(GL_ARRAY_BUFFER, gl_VBO); + + glBufferData(GL_ARRAY_BUFFER, count*sizeof(v2), vertices, GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0); + //glDisableVertexAttribArray(1); + glVertexAttrib4f(1, color.r, color.g, color.b, color.a); + + // Draw + glLineWidth(thickness); + glDrawArrays(GL_LINE_STRIP, 0, count); + + // Deinit + glDeleteBuffers(1, &gl_VBO); + glDeleteVertexArrays(1, &gl_VAO); + + glEnable(GL_DEPTH_TEST); +} + +void r_2d_immediate_triangle(v2 a, v2 b, v2 c, v4 a_color, v4 b_color, v4 c_color, v2 a_uv, v2 b_uv, v2 c_uv, r_texture *texture) +{ + glDisable(GL_DEPTH_TEST); + + // Shader + glUseProgram(r_render_state.shader_2d.id); + // Texture + set_texture_in_shader(&r_render_state.shader_2d, texture); + + // Vertex buffer data + GLuint gl_VAO, gl_VBO; + glGenVertexArrays(1, &gl_VAO); + glBindVertexArray(gl_VAO); + glGenBuffers(1, &gl_VBO); + glBindBuffer(GL_ARRAY_BUFFER, gl_VBO); + + f32 data[24] = { + a.x, a.y, b.x, b.y, c.x, c.y, + a_color.r, a_color.g, a_color.b, a_color.a, + b_color.r, b_color.g, b_color.b, b_color.a, + c_color.r, c_color.g, c_color.b, c_color.a, + a_uv.x, a_uv.y, b_uv.x, b_uv.y, c_uv.x, c_uv.y + }; + glBufferData(GL_ARRAY_BUFFER, 3*sizeof(v2)+3*sizeof(v4)+3*sizeof(v2), data, GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(v4), (void*)(3*sizeof(v2))); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)(3*sizeof(v2)+3*sizeof(v4))); + + // Draw + glDrawArrays(GL_TRIANGLES, 0, 3); + + // Deinit + glDeleteBuffers(1, &gl_VBO); + glDeleteVertexArrays(1, &gl_VAO); + + glEnable(GL_DEPTH_TEST); +} + +void r_2d_immediate_quad(v2 a, v2 b, v2 c, v2 d, v4 color, v2 a_uv, v2 b_uv, v2 c_uv, v2 d_uv, r_texture *texture) +{ + glDisable(GL_DEPTH_TEST); + + // Shader + glUseProgram(r_render_state.shader_2d.id); + // Texture + set_texture_in_shader(&r_render_state.shader_2d, texture); + + // Vertex buffer data + GLuint gl_VAO, gl_VBO; + glGenVertexArrays(1, &gl_VAO); + glBindVertexArray(gl_VAO); + glGenBuffers(1, &gl_VBO); + glBindBuffer(GL_ARRAY_BUFFER, gl_VBO); + + f32 data[16] = { + a.x, a.y, b.x, b.y, d.x, d.y, c.x, c.y, + a_uv.x, a_uv.y, b_uv.x, b_uv.y, d_uv.x, d_uv.y, c_uv.x, c_uv.y + }; + glBufferData(GL_ARRAY_BUFFER, 4*sizeof(v2)+4*sizeof(v2), data, GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0); + //glDisableVertexAttribArray(1); + glVertexAttrib4f(1, color.r, color.g, color.b, color.a); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)(4*sizeof(v2))); + + // Draw + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + // Deinit + glDeleteBuffers(1, &gl_VBO); + glDeleteVertexArrays(1, &gl_VAO); + + glEnable(GL_DEPTH_TEST); +} + +void r_2d_immediate_rectangle(Rect r, v4 color, Rect uv, r_texture *texture) +{ + v2 a = r.position + v2{r.w, 0 }; + v2 b = r.position + v2{0 , 0 }; + v2 c = r.position + v2{0 , r.h}; + v2 d = r.position + v2{r.w, r.h}; + v2 a_uv = uv.position + v2{uv.w, 0 }; + v2 b_uv = uv.position + v2{0 , 0 }; + v2 c_uv = uv.position + v2{0 , uv.h}; + v2 d_uv = uv.position + v2{uv.w, uv.h}; + r_2d_immediate_quad(a, b, c, d, color, a_uv, b_uv, c_uv, d_uv, texture); +} + +void r_2d_immediate_rounded_rectangle(Rect r, f32 radius, v4 color) +{ + radius = clamp(0, minimum(abs(r.w), abs(r.h)) / 2, radius); + + // Should compute PIXELS_PER_SEGMENT from the size of the radius + u32 num_of_segments = floor(0.5 + radius * TAU * 0.25 / PIXELS_PER_SEGMENT); + + // Split into 9 sections: + // - 5 quads (center, top, bottom, left, right) + // - 4 semicircles (corners) + // Inner vertices (CCW starting from 1st quadrant/upper-right) + v2 i0, i1, i2, i3; + i0.x = r.x + r.w - radius; + i0.y = r.y + radius; + i1.x = r.x + radius; + i1.y = r.y + radius; + i2.x = r.x + radius; + i2.y = r.y + r.h - radius; + i3.x = r.x + r.w - radius; + i3.y = r.y + r.h - radius; + + // Outer vertices + v2 o0, o1, o2, o3, o4, o5, o6, o7; + o0.x = i0.x; + o0.y = i0.y - radius; + o1.x = i1.x; + o1.y = i1.y - radius; + o2.x = i1.x - radius; + o2.y = i1.y; + o3.x = i2.x - radius; + o3.y = i2.y; + o4.x = i2.x; + o4.y = i2.y + radius; + o5.x = i3.x; + o5.y = i3.y + radius; + o6.x = i3.x + radius; + o6.y = i3.y; + o7.x = i0.x + radius; + o7.y = i0.y; + + // Reserve space for vertices + u32 vertices_count = 30; // 5 quads, specified by 2 triangles each = 5 quads * 2 triangles * 3 vertices = 30 vertices + vertices_count += num_of_segments * 12; // Add corner semicircles = 4 corners * N triangles * 3 vertices = N * 12 + + + // Build 5 quads + v2 vertices[vertices_count] = { + i0, i1, i2, i0, i2, i3, // Center quad: i0, i1, i2, i3 + o0, o1, i1, o0, i1, i0, // Top quad: o0, o1, i1, i0 + i1, o2, o3, i1, o3, i2, // Left quad: i1, o2, o3, i2 + i3, i2, o4, i3, o4, o5, // Bottom quad: i3, i2, o4, o5 + o7, i0, i3, o7, i3, o6 // Right quad: o7, i0, i3, o6 + }; + u32 corner_offset = 30; + // Corner semicircles + f32 factor = TAU * .25 / num_of_segments; + v2 inner_vertices[4] = {i0, i1, i2, i3}; + for(u32 quadrant = 0; quadrant < 4; quadrant++) + { + for(u32 i = quadrant*num_of_segments; i < (quadrant+1)*num_of_segments; i++) + { + v2 inner = inner_vertices[quadrant]; + v2 a = inner + radius * v2{cos( i * factor), -sin( i * factor)}; + v2 b = inner + radius * v2{cos((i+1) * factor), -sin((i+1) * factor)}; + vertices[corner_offset + 3*i + 0] = inner; + vertices[corner_offset + 3*i + 1] = a; + vertices[corner_offset + 3*i + 2] = b; + } + } + + r_2d_immediate_mesh(vertices_count, vertices, color, NULL, NULL); +} + +void r_2d_immediate_mesh(u64 count, v2 *vertices, v4 color, v2 *uvs, r_texture *texture) +{ + glDisable(GL_DEPTH_TEST); + + // Shader + glUseProgram(r_render_state.shader_2d.id); + // Texture + set_texture_in_shader(&r_render_state.shader_2d, texture); + + // Vertex buffer data + GLuint gl_VAO, gl_vertices, gl_uvs; + glGenVertexArrays(1, &gl_VAO); + glBindVertexArray(gl_VAO); + glGenBuffers(1, &gl_vertices); + glGenBuffers(1, &gl_uvs); + + glBindBuffer(GL_ARRAY_BUFFER, gl_vertices); + glBufferData(GL_ARRAY_BUFFER, count * sizeof(v2), vertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0); + //glDisableVertexAttribArray(1); + glVertexAttrib4f(1, color.r, color.g, color.b, color.a); + glBindBuffer(GL_ARRAY_BUFFER, gl_uvs); + glBufferData(GL_ARRAY_BUFFER, count * sizeof(v2), uvs , GL_STATIC_DRAW); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0); + + // Draw + glDrawArrays(GL_TRIANGLES, 0, count); + + // Deinitvoid r_2d_draw_mesh(r_2d_mesh *mesh, r_texture *texture); + glDeleteBuffers(1, &gl_vertices); + glDeleteBuffers(1, &gl_uvs); + glDeleteVertexArrays(1, &gl_VAO); + + glEnable(GL_DEPTH_TEST); +} + + + +void r_2d_immediate_rectangle_outline(Rect r, v4 color, f32 thickness) +{ + v2 vertices[5]; + vertices[0] = r.position + v2{ 0, 0}; + vertices[1] = r.position + v2{ 0, r.h}; + vertices[2] = r.position + v2{r.w, r.h}; + vertices[3] = r.position + v2{r.w, 0}; + vertices[4] = r.position + v2{ 0, 0}; + r_2d_immediate_polygonal_chain(5, vertices, color, thickness); +} + +void r_2d_immediate_rounded_rectangle_outline(Rect r, f32 radius, v4 color, f32 thickness) +{ + radius = clamp(0, minimum(abs(r.w), abs(r.h)) / 2, radius); + + // Should compute PIXELS_PER_SEGMENT from the size of the radius + u32 num_of_segments = floor(0.5 + radius * TAU * 0.25 / PIXELS_PER_SEGMENT); + + // Split into 9 sections: + // - 5 quads (center, top, bottom, left, right) + // - 4 semicircles (corners) + // Inner vertices (CCW starting from 1st quadrant/upper-right) + v2 i0, i1, i2, i3; + i0.x = r.x + r.w - radius; + i0.y = r.y + radius; + i1.x = r.x + radius; + i1.y = r.y + radius; + i2.x = r.x + radius; + i2.y = r.y + r.h - radius; + i3.x = r.x + r.w - radius; + i3.y = r.y + r.h - radius; + + // Reserve space for vertices + u32 vertices_count = 1 + 4 + 4*num_of_segments; // Starting vertex (1) + one for each side (4) + one for each segment (4*num_of_segments) + + v2 inner_vertices[4] = {i0, i1, i2, i3}; + + v2 vertices[vertices_count] = { + i3 + v2{radius, 0} // Starting vertex + }; + u32 v_index = 1; + + // Corner semicircles + f32 factor = TAU * .25 / num_of_segments; + for(u32 quadrant = 0; quadrant < 4; quadrant++) + { + v2 inner = inner_vertices[quadrant]; + for(u32 i = quadrant*num_of_segments; i < (quadrant+1)*num_of_segments; i++) + { + v2 a = inner + radius * v2{cos( i * factor), -sin( i * factor)}; + vertices[v_index] = a; + v_index++; + } + vertices[v_index] = inner + radius * v2{cos( (quadrant+1)*num_of_segments * factor), -sin( (quadrant+1)*num_of_segments * factor)}; + v_index++; + } + + r_2d_immediate_polygonal_chain(vertices_count, vertices, color, thickness); +} + + + +void r_2d_draw_mesh(r_2d_mesh *mesh, r_texture *texture) +{ + glDisable(GL_DEPTH_TEST); + + // Shader + glUseProgram(r_render_state.shader_2d.id); + // Texture + set_texture_in_shader(&r_render_state.shader_2d, texture); + + // Draw + glBindVertexArray(mesh->gl_VAO); + glDrawArrays(GL_TRIANGLES, 0, mesh->count); + + glEnable(GL_DEPTH_TEST); +} + + + +// Internal use functions +static void set_texture_in_shader(r_shader *shader, r_texture *texture, v4 color, u32 texture_index) +{ + // Remember to call glUseProgram before using this + if(texture) + { + glUniform1i(shader->has_texture[texture_index], 1); + glUniform1i(shader->texture[texture_index], 0); + glUniform1i(shader->texture_channels[texture_index], r_texture_channels(texture)); + glActiveTexture(GL_TEXTURE0 + texture_index); + glBindTexture(GL_TEXTURE_2D, texture->gl_id); + glUniform4f(shader->color[texture_index], color.r, color.g, color.b, color.a); + } + else + { + glUniform1i(shader->has_texture[texture_index], 0); + } +} diff --git a/code/render/2d.h b/code/render/2d.h new file mode 100644 index 0000000..070c2a4 --- /dev/null +++ b/code/render/2d.h @@ -0,0 +1,27 @@ +#ifndef _PIUMA_RENDER_2D_H_ +#define _PIUMA_RENDER_2D_H_ + +#include "../lib/types.h" +#include "../lib/math.h" +#include "../lib/geometry.h" +#include "primitives.h" + +// Immediate functions: less efficient but easy to use +void r_2d_immediate_segment(v2 a, v2 b, v4 a_color, v4 b_color, f32 thickness = 1.0f); +void r_2d_immediate_polygonal_chain(u64 count, v2 *vertices, v4 color, f32 thickness = 1.0f); +void r_2d_immediate_triangle(v2 a, v2 b, v2 c, v4 a_color, v4 b_color, v4 c_color, v2 a_uv = {0,0}, v2 b_uv = {0,0}, v2 c_uv = {0,0}, r_texture *texture = NULL); +void r_2d_immediate_quad(v2 a, v2 b, v2 c, v2 d, v4 color, v2 a_uv = {0,0}, v2 b_uv = {0,0}, v2 c_uv = {0,0}, v2 d_uv = {0,0}, r_texture *texture = NULL); +void r_2d_immediate_rectangle(Rect r, v4 color, Rect uv = {0,0,1,1}, r_texture *texture = NULL); +void r_2d_immediate_rounded_rectangle(Rect r, f32 radius, v4 color); +void r_2d_immediate_mesh(u64 count, v2 *vertices, v4 color, v2 *uvs = NULL, r_texture *texture = NULL); + +void r_2d_immediate_rectangle_outline(Rect r, v4 color, f32 thickness = 1.0f); +void r_2d_immediate_rounded_rectangle_outline(Rect r, f32 radius, v4 color, f32 thickness = 1.0f); + +// Draw functions: usual interface (create objects / send to gpu, draw call, remove from gpu) +void r_2d_draw_mesh(r_2d_mesh *mesh, r_texture *texture); +// @Feature: @Performance: something for text rendering (a lot of quads/rects) +// Maybe we could send to the gpu the following: texture of the characters, array of uvs for indexing the texture, character position + index of its uv. + + +#endif diff --git a/code/render/gl_helpers.h b/code/render/gl_helpers.h new file mode 100644 index 0000000..e16e6ed --- /dev/null +++ b/code/render/gl_helpers.h @@ -0,0 +1,52 @@ +#ifndef _PIUMA_RENDER_GL_HELPERS_H_ +#define _PIUMA_RENDER_GL_HELPERS_H_ + +#include "../debug/logger.h" + +inline void APIENTRY glDebugOutput(GLenum source, GLenum type, unsigned int id, GLenum severity, GLsizei length, const char *message, const void *userParam) +{ + // ignore non-significant error/warning codes + if(id == 131169 || id == 131185 || id == 131218 || id == 131204) + return; + + if (severity == GL_DEBUG_SEVERITY_NOTIFICATION) + return; + + // @Cleanup: LOG does not replace printf here. We need to merge the multiple printfs into a single log message + LOG(LOG_DEBUG, "---------------"); + LOG(LOG_DEBUG, "Debug message (%u): %s", id, message); + + switch (source) + { + case GL_DEBUG_SOURCE_API: LOG(LOG_DEBUG, "Source: API"); break; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: LOG(LOG_DEBUG, "Source: Window System"); break; + case GL_DEBUG_SOURCE_SHADER_COMPILER: LOG(LOG_DEBUG, "Source: Shader Compiler"); break; + case GL_DEBUG_SOURCE_THIRD_PARTY: LOG(LOG_DEBUG, "Source: Third Party"); break; + case GL_DEBUG_SOURCE_APPLICATION: LOG(LOG_DEBUG, "Source: Application"); break; + case GL_DEBUG_SOURCE_OTHER: LOG(LOG_DEBUG, "Source: Other"); break; + } + + switch (type) + { + case GL_DEBUG_TYPE_ERROR: LOG(LOG_DEBUG, "Type: Error"); break; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: LOG(LOG_DEBUG, "Type: Deprecated Behaviour"); break; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: LOG(LOG_DEBUG, "Type: Undefined Behaviour"); break; + case GL_DEBUG_TYPE_PORTABILITY: LOG(LOG_DEBUG, "Type: Portability"); break; + case GL_DEBUG_TYPE_PERFORMANCE: LOG(LOG_DEBUG, "Type: Performance"); break; + case GL_DEBUG_TYPE_MARKER: LOG(LOG_DEBUG, "Type: Marker"); break; + case GL_DEBUG_TYPE_PUSH_GROUP: LOG(LOG_DEBUG, "Type: Push Group"); break; + case GL_DEBUG_TYPE_POP_GROUP: LOG(LOG_DEBUG, "Type: Pop Group"); break; + case GL_DEBUG_TYPE_OTHER: LOG(LOG_DEBUG, "Type: Other"); break; + } + + switch (severity) + { + case GL_DEBUG_SEVERITY_HIGH: LOG(LOG_DEBUG, "Severity: high"); break; + case GL_DEBUG_SEVERITY_MEDIUM: LOG(LOG_DEBUG, "Severity: medium"); break; + case GL_DEBUG_SEVERITY_LOW: LOG(LOG_DEBUG, "Severity: low"); break; + case GL_DEBUG_SEVERITY_NOTIFICATION: LOG(LOG_DEBUG, "Severity: notification"); break; + } + LOG(LOG_DEBUG, ""); +} + +#endif diff --git a/code/render/lights.cpp b/code/render/lights.cpp new file mode 100644 index 0000000..e69de29 diff --git a/code/render/lights.h b/code/render/lights.h new file mode 100644 index 0000000..edb4d2d --- /dev/null +++ b/code/render/lights.h @@ -0,0 +1,44 @@ +#ifndef _PIUMA_RENDER_LIGHTS_H_ +#define _PIUMA_RENDER_LIGHTS_H_ + +struct r_sun_light +{ + v3 direction; + f32 _padding0; + v3 color; + f32 intensity; +}; + +struct r_point_light +{ + v3 position; + f32 _padding0; + v3 color; + f32 intensity; +}; + +struct r_spot_light +{ + v3 position; + f32 inner_radius; + v3 color; + f32 intensity; + v3 direction; + f32 outer_radius; +}; + +#define MAX_SUN_LIGHTS 4 +#define MAX_POINT_LIGHTS 128 +#define MAX_SPOT_LIGHTS 128 +struct r_light_container +{ + u32 sun_light_count; + u32 point_light_count; + u32 spot_light_count; + f32 ambient_light; + r_sun_light sun_lights[MAX_SUN_LIGHTS]; + r_point_light point_lights[MAX_POINT_LIGHTS]; + r_spot_light spot_lights[MAX_SPOT_LIGHTS]; +}; + +#endif diff --git a/code/render/object.cpp b/code/render/object.cpp new file mode 100644 index 0000000..bdcf04b --- /dev/null +++ b/code/render/object.cpp @@ -0,0 +1,79 @@ +#include "object.h" +#include "../lib/math.h" +#include "../lib/geometry.h" +#include "../platform.h" +#include "string.h" +#include "state.h" +#include "2d.h" + + +r_object r_object_create(u64 count, r_mesh **meshes, r_material **materials, v3 position, v3 rotation, v3 scale) +{ + r_object object; + + object.meshes = (r_mesh**)p_alloc(count * sizeof(r_mesh*)); + object.mesh_material = (r_material**)p_alloc(count * sizeof(r_material*)); + object.mesh_local_transform = (m4*)p_alloc(count * sizeof(m4)); + + memcpy(object.meshes, meshes, count * sizeof(r_mesh*)); + memcpy(object.mesh_material, materials, count * sizeof(r_material*)); + for(u64 i = 0; i < count; i++) + object.mesh_local_transform[i] = m4_identity; + + object.count = count; + + object.position = position; + object.rotation = rotation; + object.scale = scale; + + object.has_shadow = true; + + return object; +} + +r_object r_object_allocate(u64 count) +{ + r_object object; + + object.meshes = (r_mesh**)p_alloc(count * sizeof(r_mesh*)); + object.mesh_material = (r_material**)p_alloc(count * sizeof(r_material*)); + object.mesh_local_transform = (m4*)p_alloc(count * sizeof(m4)); + + for(u64 i = 0; i < count; i++) + object.mesh_local_transform[i] = m4_identity; + + object.count = count; + + object.position = v3{0,0,0}; + object.rotation = v3{0,0,0}; + object.scale = v3{1,1,1}; + + object.has_shadow = true; + + return object; +} + +void r_object_destroy(r_object *object) +{ + p_free(object->mesh_material); + p_free(object->meshes); + p_free(object->mesh_local_transform); + object->mesh_material = NULL; + object->meshes = NULL; + object->mesh_local_transform = NULL; + object->count = 0; +} + +m4 r_object_transform_matrix(r_object *object) +{ + // @Performance: replace with rototranslation matrix + return translation_v3(object->position) * rotation_v3(object->rotation) * scale_v3(object->scale); +} + +m4 r_object_mesh_transform_matrix(r_object *object, u64 mesh_i) +{ + // @Performance: replace with rototranslation matrix + if(object->mesh_local_transform) + return r_object_transform_matrix(object) * object->mesh_local_transform[mesh_i]; + return r_object_transform_matrix(object); +} diff --git a/code/render/object.h b/code/render/object.h new file mode 100644 index 0000000..12195f7 --- /dev/null +++ b/code/render/object.h @@ -0,0 +1,64 @@ +#ifndef _PIUMA_RENDER_OBJECT_H_ +#define _PIUMA_RENDER_OBJECT_H_ + +#include "primitives.h" +#include "shader.h" + +struct r_material +{ + // @Feature: PBR materials + r_shader *shader; + + r_texture *albedo_texture; + v4 albedo_factor; + + r_texture *metallic_texture; + f32 metallic_factor; + + r_texture *roughness_texture; + f32 roughness_factor; + + r_texture *normal_texture; + + r_texture *emissive_texture; + v4 emissive_factor; + /* + albedo | + metallic | --> Disney + UE4 paper + roughness / specular | + cavity | + subsurface | + anisotropy | --> Disney paper + clearcoat | + sheen | + ambient occlusion | --> Valve Source2, same as cavity? + emission + normals + */ +}; + + + +struct r_object +{ + // @Feature: actually support multiple meshes/materials in the implementation code + r_mesh **meshes; + r_material **mesh_material; + m4 *mesh_local_transform; + u64 count; + + v3 scale; + v3 position; + v3 rotation; + + bool has_shadow; +}; + +r_object r_object_create(u64 count, r_mesh **meshes, r_material **materials, v3 position = {0,0,0}, v3 rotation = {0,0,0}, v3 scale = {1,1,1}); +r_object r_object_allocate(u64 count); +void r_object_destroy(r_object *object); +m4 r_object_transform_matrix(r_object *object); +m4 r_object_mesh_transform_matrix(r_object *object, u64 mesh_i); + + +#endif diff --git a/code/render/primitives.cpp b/code/render/primitives.cpp new file mode 100644 index 0000000..45c60f2 --- /dev/null +++ b/code/render/primitives.cpp @@ -0,0 +1,424 @@ +#include "primitives.h" +#include "../platform.h" +#include + +// GL utils +static GLint gl_texture_internal_format(u32 texture_flags); +static GLenum gl_texture_format (u32 texture_flags); +static GLenum gl_texture_type (u32 texture_flags); + +// Texture +r_texture r_texture_create(u8 *data, v2s size, u32 flags) +{ + r_texture texture; + + texture.data = data; + texture.size = size; + texture.flags = flags | R_TEXTURE_INITIALIZED; + + glGenTextures(1, &texture.gl_id); + glBindTexture(GL_TEXTURE_2D, texture.gl_id); + + GLint internal_format = gl_texture_internal_format(flags); + GLenum format = gl_texture_format (flags); + GLenum type = gl_texture_type (flags); + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, size.x, size.y, 0, format, type, data); + + if(flags & R_TEXTURE_NO_MIPMAP) + { + // The default for GL_TEXTURE_{MIN,MAG}_FILTER is GL_NEAREST_MIPMAP_LINEAR, but we are not using mipmaps for this texture. If we don't change this parameter, the image will not render. + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + else + { + glGenerateMipmap(GL_TEXTURE_2D); + } + + if(flags & R_TEXTURE_DONT_OWN) + texture.data = NULL; + + return texture; +} + +void r_texture_resize(r_texture *texture, v2s size) +{ + glBindTexture(GL_TEXTURE_2D, texture->gl_id); + GLint internal_format = gl_texture_internal_format(texture->flags); + GLenum format = gl_texture_format (texture->flags); + GLenum type = gl_texture_type (texture->flags); + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, size.x, size.y, 0, format, type, NULL); + texture->size = size; +} + +void r_texture_update(r_texture *texture, u8 *data, v2s size, v2s position, u32 stride) +{ + glBindTexture(GL_TEXTURE_2D, texture->gl_id); + GLenum format = gl_texture_format(texture->flags); + GLenum type = gl_texture_type (texture->flags); + glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); + glTexSubImage2D(GL_TEXTURE_2D, 0, position.x, position.y, size.x, size.y, format, type, data); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + if(texture->flags & R_TEXTURE_NO_MIPMAP) + { + // The default for GL_TEXTURE_{MIN,MAG}_FILTER is GL_NEAREST_MIPMAP_LINEAR, but we are not using mipmaps for this texture. If we don't change this parameter, the image will not render. + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + else + { + glGenerateMipmap(GL_TEXTURE_2D); + } +} + +void r_texture_destroy(r_texture *texture) +{ + glDeleteTextures(1, &texture->gl_id); + + if(texture->data && !(texture->flags & R_TEXTURE_DONT_OWN)) + p_free(texture->data); + texture->size = {0,0}; + texture->flags = R_TEXTURE_DESTROYED; +} + +s32 r_texture_channels(r_texture *texture) +{ + if(texture->flags & R_TEXTURE_ALPHA) + return 1; + if(texture->flags & R_TEXTURE_RGB) + return 3; + if(texture->flags & (R_TEXTURE_RGBA | R_TEXTURE_SRGB)) + return 4; + return 4; +} + + +// Cubemap +r_cubemap r_cubemap_create(float *data[6], v2s size, u32 flags) +{ + r_cubemap cubemap; + + for(u32 i = 0; i < 6; i++) + cubemap.data[i] = data[i]; + cubemap.size = size; + cubemap.flags = flags | R_CUBEMAP_INITIALIZED; + + glGenTextures(1, &cubemap.gl_id); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.gl_id); + for(u32 i = 0; i < 6; i++) + { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, size.x, size.y, 0, GL_RGB, GL_FLOAT, data[i]); + + if(flags & R_CUBEMAP_DONT_OWN) + cubemap.data[i] = NULL; + } + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); + + return cubemap; +} + +void r_cubemap_destroy(r_cubemap *cubemap) +{ + glDeleteTextures(1, &cubemap->gl_id); + + for(u32 i = 0; i < 6; i++) + { + if(cubemap->data[i] && !(cubemap->flags & R_CUBEMAP_DONT_OWN)) + p_free(cubemap->data[i]); + } + cubemap->size = {0,0}; + cubemap->flags = R_CUBEMAP_DESTROYED; +} + + +// 2D mesh +r_2d_mesh r_2d_mesh_create(u64 count, v2 *vertices, v4 *colors, v2 *uvs) +{ + r_2d_mesh mesh; + + mesh.vertices = vertices; + mesh.colors = colors; + mesh.uvs = uvs; + mesh.count = count; + + glGenVertexArrays(1, &mesh.gl_VAO); + glBindVertexArray(mesh.gl_VAO); + + glGenBuffers(1, &mesh.gl_vertex_buffer); + glGenBuffers(1, &mesh.gl_color_buffer); + glGenBuffers(1, &mesh.gl_uv_buffer); + + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, mesh.count * sizeof(v2), mesh.vertices, GL_STATIC_DRAW); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0); + + glEnableVertexAttribArray(1); + glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_color_buffer); + glBufferData(GL_ARRAY_BUFFER, mesh.count * sizeof(v4), mesh.colors, GL_STATIC_DRAW); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(v4), (void*)0); + + if(mesh.uvs) + { + glEnableVertexAttribArray(2); + glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_uv_buffer); + glBufferData(GL_ARRAY_BUFFER, mesh.count * sizeof(v2), mesh.uvs, GL_STATIC_DRAW); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0); + } + + return mesh; +} + +void r_2d_mesh_destroy(r_2d_mesh *mesh) +{ + glBindVertexArray(mesh->gl_VAO); + glDeleteBuffers(1, &mesh->gl_vertex_buffer); + glDeleteBuffers(1, &mesh->gl_color_buffer); + glDeleteBuffers(1, &mesh->gl_uv_buffer); + glDeleteVertexArrays(1, &mesh->gl_VAO); + + if(mesh->vertices) + p_free(mesh->vertices); + if(mesh->colors) + p_free(mesh->colors); + if(mesh->uvs) + p_free(mesh->uvs); +} + + +// 3D mesh +r_mesh r_mesh_create(u64 indices_count, u32 *indices, u64 vertices_count, v3 *vertices, v3 *normals, v3 *tangents, v2 *uvs) +{ + r_mesh mesh; + + mesh.vertices = vertices; + mesh.normals = normals; + mesh.tangents = tangents; + mesh.uvs = uvs; + mesh.vertices_count = vertices_count; + mesh.indices = indices; + mesh.indices_count = indices_count; + + glGenVertexArrays(1, &mesh.gl_VAO); + glBindVertexArray(mesh.gl_VAO); + + glGenBuffers(1, &mesh.gl_vertex_buffer); + glGenBuffers(1, &mesh.gl_normal_buffer); + glGenBuffers(1, &mesh.gl_tangent_buffer); + glGenBuffers(1, &mesh.gl_uv_buffer); + glGenBuffers(1, &mesh.gl_index_buffer); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.gl_index_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.indices_count * sizeof(u32), mesh.indices, GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, mesh.vertices_count * sizeof(v3), mesh.vertices, GL_STATIC_DRAW); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(v3), (void*)0); + + if(mesh.normals) + { + glEnableVertexAttribArray(1); + glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_normal_buffer); + glBufferData(GL_ARRAY_BUFFER, mesh.vertices_count * sizeof(v3), mesh.normals, GL_STATIC_DRAW); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(v3), (void*)0); + } + + if(mesh.tangents) + { + glEnableVertexAttribArray(2); + glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_tangent_buffer); + glBufferData(GL_ARRAY_BUFFER, mesh.vertices_count * sizeof(v3), mesh.tangents, GL_STATIC_DRAW); + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(v3), (void*)0); + } + + if(mesh.uvs) + { + glEnableVertexAttribArray(3); + glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_uv_buffer); + glBufferData(GL_ARRAY_BUFFER, mesh.vertices_count * sizeof(v2), mesh.uvs, GL_STATIC_DRAW); + glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0); + } + + return mesh; +} + +void r_mesh_destroy(r_mesh *mesh) +{ + glBindVertexArray(mesh->gl_VAO); + glDeleteBuffers(1, &mesh->gl_vertex_buffer); + glDeleteBuffers(1, &mesh->gl_normal_buffer); + glDeleteBuffers(1, &mesh->gl_uv_buffer); + glDeleteBuffers(1, &mesh->gl_index_buffer); + glDeleteVertexArrays(1, &mesh->gl_VAO); + + if(mesh->vertices) + p_free(mesh->vertices); + if(mesh->normals) + p_free(mesh->normals); + if(mesh->uvs) + p_free(mesh->uvs); + if(mesh->indices) + p_free(mesh->indices); +} + + +// Common meshes +r_mesh r_mesh_build_cube() +{ + v3 v[24] = { + {0.5, 0.5, -0.5}, + {0.5, 0.5, -0.5}, + {0.5, 0.5, -0.5}, + {0.5, -0.5, -0.5}, + {0.5, -0.5, -0.5}, + {0.5, -0.5, -0.5}, + {0.5, 0.5, 0.5}, + {0.5, 0.5, 0.5}, + {0.5, 0.5, 0.5}, + {0.5, -0.5, 0.5}, + {0.5, -0.5, 0.5}, + {0.5, -0.5, 0.5}, + {-0.5, 0.5, -0.5}, + {-0.5, 0.5, -0.5}, + {-0.5, 0.5, -0.5}, + {-0.5, -0.5, -0.5}, + {-0.5, -0.5, -0.5}, + {-0.5, -0.5, -0.5}, + {-0.5, 0.5, 0.5}, + {-0.5, 0.5, 0.5}, + {-0.5, 0.5, 0.5}, + {-0.5, -0.5, 0.5}, + {-0.5, -0.5, 0.5}, + {-0.5, -0.5, 0.5} + }; + v3 n[24] = { + {0.0, 1.0, 0.0}, + {1.0, 0.0, 0.0}, + {0.0, 0.0, -1.0}, + {0.0, -1.0, 0.0}, + {1.0, 0.0, 0.0}, + {0.0, 0.0, -1.0}, + {0.0, 1.0, 0.0}, + {0.0, 0.0, 1.0}, + {1.0, 0.0, 0.0}, + {0.0, 0.0, 1.0}, + {0.0, -1.0, 0.0}, + {1.0, 0.0, 0.0}, + {0.0, 1.0, 0.0}, + {-1.0, 0.0, 0.0}, + {0.0, 0.0, -1.0}, + {-1.0, 0.0, 0.0}, + {0.0, -1.0, 0.0}, + {0.0, 0.0, -1.0}, + {0.0, 1.0, 0.0}, + {0.0, 0.0, 1.0}, + {-1.0, 0.0, 0.0}, + {0.0, 0.0, 1.0}, + {-1.0, 0.0, 0.0}, + {0.0, -1.0, 0.0} + }; + u32 i[36] = {12, 6, 0, 7, 21, 9, 20, 15, 22, 3, 23, 16, 1, 11, 4, 14, 5, 17, 12, 18, 6, 7, 19, 21, 20, 13, 15, 3, 10, 23, 1, 8, 11, 14, 2, 5}; + + v3 *vertices = (v3*)p_alloc(24 * sizeof(v3)); + v3 *normals = (v3*)p_alloc(24 * sizeof(v3)); + u32 *indices = (u32*)p_alloc(36 * sizeof(u32)); + + memcpy(vertices, v, 24 * sizeof(v3)); + memcpy(normals , n, 24 * sizeof(v3)); + memcpy(indices , i, 36 * sizeof(u32)); + + return r_mesh_create(36, indices, 24, vertices, normals, NULL, NULL); +} + + + +// Framebuffers +r_framebuffer r_framebuffer_create(v2s size, u32 flags) +{ + r_framebuffer fb; + fb.flags = flags; + fb.size = size; + + glGenFramebuffers(1, &fb.gl_id); + glBindFramebuffer(GL_FRAMEBUFFER, fb.gl_id); + + fb.color_texture = r_texture_create(NULL, size, R_TEXTURE_RGBA | R_TEXTURE_HDR | R_TEXTURE_NO_MIPMAP); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb.color_texture.gl_id, 0); + + fb.gl_depth_id = 0; + if(flags & R_FRAMEBUFFER_DEPTH) + { + glGenRenderbuffers(1, &fb.gl_depth_id); + glBindRenderbuffer(GL_RENDERBUFFER, fb.gl_depth_id); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, size.x, size.y); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb.gl_depth_id); + } + + return fb; +} + +void r_framebuffer_destroy(r_framebuffer *fb) +{ + r_texture_destroy(&fb->color_texture); + if(fb->flags & R_FRAMEBUFFER_DEPTH) + glDeleteRenderbuffers(1, &fb->gl_depth_id); + glDeleteFramebuffers(1, &fb->gl_id); +} + +void r_framebuffer_update_size(r_framebuffer *fb, v2s size) +{ + glBindFramebuffer(GL_FRAMEBUFFER, fb->gl_id); + + r_texture_resize(&fb->color_texture, size); + + if(fb->flags & R_FRAMEBUFFER_DEPTH) + { + glBindRenderbuffer(GL_RENDERBUFFER, fb->gl_depth_id); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, size.x, size.y); + } + + fb->size = size; +} + + + +// GL utils + +static GLint gl_texture_internal_format(u32 texture_flags) +{ + if(texture_flags & R_TEXTURE_ALPHA) + return GL_RED; + else if(texture_flags & R_TEXTURE_RGB) + return (texture_flags & R_TEXTURE_HDR) ? GL_RGB16F : GL_RGB; + else if(texture_flags & R_TEXTURE_RGBA) + return (texture_flags & R_TEXTURE_HDR) ? GL_RGBA16F : GL_RGBA; + else if(texture_flags & R_TEXTURE_SRGB) + return GL_SRGB_ALPHA; + return GL_RGBA; +} + +static GLenum gl_texture_format(u32 texture_flags) +{ + if(texture_flags & R_TEXTURE_ALPHA) + return GL_RED; + else if(texture_flags & R_TEXTURE_RGB) + return GL_RGB; + else if(texture_flags & R_TEXTURE_RGBA) + return GL_RGBA; + else if(texture_flags & R_TEXTURE_SRGB) + return GL_RGBA; + return GL_RGBA; +} + +static GLenum gl_texture_type(u32 texture_flags) +{ + if(texture_flags & R_TEXTURE_HDR) + return GL_FLOAT; + return GL_UNSIGNED_BYTE; +} diff --git a/code/render/primitives.h b/code/render/primitives.h new file mode 100644 index 0000000..a112e87 --- /dev/null +++ b/code/render/primitives.h @@ -0,0 +1,149 @@ +#ifndef _PIUMA_RENDER_PRIMITIVES_H_ +#define _PIUMA_RENDER_PRIMITIVES_H_ + +#include "../lib/types.h" +#include "../lib/math.h" +#include "GL/glcorearb.h" + +enum r_texture_flags : u32 +{ + R_TEXTURE_NONE = 0x00, + + R_TEXTURE_ALPHA = 0x01, + R_TEXTURE_RGB = 0x02, + R_TEXTURE_RGBA = 0x04, + R_TEXTURE_SRGB = 0x08, + + R_TEXTURE_HDR = 0x10, + + R_TEXTURE_NO_MIPMAP = 0x00010000, + + R_TEXTURE_DONT_OWN = 0x20000000, + R_TEXTURE_INITIALIZED = 0x40000000, + R_TEXTURE_DESTROYED = 0x80000000 +}; + +struct r_texture +{ + u8 *data; + v2s size; + u32 flags; + + // OpenGL + GLuint gl_id; +}; + +r_texture r_texture_create(u8 *data, v2s size, u32 flags); +void r_texture_destroy(r_texture *texture); +void r_texture_resize(r_texture *texture, v2s size); +void r_texture_update(r_texture *texture, u8 *data, v2s size, v2s position = {0,0}, u32 stride = 0); +s32 r_texture_channels(r_texture *texture); + + + + +enum r_cubemap_flags : u32 +{ + R_CUBEMAP_NONE = 0x00, + + // R_TEXTURE_ALPHA = 0x01, + // R_TEXTURE_RGB = 0x02, + // R_TEXTURE_RGBA = 0x04, + // R_TEXTURE_SRGB = 0x08, + + R_CUBEMAP_DONT_OWN = 0x20000000, + R_CUBEMAP_INITIALIZED = 0x40000000, + R_CUBEMAP_DESTROYED = 0x80000000 +}; + +struct r_cubemap +{ + float *data[6]; + v2s size; + u32 flags; + + // OpenGL + GLuint gl_id; +}; + +r_cubemap r_cubemap_create(float *data[6], v2s size, u32 flags); +void r_cubemap_destroy(r_cubemap *cubemap); + + + + +struct r_2d_mesh +{ + v2 *vertices; + v4 *colors; + v2 *uvs; + u64 count; + + // OpenGL + GLuint gl_VAO; + GLuint gl_vertex_buffer; + GLuint gl_color_buffer; + GLuint gl_uv_buffer; +}; + +r_2d_mesh r_2d_mesh_create(u64 count, v2 *vertices, v4 *colors, v2 *uvs = NULL); +void r_2d_mesh_destroy(r_2d_mesh *mesh); +// r_2d_mesh_update/change + + + + +struct r_mesh +{ + v3 *vertices; + v3 *normals; + v3 *tangents; + v2 *uvs; + u64 vertices_count; + + u32 *indices; + u64 indices_count; + + // OpenGL + GLuint gl_VAO; + GLuint gl_vertex_buffer; + GLuint gl_normal_buffer; + GLuint gl_tangent_buffer; + GLuint gl_uv_buffer; + GLuint gl_index_buffer; +}; + +r_mesh r_mesh_create(u64 indices_count, u32 *indices, u64 vertices_count, v3 *vertices, v3 *normals = NULL, v3 *tangents = NULL, v2 *uvs = NULL); +void r_mesh_destroy(r_mesh *mesh); +// r_mesh_update/change + + +// Common meshes +r_mesh r_mesh_build_cube(); + + + +// Framebuffers +enum r_framebuffer_flags : u32 +{ + R_FRAMEBUFFER_NONE = 0, + R_FRAMEBUFFER_DEPTH, +}; + +struct r_framebuffer +{ + u32 gl_id; + u32 gl_depth_id; + r_texture color_texture; + v2s size; + u32 flags; +}; + +r_framebuffer r_framebuffer_create(v2s size, u32 flags); +void r_framebuffer_destroy(r_framebuffer *fb); +void r_framebuffer_update_size(r_framebuffer *fb, v2s size); + + + + +#endif diff --git a/code/render/render.cpp b/code/render/render.cpp new file mode 100644 index 0000000..ff09771 --- /dev/null +++ b/code/render/render.cpp @@ -0,0 +1,513 @@ +#include "render.h" +#include "state.h" +#include "../platform.h" +#include "../lib/math.h" +#include "../debug/logger.h" +#include "gl_helpers.h" +#include "string.h" +#include "../camera.h" // @Cleanup: remove dependency from camera + +r_state r_render_state; + + +void r_init() +{ + // OpenGL debug messages + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + glDebugMessageCallback(glDebugOutput, nullptr); + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE); + + // Depth test + glEnable(GL_DEPTH_TEST); + // Face culling + glEnable(GL_CULL_FACE); + // Texture pixel alignment (default is 4, so rgb textures should have every pixel padded to 4 bytes. With this we can have 3 byte pixels with no padding between eachother) + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + // Blending + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + // Cubemap + glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + + + // Load shaders + if(!r_shader_from_files(&r_render_state.shader_2d, "shaders/2d.vert", "shaders/2d.frag")) + { + LOG(LOG_ERROR, "Cannot load 2D shader."); + } + if(!r_shader_from_files(&r_render_state.shader_postprocessing, "shaders/postprocessing.vert", "shaders/postprocessing.frag")) + { + LOG(LOG_ERROR, "Cannot load postprocessing shader."); + } + if(!r_shader_from_files(&r_render_state.shader_pbr, "shaders/pbr.vert", "shaders/pbr.frag")) + { + LOG(LOG_ERROR, "Cannot load pbr shader."); + } + if(!r_shader_from_files(&r_render_state.shader_shadow_map, "shaders/shadow_map.vert", "shaders/shadow_map.frag")) + { + LOG(LOG_ERROR, "Cannot load shadow_map shader."); + } + if(!r_shader_from_files(&r_render_state.shader_environment_map, "shaders/environment_map.vert", "shaders/environment_map.frag")) + { + LOG(LOG_ERROR, "Cannot load environment_map shader."); + } + + // Screen size + u32 width, height; + p_window_dimensions(&width, &height); + glViewport(0, 0, width, height); + + + /* We have to apply some post-processing effects only to the 3D world + * and some effects to both the 3D world and the HUD. + * + * So we make 2 framebuffers: + * 1. target for 3D world + * 2. target for HUD + * After rendering, we apply the correct post-processing effects and + * merge them toghether. + */ + // Init framebuffers + v2s size = {width, height}; + r_render_state.framebuffer_SCREEN = {.gl_id = 0, .size = size, .flags = 0}; // This is a special framebuffer, because it's already there. We don't need to create it + r_render_state.framebuffer_HUD = r_framebuffer_create(size, 0); + r_render_state.framebuffer_3D = r_framebuffer_create(size, R_FRAMEBUFFER_DEPTH); + + // Default framebuffer + r_framebuffer_select(&r_render_state.framebuffer_SCREEN); + + // Init fullscreen quad + glGenVertexArrays(1, &r_render_state.gl_screen_quad_VAO); + glBindVertexArray(r_render_state.gl_screen_quad_VAO); + glGenBuffers(1, &r_render_state.gl_screen_quad_VBO); + glBindBuffer(GL_ARRAY_BUFFER, r_render_state.gl_screen_quad_VBO); + v2 quad_vertices[2*6] = + { + // position UV coords + v2{-1, 1}, v2{0, 1}, + v2{-1, -1}, v2{0, 0}, + v2{ 1, -1}, v2{1, 0}, + v2{-1, 1}, v2{0, 1}, + v2{ 1, -1}, v2{1, 0}, + v2{ 1, 1}, v2{1, 1} + }; + glBufferData(GL_ARRAY_BUFFER, 2*6 * sizeof(v2), quad_vertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(v2), (void*)(0)); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2*sizeof(v2), (void*)(sizeof(v2))); + + // Render state + r_size_update(width, height); +} + +void r_deinit() +{ + r_framebuffer_destroy(&r_render_state.framebuffer_HUD); + r_framebuffer_destroy(&r_render_state.framebuffer_3D); + + glDeleteBuffers(1, &r_render_state.gl_screen_quad_VBO); + glDeleteVertexArrays(1, &r_render_state.gl_screen_quad_VAO); +} + +void r_size_update(u32 width, u32 height) +{ + // Screen size + r_render_state.width = width; + r_render_state.height = height; + // Update shaders + glUseProgram(r_render_state.shader_2d.id); + glUniform1i(r_render_state.shader_2d.width , width); + glUniform1i(r_render_state.shader_2d.height, height); + + // Update framebuffers size + v2s size = {width, height}; + r_render_state.framebuffer_SCREEN.size = size; + r_framebuffer_update_size(&r_render_state.framebuffer_HUD, size); + r_framebuffer_update_size(&r_render_state.framebuffer_3D , size); +} + +void r_time_update(f64 time) +{ + r_render_state.time = time; +} + + + + +void r_clear(v4 color) +{ + glClearColor(color.r, color.g, color.b, color.a); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void r_swap() +{ + p_graphics_swap(); +} + + + +r_scene r_scene_create() +{ + r_scene scene; + + scene.view_matrix = m4_identity; + scene.view_matrix_inverse = m4_identity; + + scene.projection_matrix = m4_identity; + scene.projection_matrix_inverse = m4_identity; + + scene.objects = NULL; + scene.objects_count = 0; + + + // Init lights memory + memset(&scene.lights, 0, sizeof(r_light_container)); + glGenBuffers(1, &scene.gl_lights); + glBindBuffer(GL_UNIFORM_BUFFER, scene.gl_lights); + glBufferData(GL_UNIFORM_BUFFER, sizeof(r_light_container), &scene.lights, GL_STATIC_DRAW); + glBindBufferBase(GL_UNIFORM_BUFFER, 0, scene.gl_lights); + + return scene; +} + +void r_scene_destroy(r_scene *scene) +{ + glDeleteBuffers(1, &scene->gl_lights); + r_cubemap_destroy(&scene->environment_map); +} + +void r_scene_set_view(r_scene *scene, m4 m) +{ + scene->view_matrix = m; + scene->view_matrix_inverse = inverse(m); +} + +void r_scene_set_projection(r_scene *scene, m4 m) +{ + scene->projection_matrix = m; + scene->projection_matrix_inverse = inverse(m); +} + +void r_update_lights(r_scene *scene) +{ + glBindBuffer(GL_UNIFORM_BUFFER, scene->gl_lights); + glBufferData(GL_UNIFORM_BUFFER, sizeof(r_light_container), &scene->lights, GL_STATIC_DRAW); +} + + + +r_object build_basic_cube() +{ + r_object object = r_object_allocate(1); + + object.meshes[0] = (r_mesh*)p_alloc(sizeof(r_mesh)); + *object.meshes[0] = r_mesh_build_cube(); + + object.mesh_material[0] = (r_material*)p_alloc(sizeof(r_material)); + memset(object.mesh_material[0], 0, sizeof(r_material)); + object.mesh_material[0]->shader = &r_render_state.shader_pbr; + object.mesh_material[0]->albedo_factor = {1,1,1,1}; + object.mesh_material[0]->emissive_factor = {1,1,1,0}; + + return object; +} + +void r_render_scene(r_scene *scene) +{ + // Render shadow maps + scene->shadow_map = r_build_shadow_map_sun(scene, &scene->lights.sun_lights[0]); + + // Render environment map + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + static r_object cube = build_basic_cube(); + cube.position = extract_column(scene->view_matrix_inverse, 3).xyz; + glUseProgram(r_render_state.shader_environment_map.id); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, scene->environment_map.gl_id); + glUniform1i(r_render_state.shader_environment_map.has_environment_map, 1); + glUniform1i(r_render_state.shader_environment_map.environment_map , 0); + r_render_object(scene, &cube, &r_render_state.shader_environment_map); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + + // Render objects + glUseProgram(r_render_state.shader_pbr.id); + + // Set shadow map + // @Cleanup: this should be in r_render_objects + glActiveTexture(GL_TEXTURE5); + glBindTexture(GL_TEXTURE_2D, scene->shadow_map.gl_depth_texture); + glUniform1i (r_render_state.shader_pbr.has_shadow_map, 1); + glUniform1i (r_render_state.shader_pbr.shadow_map , 5); + glUniformMatrix4fv(r_render_state.shader_pbr.shadow_matrix , 1, GL_TRUE, (const f32 *)(scene->shadow_map.view_matrix.E)); + + // Environment map + glActiveTexture(GL_TEXTURE6); + glBindTexture(GL_TEXTURE_CUBE_MAP, scene->environment_map.gl_id); + glUniform1i(r_render_state.shader_pbr.has_environment_map, 1); + glUniform1i(r_render_state.shader_pbr.environment_map , 6); + + // Render objects + for(u32 i = 0; i < scene->objects_count; i++) + { + r_render_object(scene, &scene->objects[i]); + } + + glUniform1i(r_render_state.shader_pbr.has_shadow_map, 0); + glUniform1i(r_render_state.shader_pbr.has_environment_map, 0); + r_free_shadow_map(&scene->shadow_map); +} + + + + + +// Single object rendering functions (batched funcs are below) +void r_render_object(r_scene *scene, r_object *object, r_shader *shader_override) +{ + // @Performance: Minimize draw calls by batching mesh rendering + for(u32 i = 0; i < object->count; i++) + { + r_mesh *mesh = object->meshes[i]; + r_material *material = object->mesh_material[i]; + r_shader *shader = material->shader; + if(shader_override) + shader = shader_override; + + glUseProgram(shader->id); + + glUniform1f(shader->time , (f32)r_render_state.time ); + glUniform1f(shader->width , (f32)r_render_state.width ); + glUniform1f(shader->height, (f32)r_render_state.height); + + // Transform matrices + m4 projview_matrix = scene->projection_matrix * scene->view_matrix; + m4 projview_matrix_inverse = inverse(projview_matrix); + glUniformMatrix4fv(shader->view_matrix, 1, GL_TRUE, (const f32 *)(&projview_matrix)); + glUniformMatrix4fv(shader->view_matrix_inverse, 1, GL_TRUE, (const f32 *)(&projview_matrix_inverse)); + + m4 model_transform = r_object_mesh_transform_matrix(object, i); + glUniformMatrix4fv(shader->model_matrix, 1, GL_TRUE, (const f32 *)(&model_transform)); + + // Textures + // Albedo + glUniform1i(shader->has_albedo_texture, material->albedo_texture != NULL); + if(material->albedo_texture) + { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, material->albedo_texture->gl_id); + glUniform1i(shader->albedo_texture, 0); + } + glUniform4f(shader->albedo_factor, material->albedo_factor.r, material->albedo_factor.g, material->albedo_factor.b, material->albedo_factor.a); + + // Metallic + glUniform1i(shader->has_metallic_texture, material->metallic_texture != NULL); + if(material->metallic_texture) + { + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, material->metallic_texture->gl_id); + glUniform1i(shader->metallic_texture, 1); + } + glUniform1f(shader->metallic_factor, material->metallic_factor); + + // Roughness + glUniform1i(shader->has_roughness_texture, material->roughness_texture != NULL); + if(material->roughness_texture) + { + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, material->roughness_texture->gl_id); + glUniform1i(shader->roughness_texture, 2); + } + glUniform1f(shader->roughness_factor, material->roughness_factor); + + // Normal + glUniform1i(shader->has_normal_texture, material->normal_texture != NULL); + if(material->normal_texture) + { + glActiveTexture(GL_TEXTURE3); + glBindTexture(GL_TEXTURE_2D, material->normal_texture->gl_id); + glUniform1i(shader->normal_texture, 3); + } + + // Emissive + glUniform1i(shader->has_emissive_texture, material->emissive_texture != NULL); + if(material->emissive_texture) + { + glActiveTexture(GL_TEXTURE4); + glBindTexture(GL_TEXTURE_2D, material->emissive_texture->gl_id); + glUniform1i(shader->emissive_texture, 4); + } + glUniform4f(shader->emissive_factor, material->emissive_factor.r, material->emissive_factor.g, material->emissive_factor.b, material->emissive_factor.a); + + + + // Draw call + glBindVertexArray(mesh->gl_VAO); + glDrawElements(GL_TRIANGLES, mesh->indices_count, GL_UNSIGNED_INT, (void*)(0)); + } +} + +void r_render_object_wireframe(r_scene *scene, r_object *object) +{ + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + r_render_object(scene, object); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +} + +void r_render_line(r_scene *scene, v3 a, v3 b, v4 a_color, v4 b_color, f32 thickness) +{ + m4 projview_matrix = scene->projection_matrix * scene->view_matrix; + v4 a_trans = projview_matrix * V4(a, 1); + v4 b_trans = projview_matrix * V4(b, 1); + a_trans /= a_trans.w; + b_trans /= b_trans.w; + if(a_trans.z > 1 || b_trans.z > 1) + return; + v2 a_2d = a_trans.xy; + v2 b_2d = b_trans.xy; + a_2d = (v2{.5,.5} + v2{.5,-0.5} * a_2d) * v2{(f32)r_render_state.width, (f32)r_render_state.height}; + b_2d = (v2{.5,.5} + v2{.5,-0.5} * b_2d) * v2{(f32)r_render_state.width, (f32)r_render_state.height}; + + r_2d_immediate_segment(a_2d, b_2d, a_color, b_color, thickness); // @Cleanup: This should be independent from the 2D drawing functions, probably. +} + + + + +// Batch render functions +void r_render_objects(r_scene *scene, u64 count, r_object **objects) +{ + // @Feature: implement +} + + + + +r_shadow_map r_build_shadow_map_sun(r_scene *scene, r_sun_light *sun) +{ + glCullFace(GL_FRONT); + + r_shadow_map sm; + u32 width = 2*1024; + u32 height = 2*1024; + + // Init depth texture/framebuffer + glGenTextures(1, &sm.gl_depth_texture); + glBindTexture(GL_TEXTURE_2D, sm.gl_depth_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D, 0); + + glGenFramebuffers(1, &sm.gl_FBO); + glBindFramebuffer(GL_FRAMEBUFFER, sm.gl_FBO); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, sm.gl_depth_texture, 0); + glDrawBuffer(GL_NONE); + + glBindTexture(GL_TEXTURE_2D, 0); + + // Compute light frustum + m4 projview_matrix = scene->projection_matrix * scene->view_matrix; + m4 projview_matrix_inverse = inverse(projview_matrix); + v4 center_point_4 = projview_matrix_inverse * v4{0, 0, 0, 1}; + v3 center_point = center_point_4.xyz / center_point_4.w; + f32 size = 1; + v4 bottom = projview_matrix_inverse * v4{ 0,-1, 0, 1}; + v4 top = projview_matrix_inverse * v4{ 0, 1, 0, 1}; + v4 left = projview_matrix_inverse * v4{-1, 0, 0, 1}; + v4 right = projview_matrix_inverse * v4{ 1, 0, 0, 1}; + v4 front = projview_matrix_inverse * v4{ 0, 0,-1, 1}; + v4 back = projview_matrix_inverse * v4{ 0, 0, 1, 1}; + size = maximum(size, length(top.xyz/top.w - bottom.xyz/bottom.w)); + size = maximum(size, length(right.xyz/right.w - left.xyz/left.w)); + size = maximum(size, length(back.xyz/back.w - front.xyz/front.w)); + size /= 2; // @Cleanup: hack to have better precision. There are multiple ways to do it right: smallest box around all objects, cascaded shadow maps + + + v3 light_position = center_point - size * sun->direction; + m4 light_camera_matrix = r_view_matrix(light_position, sun->direction, v3{0, 0, 1}); + m4 projection_matrix = r_orthographic_matrix(-size/2, size/2, -size/2, size/2, 0.1, 100.0); + + sm.view_matrix = projection_matrix * light_camera_matrix; + + // Render shadow map + glViewport(0, 0, width, height); + glClear(GL_DEPTH_BUFFER_BIT); + + // @Feature @Cleanup: Some objects might not need to be drawn. Add a "hidden" flag. Maybe take the list of objects to render as an argument + m4 old_view = scene->view_matrix; + m4 old_proj = scene->projection_matrix; + r_scene_set_view (scene, sm.view_matrix); + r_scene_set_projection(scene, m4_identity); + for(u32 i = 0; i < scene->objects_count; i++) + { + if(scene->objects[i].has_shadow) + { + r_render_object(scene, &scene->objects[i], &r_render_state.shader_shadow_map); + } + } + r_scene_set_view (scene, old_view); + r_scene_set_projection(scene, old_proj); + + // Restore framebuffer + r_framebuffer_select(r_render_state.current_framebuffer); + + glCullFace(GL_BACK); + return sm; +} + +void r_free_shadow_map(r_shadow_map *sm) +{ + glDeleteTextures(1, &sm->gl_depth_texture); + glDeleteFramebuffers(1, &sm->gl_FBO); +} + + + + +void r_merge_and_postprocess() +{ + glDisable(GL_DEPTH_TEST); + glEnable(GL_FRAMEBUFFER_SRGB); + + glUseProgram(r_render_state.shader_postprocessing.id); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, r_render_state.framebuffer_3D.color_texture.gl_id); + glUniform1i(r_render_state.shader_postprocessing.texture[0], 0); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, r_render_state.framebuffer_HUD.color_texture.gl_id); + glUniform1i(r_render_state.shader_postprocessing.texture[1], 1); + + glBindVertexArray(r_render_state.gl_screen_quad_VAO); + glDrawArrays(GL_TRIANGLES, 0, 6); + + glEnable(GL_DEPTH_TEST); + glDisable(GL_FRAMEBUFFER_SRGB); +} + + + +void r_framebuffer_select(r_framebuffer *fb) +{ + + glBindFramebuffer(GL_FRAMEBUFFER, fb->gl_id); + glViewport(0, 0, fb->size.x, fb->size.y); + + glUseProgram(r_render_state.shader_2d.id); + glUniform1i(r_render_state.shader_2d.width , fb->size.x); + glUniform1i(r_render_state.shader_2d.height, fb->size.y); + + r_render_state.current_framebuffer = fb; +} diff --git a/code/render/render.h b/code/render/render.h new file mode 100644 index 0000000..7c29f71 --- /dev/null +++ b/code/render/render.h @@ -0,0 +1,90 @@ +// 2d: Functions to render 2d things immediatly. Simple things like meshes, lines, triangles, quads. +// render: Renderer, takes complex objects with materials and renders them. +// Might use an object buffer/queue and render things later, to enable +// sorting and other complex rendering procedures (culling). +// shader: Shaders! They have enough code to have their own source files +// object: Objects, materials, maybe other things. To be used by the renderer. +// light: Lights to be used by the renderer. There are enought types to separate them from objects. + +// Might (or might not) add: compute shaders, vfx/postprocessing passes, animations + +#ifndef _PIUMA_RENDER_RENDER_H_ +#define _PIUMA_RENDER_RENDER_H_ + +#include "primitives.h" +#include "shader.h" +#include "2d.h" +#include "lights.h" +#include "object.h" +#include "state.h" + +void r_init(); +void r_deinit(); + +void r_size_update(u32 width, u32 height); +void r_time_update(f64 time); + +void r_clear(v4 color = {0.0, 0.0, 0.0, 0.0}); +void r_swap(); + + +struct r_shadow_map +{ + // FBO + depth + u32 gl_FBO; + u32 gl_depth_texture; + m4 view_matrix; +}; + +struct r_scene +{ + m4 view_matrix; + m4 view_matrix_inverse; + + m4 projection_matrix; + m4 projection_matrix_inverse; + + r_object *objects; + u64 objects_count; + + r_light_container lights; + GLuint gl_lights; + + r_shadow_map shadow_map; + + r_cubemap environment_map; +}; + +r_scene r_scene_create(); +void r_scene_destroy(r_scene *scene); + +void r_scene_set_view (r_scene *scene, m4 m); +void r_scene_set_projection(r_scene *scene, m4 m); +void r_update_lights(r_scene *scene); + + +void r_render_scene(r_scene *scene); + + + +// Single object rendering functions (batched funcs are below) +void r_render_object(r_scene *scene, r_object *object, r_shader *shader_override = NULL); +void r_render_object_wireframe(r_scene *scene, r_object *object); +void r_render_line(r_scene *scene, v3 a, v3 b, v4 a_color, v4 b_color, f32 thickness = 1.0); + + +// Batch render functions +void r_render_objects(r_scene *scene, u64 count, r_object **objects); + + + +// Shadow maps +r_shadow_map r_build_shadow_map_sun(r_scene *scene, r_sun_light *sun); +void r_free_shadow_map(r_shadow_map *sm); + + +void r_framebuffer_select(r_framebuffer *fb); +void r_merge_and_postprocess(); + + +#endif diff --git a/code/render/shader.cpp b/code/render/shader.cpp new file mode 100644 index 0000000..7a133c8 --- /dev/null +++ b/code/render/shader.cpp @@ -0,0 +1,193 @@ +#include "shader.h" +#include "../debug/logger.h" + +#define STB_INCLUDE_IMPLEMENTATION +#define STB_INCLUDE_LINE_GLSL +#include "stb_include.h" + +// Internal functions +void r_shader_set_uniform_locations(r_shader *shader); + + + + + +bool r_shader_from_files(r_shader *shader, const char *vertex, const char *fragment, const char *geometry, const char *tesseletion_control, const char *tesseletion_evaluation, const char *compute) +{ + const int count = 6; + char *source[count] = {NULL, NULL, NULL, NULL, NULL, NULL}; + const char *filename[count] = {vertex, fragment, geometry, tesseletion_control, tesseletion_evaluation, compute}; + const char *type[count] = {"vertex", "fragment", "geometry", "tesseletion_control", "tesseletion_evaluation", "compute"}; + + // Load sources + bool has_error = false; + char error[256]; + for(int i = 0; i < count; i++) + { + if(filename[i]) + { + char path[4096]; + u32 len = strlen(filename[i]); + memcpy(path, filename[i], len+1); + int last_slash = len; + while(last_slash > 0) + { + if(filename[i][last_slash] == '/') + break; + last_slash--; + } + path[last_slash] = '\0'; + + source[i] = stb_include_file((char*)filename[i], NULL, path, error); + if(!source[i]) + { + has_error = true; + LOG(LOG_ERROR, "Error loading %s shader: %s", type[i], error); + break; + } + } + } + + // Compile shader + if(!has_error) + { + has_error = !r_shader_from_text(shader, source[0], source[1], source[2], source[3], source[5], source[5]); + } + + // Cleanup + for(int i = 0; i < count; i++) + { + if(source[i]) + free(source[i]); + } + + return !has_error; +} + + +bool r_shader_from_text(r_shader *shader, const char *vertex, const char *fragment, const char *geometry, const char *tesseletion_control, const char *tesseletion_evaluation, const char *compute) +{ + int count = 6; + GLuint type[count] = {GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER, GL_TESS_CONTROL_SHADER, GL_TESS_EVALUATION_SHADER, GL_COMPUTE_SHADER}; + const char *source[count] = {vertex, fragment, geometry, tesseletion_control, tesseletion_evaluation, compute}; + + // Create shader program + shader->id = glCreateProgram(); + + bool has_error = false; + for(int i = 0; i < count; i++) + { + if(!source[i]) + continue; + + // Compile current shader source + GLuint source_id = glCreateShader(type[i]); + GLint size = strlen(source[i]); + glShaderSource(source_id, 1, &source[i], &size); + glCompileShader(source_id); + + // Check for success/errors + char build_info[2048]; + GLint build_success; + glGetShaderiv(source_id, GL_COMPILE_STATUS, &build_success); + if (!build_success) + { + glGetShaderInfoLog(source_id, 2048, NULL, build_info); + LOG(LOG_ERROR, "Cannot compile shader %.*s: %s", (int)size, source, build_info); + glDeleteShader(source_id); + has_error = true; + break; + } + + // Attach compiler shader to program + glAttachShader(shader->id, source_id); + } + + // @Correctness: Clean up shader sources after fail + if(has_error) + return false; + + // Link program + glLinkProgram(shader->id); + + // Check for success/error + GLint build_success; + char build_info[512]; + glGetProgramiv(shader->id, GL_LINK_STATUS, &build_success); + if(!build_success) { + glGetProgramInfoLog(shader->id, 512, NULL, build_info); + LOG(LOG_ERROR, "Cannot link shader program: %s", build_info); + glDeleteShader(shader->id); + has_error = true; + } + + // Load uniform locations + if(!has_error) + r_shader_set_uniform_locations(shader); + + return !has_error; +} + + +void r_shader_delete(r_shader *shader) +{ + glDeleteProgram(shader->id); +} + + + + + +// Internal functions +void r_shader_set_uniform_locations(r_shader *shader) +{ + shader->time = glGetUniformLocation(shader->id, "time"); + shader->width = glGetUniformLocation(shader->id, "width"); + shader->height = glGetUniformLocation(shader->id, "height"); + + shader->view_matrix = glGetUniformLocation(shader->id, "view_matrix"); + shader->view_matrix_inverse = glGetUniformLocation(shader->id, "view_matrix_inverse"); + shader->model_matrix = glGetUniformLocation(shader->id, "model_matrix"); + shader->camera_position = glGetUniformLocation(shader->id, "camera_position"); + + for(int i = 0; i < R_SHADER_COLOR_MAX; i++) + { + char uniform_name[32]; sprintf(uniform_name, "color%d", i); + shader->color[i] = glGetUniformLocation(shader->id, uniform_name); + } + for(int i = 0; i < R_SHADER_TEXTURES_MAX; i++) + { + char uniform_name[32]; + sprintf(uniform_name, "has_texture%d", i); + shader->has_texture [i] = glGetUniformLocation(shader->id, uniform_name); + sprintf(uniform_name, "texture%d", i); + shader->texture [i] = glGetUniformLocation(shader->id, uniform_name); + sprintf(uniform_name, "texture_channels%d", i); + shader->texture_channels[i] = glGetUniformLocation(shader->id, uniform_name); + } + + shader->lights = glGetUniformBlockIndex(shader->id, "lights"); + if(shader->lights != GL_INVALID_INDEX) + glUniformBlockBinding(shader->id, shader->lights, 0); + shader->has_shadow_map = glGetUniformLocation(shader->id, "has_shadow_map"); + shader->shadow_map = glGetUniformLocation(shader->id, "shadow_map"); + shader->shadow_matrix = glGetUniformLocation(shader->id, "shadow_matrix"); + + shader->has_environment_map = glGetUniformLocation(shader->id, "has_environment_map"); + shader->environment_map = glGetUniformLocation(shader->id, "environment_map"); + + shader->has_albedo_texture = glGetUniformLocation(shader->id, "has_albedo_texture"); + shader->albedo_texture = glGetUniformLocation(shader->id, "albedo_texture"); + shader->albedo_factor = glGetUniformLocation(shader->id, "albedo_factor"); + shader->has_metallic_texture = glGetUniformLocation(shader->id, "has_metallic_texture"); + shader->metallic_texture = glGetUniformLocation(shader->id, "metallic_texture"); + shader->metallic_factor = glGetUniformLocation(shader->id, "metallic_factor"); + shader->has_roughness_texture = glGetUniformLocation(shader->id, "has_roughness_texture"); + shader->roughness_texture = glGetUniformLocation(shader->id, "roughness_texture"); + shader->roughness_factor = glGetUniformLocation(shader->id, "roughness_factor"); + shader->has_normal_texture = glGetUniformLocation(shader->id, "has_normal_texture"); + shader->normal_texture = glGetUniformLocation(shader->id, "normal_texture"); + shader->has_emissive_texture = glGetUniformLocation(shader->id, "has_emissive_texture"); + shader->emissive_texture = glGetUniformLocation(shader->id, "emissive_texture"); + shader->emissive_factor = glGetUniformLocation(shader->id, "emissive_factor"); +} diff --git a/code/render/shader.h b/code/render/shader.h new file mode 100644 index 0000000..7554266 --- /dev/null +++ b/code/render/shader.h @@ -0,0 +1,85 @@ +#ifndef _PIUMA_RENDER_SHADER_H_ +#define _PIUMA_RENDER_SHADER_H_ + +#include "../lib/types.h" +#include "GL/glcorearb.h" + +/* +I put the uniform ids of all the shaders in the r_shader structure. Then I use only the +ones I need. Yeah, it's not very flexible. + +I would like to have automatic syncronization between shader and C++ code. +There are 3 ways to do that: +- Parse shader code and auto-generate C++ structures and code; +- Parse C++ structures and generate a shader source, or update an existing one; +- Use a language than let me define shader code directly in that language, then it's compiled + directly to GPU ASM, or it generates GLSL code that is then given to the GPU driver. This is + the ideal solution, but yeah, that language is not C++... if it event exists. Let's hope for + a better future. +*/ + +#define R_SHADER_TEXTURES_MAX 4 +#define R_SHADER_COLOR_MAX 4 + +struct r_shader +{ + GLuint id; + + // Common state + GLint time; + GLint width; + GLint height; + + // View and camera + GLint view_matrix; + GLint view_matrix_inverse; + GLint model_matrix; + GLint camera_position; + + // For arrays, the name in the shader is in the format [name][index]. + // Example: color[4] will be: color0, color1, color2, color3 + + // Generic parameters that can be used for different purpose based on the shader needs. + // Example: texture1 could be a diffuse texture, texture2 an emissive texture, texture3 bump mapping + GLint color[R_SHADER_COLOR_MAX]; + GLint has_texture [R_SHADER_TEXTURES_MAX]; // Is textureX assigned or not? + GLint texture [R_SHADER_TEXTURES_MAX]; // Actual texture + GLint texture_channels[R_SHADER_TEXTURES_MAX]; // Number of channels in the texture data + + // Lights and shadows + // @Cleanup: maybe merge this with the generic texture parameter? + GLint lights; + GLint has_shadow_map; + GLint shadow_map; + GLint shadow_matrix; + + // Environment map + GLint has_environment_map; + GLint environment_map; + + // PBR material + GLint has_albedo_texture; + GLint albedo_texture; + GLint albedo_factor; + GLint has_metallic_texture; + GLint metallic_texture; + GLint metallic_factor; + GLint has_roughness_texture; + GLint roughness_texture; + GLint roughness_factor; + GLint has_normal_texture; + GLint normal_texture; + GLint has_emissive_texture; + GLint emissive_texture; + GLint emissive_factor; +}; + + +bool r_shader_from_files(r_shader *shader, const char *vertex, const char *fragment, const char *geometry = NULL, const char *tesseletion_control = NULL, const char *tesseletion_evaluation = NULL, const char *compute = NULL); +bool r_shader_from_text(r_shader *shader, const char *vertex, const char *fragment, const char *geometry = NULL, const char *tesseletion_control = NULL, const char *tesseletion_evaluation = NULL, const char *compute = NULL); + +void r_shader_delete(r_shader *shader); + + + +#endif diff --git a/code/render/state.h b/code/render/state.h new file mode 100644 index 0000000..feca936 --- /dev/null +++ b/code/render/state.h @@ -0,0 +1,36 @@ +#ifndef _PIUMA_RENDER_STATE_H_ +#define _PIUMA_RENDER_STATE_H_ + +#include "shader.h" +#include "primitives.h" + +struct r_state +{ + // Shaders + r_shader shader_2d; + r_shader shader_postprocessing; + r_shader shader_pbr; + r_shader shader_shadow_map; + r_shader shader_environment_map; + + // Screen size + u32 width, height; + + // Time + f64 time; + + // Framebuffers + r_framebuffer *current_framebuffer; + + r_framebuffer framebuffer_SCREEN; + r_framebuffer framebuffer_HUD; + r_framebuffer framebuffer_3D; + + // Quads + u32 gl_screen_quad_VAO; + u32 gl_screen_quad_VBO; +}; + +extern r_state r_render_state; + +#endif diff --git a/external/cgltf.cpp b/external/cgltf.cpp new file mode 100644 index 0000000..6268933 --- /dev/null +++ b/external/cgltf.cpp @@ -0,0 +1,6 @@ +#define CGLTF_IMPLEMENTATION +#include "cgltf.h" + +#undef CGLTF_IMPLEMENTATION +#define CGLTF_WRITE_IMPLEMENTATION +#include "cgltf_write.h" diff --git a/external/cgltf.h b/external/cgltf.h new file mode 100644 index 0000000..af24c65 --- /dev/null +++ b/external/cgltf.h @@ -0,0 +1,7050 @@ +/** + * cgltf - a single-file glTF 2.0 parser written in C99. + * + * Version: 1.13 + * + * Website: https://github.com/jkuhlmann/cgltf + * + * Distributed under the MIT License, see notice at the end of this file. + * + * Building: + * Include this file where you need the struct and function + * declarations. Have exactly one source file where you define + * `CGLTF_IMPLEMENTATION` before including this file to get the + * function definitions. + * + * Reference: + * `cgltf_result cgltf_parse(const cgltf_options*, const void*, + * cgltf_size, cgltf_data**)` parses both glTF and GLB data. If + * this function returns `cgltf_result_success`, you have to call + * `cgltf_free()` on the created `cgltf_data*` variable. + * Note that contents of external files for buffers and images are not + * automatically loaded. You'll need to read these files yourself using + * URIs in the `cgltf_data` structure. + * + * `cgltf_options` is the struct passed to `cgltf_parse()` to control + * parts of the parsing process. You can use it to force the file type + * and provide memory allocation as well as file operation callbacks. + * Should be zero-initialized to trigger default behavior. + * + * `cgltf_data` is the struct allocated and filled by `cgltf_parse()`. + * It generally mirrors the glTF format as described by the spec (see + * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0). + * + * `void cgltf_free(cgltf_data*)` frees the allocated `cgltf_data` + * variable. + * + * `cgltf_result cgltf_load_buffers(const cgltf_options*, cgltf_data*, + * const char* gltf_path)` can be optionally called to open and read buffer + * files using the `FILE*` APIs. The `gltf_path` argument is the path to + * the original glTF file, which allows the parser to resolve the path to + * buffer files. + * + * `cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, + * cgltf_size size, const char* base64, void** out_data)` decodes + * base64-encoded data content. Used internally by `cgltf_load_buffers()`. + * This is useful when decoding data URIs in images. + * + * `cgltf_result cgltf_parse_file(const cgltf_options* options, const + * char* path, cgltf_data** out_data)` can be used to open the given + * file using `FILE*` APIs and parse the data using `cgltf_parse()`. + * + * `cgltf_result cgltf_validate(cgltf_data*)` can be used to do additional + * checks to make sure the parsed glTF data is valid. + * + * `cgltf_node_transform_local` converts the translation / rotation / scale properties of a node + * into a mat4. + * + * `cgltf_node_transform_world` calls `cgltf_node_transform_local` on every ancestor in order + * to compute the root-to-node transformation. + * + * `cgltf_accessor_unpack_floats` reads in the data from an accessor, applies sparse data (if any), + * and converts them to floating point. Assumes that `cgltf_load_buffers` has already been called. + * By passing null for the output pointer, users can find out how many floats are required in the + * output buffer. + * + * `cgltf_num_components` is a tiny utility that tells you the dimensionality of + * a certain accessor type. This can be used before `cgltf_accessor_unpack_floats` to help allocate + * the necessary amount of memory. `cgltf_component_size` and `cgltf_calc_size` exist for + * similar purposes. + * + * `cgltf_accessor_read_float` reads a certain element from a non-sparse accessor and converts it to + * floating point, assuming that `cgltf_load_buffers` has already been called. The passed-in element + * size is the number of floats in the output buffer, which should be in the range [1, 16]. Returns + * false if the passed-in element_size is too small, or if the accessor is sparse. + * + * `cgltf_accessor_read_uint` is similar to its floating-point counterpart, but limited to reading + * vector types and does not support matrix types. The passed-in element size is the number of uints + * in the output buffer, which should be in the range [1, 4]. Returns false if the passed-in + * element_size is too small, or if the accessor is sparse. + * + * `cgltf_accessor_read_index` is similar to its floating-point counterpart, but it returns size_t + * and only works with single-component data types. + * + * `cgltf_copy_extras_json` allows users to retrieve the "extras" data that can be attached to many + * glTF objects (which can be arbitrary JSON data). This is a legacy function, consider using + * cgltf_extras::data directly instead. You can parse this data using your own JSON parser + * or, if you've included the cgltf implementation using the integrated JSMN JSON parser. + */ +#ifndef CGLTF_H_INCLUDED__ +#define CGLTF_H_INCLUDED__ + +#include +#include /* For uint8_t, uint32_t */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef size_t cgltf_size; +typedef long long int cgltf_ssize; +typedef float cgltf_float; +typedef int cgltf_int; +typedef unsigned int cgltf_uint; +typedef int cgltf_bool; + +typedef enum cgltf_file_type +{ + cgltf_file_type_invalid, + cgltf_file_type_gltf, + cgltf_file_type_glb, + cgltf_file_type_max_enum +} cgltf_file_type; + +typedef enum cgltf_result +{ + cgltf_result_success, + cgltf_result_data_too_short, + cgltf_result_unknown_format, + cgltf_result_invalid_json, + cgltf_result_invalid_gltf, + cgltf_result_invalid_options, + cgltf_result_file_not_found, + cgltf_result_io_error, + cgltf_result_out_of_memory, + cgltf_result_legacy_gltf, + cgltf_result_max_enum +} cgltf_result; + +typedef struct cgltf_memory_options +{ + void* (*alloc_func)(void* user, cgltf_size size); + void (*free_func) (void* user, void* ptr); + void* user_data; +} cgltf_memory_options; + +typedef struct cgltf_file_options +{ + cgltf_result(*read)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, const char* path, cgltf_size* size, void** data); + void (*release)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data); + void* user_data; +} cgltf_file_options; + +typedef struct cgltf_options +{ + cgltf_file_type type; /* invalid == auto detect */ + cgltf_size json_token_count; /* 0 == auto */ + cgltf_memory_options memory; + cgltf_file_options file; +} cgltf_options; + +typedef enum cgltf_buffer_view_type +{ + cgltf_buffer_view_type_invalid, + cgltf_buffer_view_type_indices, + cgltf_buffer_view_type_vertices, + cgltf_buffer_view_type_max_enum +} cgltf_buffer_view_type; + +typedef enum cgltf_attribute_type +{ + cgltf_attribute_type_invalid, + cgltf_attribute_type_position, + cgltf_attribute_type_normal, + cgltf_attribute_type_tangent, + cgltf_attribute_type_texcoord, + cgltf_attribute_type_color, + cgltf_attribute_type_joints, + cgltf_attribute_type_weights, + cgltf_attribute_type_custom, + cgltf_attribute_type_max_enum +} cgltf_attribute_type; + +typedef enum cgltf_component_type +{ + cgltf_component_type_invalid, + cgltf_component_type_r_8, /* BYTE */ + cgltf_component_type_r_8u, /* UNSIGNED_BYTE */ + cgltf_component_type_r_16, /* SHORT */ + cgltf_component_type_r_16u, /* UNSIGNED_SHORT */ + cgltf_component_type_r_32u, /* UNSIGNED_INT */ + cgltf_component_type_r_32f, /* FLOAT */ + cgltf_component_type_max_enum +} cgltf_component_type; + +typedef enum cgltf_type +{ + cgltf_type_invalid, + cgltf_type_scalar, + cgltf_type_vec2, + cgltf_type_vec3, + cgltf_type_vec4, + cgltf_type_mat2, + cgltf_type_mat3, + cgltf_type_mat4, + cgltf_type_max_enum +} cgltf_type; + +typedef enum cgltf_primitive_type +{ + cgltf_primitive_type_points, + cgltf_primitive_type_lines, + cgltf_primitive_type_line_loop, + cgltf_primitive_type_line_strip, + cgltf_primitive_type_triangles, + cgltf_primitive_type_triangle_strip, + cgltf_primitive_type_triangle_fan, + cgltf_primitive_type_max_enum +} cgltf_primitive_type; + +typedef enum cgltf_alpha_mode +{ + cgltf_alpha_mode_opaque, + cgltf_alpha_mode_mask, + cgltf_alpha_mode_blend, + cgltf_alpha_mode_max_enum +} cgltf_alpha_mode; + +typedef enum cgltf_animation_path_type { + cgltf_animation_path_type_invalid, + cgltf_animation_path_type_translation, + cgltf_animation_path_type_rotation, + cgltf_animation_path_type_scale, + cgltf_animation_path_type_weights, + cgltf_animation_path_type_max_enum +} cgltf_animation_path_type; + +typedef enum cgltf_interpolation_type { + cgltf_interpolation_type_linear, + cgltf_interpolation_type_step, + cgltf_interpolation_type_cubic_spline, + cgltf_interpolation_type_max_enum +} cgltf_interpolation_type; + +typedef enum cgltf_camera_type { + cgltf_camera_type_invalid, + cgltf_camera_type_perspective, + cgltf_camera_type_orthographic, + cgltf_camera_type_max_enum +} cgltf_camera_type; + +typedef enum cgltf_light_type { + cgltf_light_type_invalid, + cgltf_light_type_directional, + cgltf_light_type_point, + cgltf_light_type_spot, + cgltf_light_type_max_enum +} cgltf_light_type; + +typedef enum cgltf_data_free_method { + cgltf_data_free_method_none, + cgltf_data_free_method_file_release, + cgltf_data_free_method_memory_free, + cgltf_data_free_method_max_enum +} cgltf_data_free_method; + +typedef struct cgltf_extras { + cgltf_size start_offset; /* this field is deprecated and will be removed in the future; use data instead */ + cgltf_size end_offset; /* this field is deprecated and will be removed in the future; use data instead */ + + char* data; +} cgltf_extras; + +typedef struct cgltf_extension { + char* name; + char* data; +} cgltf_extension; + +typedef struct cgltf_buffer +{ + char* name; + cgltf_size size; + char* uri; + void* data; /* loaded by cgltf_load_buffers */ + cgltf_data_free_method data_free_method; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_buffer; + +typedef enum cgltf_meshopt_compression_mode { + cgltf_meshopt_compression_mode_invalid, + cgltf_meshopt_compression_mode_attributes, + cgltf_meshopt_compression_mode_triangles, + cgltf_meshopt_compression_mode_indices, + cgltf_meshopt_compression_mode_max_enum +} cgltf_meshopt_compression_mode; + +typedef enum cgltf_meshopt_compression_filter { + cgltf_meshopt_compression_filter_none, + cgltf_meshopt_compression_filter_octahedral, + cgltf_meshopt_compression_filter_quaternion, + cgltf_meshopt_compression_filter_exponential, + cgltf_meshopt_compression_filter_max_enum +} cgltf_meshopt_compression_filter; + +typedef struct cgltf_meshopt_compression +{ + cgltf_buffer* buffer; + cgltf_size offset; + cgltf_size size; + cgltf_size stride; + cgltf_size count; + cgltf_meshopt_compression_mode mode; + cgltf_meshopt_compression_filter filter; +} cgltf_meshopt_compression; + +typedef struct cgltf_buffer_view +{ + char *name; + cgltf_buffer* buffer; + cgltf_size offset; + cgltf_size size; + cgltf_size stride; /* 0 == automatically determined by accessor */ + cgltf_buffer_view_type type; + void* data; /* overrides buffer->data if present, filled by extensions */ + cgltf_bool has_meshopt_compression; + cgltf_meshopt_compression meshopt_compression; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_buffer_view; + +typedef struct cgltf_accessor_sparse +{ + cgltf_size count; + cgltf_buffer_view* indices_buffer_view; + cgltf_size indices_byte_offset; + cgltf_component_type indices_component_type; + cgltf_buffer_view* values_buffer_view; + cgltf_size values_byte_offset; + cgltf_extras extras; + cgltf_extras indices_extras; + cgltf_extras values_extras; + cgltf_size extensions_count; + cgltf_extension* extensions; + cgltf_size indices_extensions_count; + cgltf_extension* indices_extensions; + cgltf_size values_extensions_count; + cgltf_extension* values_extensions; +} cgltf_accessor_sparse; + +typedef struct cgltf_accessor +{ + char* name; + cgltf_component_type component_type; + cgltf_bool normalized; + cgltf_type type; + cgltf_size offset; + cgltf_size count; + cgltf_size stride; + cgltf_buffer_view* buffer_view; + cgltf_bool has_min; + cgltf_float min[16]; + cgltf_bool has_max; + cgltf_float max[16]; + cgltf_bool is_sparse; + cgltf_accessor_sparse sparse; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_accessor; + +typedef struct cgltf_attribute +{ + char* name; + cgltf_attribute_type type; + cgltf_int index; + cgltf_accessor* data; +} cgltf_attribute; + +typedef struct cgltf_image +{ + char* name; + char* uri; + cgltf_buffer_view* buffer_view; + char* mime_type; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_image; + +typedef struct cgltf_sampler +{ + char* name; + cgltf_int mag_filter; + cgltf_int min_filter; + cgltf_int wrap_s; + cgltf_int wrap_t; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_sampler; + +typedef struct cgltf_texture +{ + char* name; + cgltf_image* image; + cgltf_sampler* sampler; + cgltf_bool has_basisu; + cgltf_image* basisu_image; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_texture; + +typedef struct cgltf_texture_transform +{ + cgltf_float offset[2]; + cgltf_float rotation; + cgltf_float scale[2]; + cgltf_bool has_texcoord; + cgltf_int texcoord; +} cgltf_texture_transform; + +typedef struct cgltf_texture_view +{ + cgltf_texture* texture; + cgltf_int texcoord; + cgltf_float scale; /* equivalent to strength for occlusion_texture */ + cgltf_bool has_transform; + cgltf_texture_transform transform; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_texture_view; + +typedef struct cgltf_pbr_metallic_roughness +{ + cgltf_texture_view base_color_texture; + cgltf_texture_view metallic_roughness_texture; + + cgltf_float base_color_factor[4]; + cgltf_float metallic_factor; + cgltf_float roughness_factor; +} cgltf_pbr_metallic_roughness; + +typedef struct cgltf_pbr_specular_glossiness +{ + cgltf_texture_view diffuse_texture; + cgltf_texture_view specular_glossiness_texture; + + cgltf_float diffuse_factor[4]; + cgltf_float specular_factor[3]; + cgltf_float glossiness_factor; +} cgltf_pbr_specular_glossiness; + +typedef struct cgltf_clearcoat +{ + cgltf_texture_view clearcoat_texture; + cgltf_texture_view clearcoat_roughness_texture; + cgltf_texture_view clearcoat_normal_texture; + + cgltf_float clearcoat_factor; + cgltf_float clearcoat_roughness_factor; +} cgltf_clearcoat; + +typedef struct cgltf_transmission +{ + cgltf_texture_view transmission_texture; + cgltf_float transmission_factor; +} cgltf_transmission; + +typedef struct cgltf_ior +{ + cgltf_float ior; +} cgltf_ior; + +typedef struct cgltf_specular +{ + cgltf_texture_view specular_texture; + cgltf_texture_view specular_color_texture; + cgltf_float specular_color_factor[3]; + cgltf_float specular_factor; +} cgltf_specular; + +typedef struct cgltf_volume +{ + cgltf_texture_view thickness_texture; + cgltf_float thickness_factor; + cgltf_float attenuation_color[3]; + cgltf_float attenuation_distance; +} cgltf_volume; + +typedef struct cgltf_sheen +{ + cgltf_texture_view sheen_color_texture; + cgltf_float sheen_color_factor[3]; + cgltf_texture_view sheen_roughness_texture; + cgltf_float sheen_roughness_factor; +} cgltf_sheen; + +typedef struct cgltf_emissive_strength +{ + cgltf_float emissive_strength; +} cgltf_emissive_strength; + +typedef struct cgltf_iridescence +{ + cgltf_float iridescence_factor; + cgltf_texture_view iridescence_texture; + cgltf_float iridescence_ior; + cgltf_float iridescence_thickness_min; + cgltf_float iridescence_thickness_max; + cgltf_texture_view iridescence_thickness_texture; +} cgltf_iridescence; + +typedef struct cgltf_anisotropy +{ + cgltf_float anisotropy_strength; + cgltf_float anisotropy_rotation; + cgltf_texture_view anisotropy_texture; +} cgltf_anisotropy; + +typedef struct cgltf_material +{ + char* name; + cgltf_bool has_pbr_metallic_roughness; + cgltf_bool has_pbr_specular_glossiness; + cgltf_bool has_clearcoat; + cgltf_bool has_transmission; + cgltf_bool has_volume; + cgltf_bool has_ior; + cgltf_bool has_specular; + cgltf_bool has_sheen; + cgltf_bool has_emissive_strength; + cgltf_bool has_iridescence; + cgltf_bool has_anisotropy; + cgltf_pbr_metallic_roughness pbr_metallic_roughness; + cgltf_pbr_specular_glossiness pbr_specular_glossiness; + cgltf_clearcoat clearcoat; + cgltf_ior ior; + cgltf_specular specular; + cgltf_sheen sheen; + cgltf_transmission transmission; + cgltf_volume volume; + cgltf_emissive_strength emissive_strength; + cgltf_iridescence iridescence; + cgltf_anisotropy anisotropy; + cgltf_texture_view normal_texture; + cgltf_texture_view occlusion_texture; + cgltf_texture_view emissive_texture; + cgltf_float emissive_factor[3]; + cgltf_alpha_mode alpha_mode; + cgltf_float alpha_cutoff; + cgltf_bool double_sided; + cgltf_bool unlit; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_material; + +typedef struct cgltf_material_mapping +{ + cgltf_size variant; + cgltf_material* material; + cgltf_extras extras; +} cgltf_material_mapping; + +typedef struct cgltf_morph_target { + cgltf_attribute* attributes; + cgltf_size attributes_count; +} cgltf_morph_target; + +typedef struct cgltf_draco_mesh_compression { + cgltf_buffer_view* buffer_view; + cgltf_attribute* attributes; + cgltf_size attributes_count; +} cgltf_draco_mesh_compression; + +typedef struct cgltf_mesh_gpu_instancing { + cgltf_attribute* attributes; + cgltf_size attributes_count; +} cgltf_mesh_gpu_instancing; + +typedef struct cgltf_primitive { + cgltf_primitive_type type; + cgltf_accessor* indices; + cgltf_material* material; + cgltf_attribute* attributes; + cgltf_size attributes_count; + cgltf_morph_target* targets; + cgltf_size targets_count; + cgltf_extras extras; + cgltf_bool has_draco_mesh_compression; + cgltf_draco_mesh_compression draco_mesh_compression; + cgltf_material_mapping* mappings; + cgltf_size mappings_count; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_primitive; + +typedef struct cgltf_mesh { + char* name; + cgltf_primitive* primitives; + cgltf_size primitives_count; + cgltf_float* weights; + cgltf_size weights_count; + char** target_names; + cgltf_size target_names_count; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_mesh; + +typedef struct cgltf_node cgltf_node; + +typedef struct cgltf_skin { + char* name; + cgltf_node** joints; + cgltf_size joints_count; + cgltf_node* skeleton; + cgltf_accessor* inverse_bind_matrices; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_skin; + +typedef struct cgltf_camera_perspective { + cgltf_bool has_aspect_ratio; + cgltf_float aspect_ratio; + cgltf_float yfov; + cgltf_bool has_zfar; + cgltf_float zfar; + cgltf_float znear; + cgltf_extras extras; +} cgltf_camera_perspective; + +typedef struct cgltf_camera_orthographic { + cgltf_float xmag; + cgltf_float ymag; + cgltf_float zfar; + cgltf_float znear; + cgltf_extras extras; +} cgltf_camera_orthographic; + +typedef struct cgltf_camera { + char* name; + cgltf_camera_type type; + union { + cgltf_camera_perspective perspective; + cgltf_camera_orthographic orthographic; + } data; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_camera; + +typedef struct cgltf_light { + char* name; + cgltf_float color[3]; + cgltf_float intensity; + cgltf_light_type type; + cgltf_float range; + cgltf_float spot_inner_cone_angle; + cgltf_float spot_outer_cone_angle; + cgltf_extras extras; +} cgltf_light; + +struct cgltf_node { + char* name; + cgltf_node* parent; + cgltf_node** children; + cgltf_size children_count; + cgltf_skin* skin; + cgltf_mesh* mesh; + cgltf_camera* camera; + cgltf_light* light; + cgltf_float* weights; + cgltf_size weights_count; + cgltf_bool has_translation; + cgltf_bool has_rotation; + cgltf_bool has_scale; + cgltf_bool has_matrix; + cgltf_float translation[3]; + cgltf_float rotation[4]; + cgltf_float scale[3]; + cgltf_float matrix[16]; + cgltf_extras extras; + cgltf_bool has_mesh_gpu_instancing; + cgltf_mesh_gpu_instancing mesh_gpu_instancing; + cgltf_size extensions_count; + cgltf_extension* extensions; +}; + +typedef struct cgltf_scene { + char* name; + cgltf_node** nodes; + cgltf_size nodes_count; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_scene; + +typedef struct cgltf_animation_sampler { + cgltf_accessor* input; + cgltf_accessor* output; + cgltf_interpolation_type interpolation; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_animation_sampler; + +typedef struct cgltf_animation_channel { + cgltf_animation_sampler* sampler; + cgltf_node* target_node; + cgltf_animation_path_type target_path; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_animation_channel; + +typedef struct cgltf_animation { + char* name; + cgltf_animation_sampler* samplers; + cgltf_size samplers_count; + cgltf_animation_channel* channels; + cgltf_size channels_count; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_animation; + +typedef struct cgltf_material_variant +{ + char* name; + cgltf_extras extras; +} cgltf_material_variant; + +typedef struct cgltf_asset { + char* copyright; + char* generator; + char* version; + char* min_version; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_asset; + +typedef struct cgltf_data +{ + cgltf_file_type file_type; + void* file_data; + + cgltf_asset asset; + + cgltf_mesh* meshes; + cgltf_size meshes_count; + + cgltf_material* materials; + cgltf_size materials_count; + + cgltf_accessor* accessors; + cgltf_size accessors_count; + + cgltf_buffer_view* buffer_views; + cgltf_size buffer_views_count; + + cgltf_buffer* buffers; + cgltf_size buffers_count; + + cgltf_image* images; + cgltf_size images_count; + + cgltf_texture* textures; + cgltf_size textures_count; + + cgltf_sampler* samplers; + cgltf_size samplers_count; + + cgltf_skin* skins; + cgltf_size skins_count; + + cgltf_camera* cameras; + cgltf_size cameras_count; + + cgltf_light* lights; + cgltf_size lights_count; + + cgltf_node* nodes; + cgltf_size nodes_count; + + cgltf_scene* scenes; + cgltf_size scenes_count; + + cgltf_scene* scene; + + cgltf_animation* animations; + cgltf_size animations_count; + + cgltf_material_variant* variants; + cgltf_size variants_count; + + cgltf_extras extras; + + cgltf_size data_extensions_count; + cgltf_extension* data_extensions; + + char** extensions_used; + cgltf_size extensions_used_count; + + char** extensions_required; + cgltf_size extensions_required_count; + + const char* json; + cgltf_size json_size; + + const void* bin; + cgltf_size bin_size; + + cgltf_memory_options memory; + cgltf_file_options file; +} cgltf_data; + +cgltf_result cgltf_parse( + const cgltf_options* options, + const void* data, + cgltf_size size, + cgltf_data** out_data); + +cgltf_result cgltf_parse_file( + const cgltf_options* options, + const char* path, + cgltf_data** out_data); + +cgltf_result cgltf_load_buffers( + const cgltf_options* options, + cgltf_data* data, + const char* gltf_path); + +cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data); + +cgltf_size cgltf_decode_string(char* string); +cgltf_size cgltf_decode_uri(char* uri); + +cgltf_result cgltf_validate(cgltf_data* data); + +void cgltf_free(cgltf_data* data); + +void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix); +void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix); + +const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view); + +cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size); +cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size); +cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index); + +cgltf_size cgltf_num_components(cgltf_type type); +cgltf_size cgltf_component_size(cgltf_component_type component_type); +cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type); + +cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count); +cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, cgltf_uint* out, cgltf_size index_count); + +/* this function is deprecated and will be removed in the future; use cgltf_extras::data instead */ +cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size); + +cgltf_size cgltf_mesh_index(const cgltf_data* data, const cgltf_mesh* object); +cgltf_size cgltf_material_index(const cgltf_data* data, const cgltf_material* object); +cgltf_size cgltf_accessor_index(const cgltf_data* data, const cgltf_accessor* object); +cgltf_size cgltf_buffer_view_index(const cgltf_data* data, const cgltf_buffer_view* object); +cgltf_size cgltf_buffer_index(const cgltf_data* data, const cgltf_buffer* object); +cgltf_size cgltf_image_index(const cgltf_data* data, const cgltf_image* object); +cgltf_size cgltf_texture_index(const cgltf_data* data, const cgltf_texture* object); +cgltf_size cgltf_sampler_index(const cgltf_data* data, const cgltf_sampler* object); +cgltf_size cgltf_skin_index(const cgltf_data* data, const cgltf_skin* object); +cgltf_size cgltf_camera_index(const cgltf_data* data, const cgltf_camera* object); +cgltf_size cgltf_light_index(const cgltf_data* data, const cgltf_light* object); +cgltf_size cgltf_node_index(const cgltf_data* data, const cgltf_node* object); +cgltf_size cgltf_scene_index(const cgltf_data* data, const cgltf_scene* object); +cgltf_size cgltf_animation_index(const cgltf_data* data, const cgltf_animation* object); +cgltf_size cgltf_animation_sampler_index(const cgltf_animation* animation, const cgltf_animation_sampler* object); +cgltf_size cgltf_animation_channel_index(const cgltf_animation* animation, const cgltf_animation_channel* object); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef CGLTF_H_INCLUDED__ */ + +/* + * + * Stop now, if you are only interested in the API. + * Below, you find the implementation. + * + */ + +#if defined(__INTELLISENSE__) || defined(__JETBRAINS_IDE__) +/* This makes MSVC/CLion intellisense work. */ +#define CGLTF_IMPLEMENTATION +#endif + +#ifdef CGLTF_IMPLEMENTATION + +#include /* For assert */ +#include /* For strncpy */ +#include /* For fopen */ +#include /* For UINT_MAX etc */ +#include /* For FLT_MAX */ + +#if !defined(CGLTF_MALLOC) || !defined(CGLTF_FREE) || !defined(CGLTF_ATOI) || !defined(CGLTF_ATOF) || !defined(CGLTF_ATOLL) +#include /* For malloc, free, atoi, atof */ +#endif + +/* JSMN_PARENT_LINKS is necessary to make parsing large structures linear in input size */ +#define JSMN_PARENT_LINKS + +/* JSMN_STRICT is necessary to reject invalid JSON documents */ +#define JSMN_STRICT + +/* + * -- jsmn.h start -- + * Source: https://github.com/zserge/jsmn + * License: MIT + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; +typedef struct { + jsmntype_t type; + ptrdiff_t start; + ptrdiff_t end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; +typedef struct { + size_t pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g parent object or array */ +} jsmn_parser; +static void jsmn_init(jsmn_parser *parser); +static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens); +/* + * -- jsmn.h end -- + */ + + +#ifndef CGLTF_CONSTS +static const cgltf_size GlbHeaderSize = 12; +static const cgltf_size GlbChunkHeaderSize = 8; +static const uint32_t GlbVersion = 2; +static const uint32_t GlbMagic = 0x46546C67; +static const uint32_t GlbMagicJsonChunk = 0x4E4F534A; +static const uint32_t GlbMagicBinChunk = 0x004E4942; +#define CGLTF_CONSTS +#endif + +#ifndef CGLTF_MALLOC +#define CGLTF_MALLOC(size) malloc(size) +#endif +#ifndef CGLTF_FREE +#define CGLTF_FREE(ptr) free(ptr) +#endif +#ifndef CGLTF_ATOI +#define CGLTF_ATOI(str) atoi(str) +#endif +#ifndef CGLTF_ATOF +#define CGLTF_ATOF(str) atof(str) +#endif +#ifndef CGLTF_ATOLL +#define CGLTF_ATOLL(str) atoll(str) +#endif +#ifndef CGLTF_VALIDATE_ENABLE_ASSERTS +#define CGLTF_VALIDATE_ENABLE_ASSERTS 0 +#endif + +static void* cgltf_default_alloc(void* user, cgltf_size size) +{ + (void)user; + return CGLTF_MALLOC(size); +} + +static void cgltf_default_free(void* user, void* ptr) +{ + (void)user; + CGLTF_FREE(ptr); +} + +static void* cgltf_calloc(cgltf_options* options, size_t element_size, cgltf_size count) +{ + if (SIZE_MAX / element_size < count) + { + return NULL; + } + void* result = options->memory.alloc_func(options->memory.user_data, element_size * count); + if (!result) + { + return NULL; + } + memset(result, 0, element_size * count); + return result; +} + +static cgltf_result cgltf_default_file_read(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, const char* path, cgltf_size* size, void** data) +{ + (void)file_options; + void* (*memory_alloc)(void*, cgltf_size) = memory_options->alloc_func ? memory_options->alloc_func : &cgltf_default_alloc; + void (*memory_free)(void*, void*) = memory_options->free_func ? memory_options->free_func : &cgltf_default_free; + + FILE* file = fopen(path, "rb"); + if (!file) + { + return cgltf_result_file_not_found; + } + + cgltf_size file_size = size ? *size : 0; + + if (file_size == 0) + { + fseek(file, 0, SEEK_END); + +#ifdef _MSC_VER + __int64 length = _ftelli64(file); +#else + long length = ftell(file); +#endif + + if (length < 0) + { + fclose(file); + return cgltf_result_io_error; + } + + fseek(file, 0, SEEK_SET); + file_size = (cgltf_size)length; + } + + char* file_data = (char*)memory_alloc(memory_options->user_data, file_size); + if (!file_data) + { + fclose(file); + return cgltf_result_out_of_memory; + } + + cgltf_size read_size = fread(file_data, 1, file_size, file); + + fclose(file); + + if (read_size != file_size) + { + memory_free(memory_options->user_data, file_data); + return cgltf_result_io_error; + } + + if (size) + { + *size = file_size; + } + if (data) + { + *data = file_data; + } + + return cgltf_result_success; +} + +static void cgltf_default_file_release(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data) +{ + (void)file_options; + void (*memfree)(void*, void*) = memory_options->free_func ? memory_options->free_func : &cgltf_default_free; + memfree(memory_options->user_data, data); +} + +static cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data** out_data); + +cgltf_result cgltf_parse(const cgltf_options* options, const void* data, cgltf_size size, cgltf_data** out_data) +{ + if (size < GlbHeaderSize) + { + return cgltf_result_data_too_short; + } + + if (options == NULL) + { + return cgltf_result_invalid_options; + } + + cgltf_options fixed_options = *options; + if (fixed_options.memory.alloc_func == NULL) + { + fixed_options.memory.alloc_func = &cgltf_default_alloc; + } + if (fixed_options.memory.free_func == NULL) + { + fixed_options.memory.free_func = &cgltf_default_free; + } + + uint32_t tmp; + // Magic + memcpy(&tmp, data, 4); + if (tmp != GlbMagic) + { + if (fixed_options.type == cgltf_file_type_invalid) + { + fixed_options.type = cgltf_file_type_gltf; + } + else if (fixed_options.type == cgltf_file_type_glb) + { + return cgltf_result_unknown_format; + } + } + + if (fixed_options.type == cgltf_file_type_gltf) + { + cgltf_result json_result = cgltf_parse_json(&fixed_options, (const uint8_t*)data, size, out_data); + if (json_result != cgltf_result_success) + { + return json_result; + } + + (*out_data)->file_type = cgltf_file_type_gltf; + + return cgltf_result_success; + } + + const uint8_t* ptr = (const uint8_t*)data; + // Version + memcpy(&tmp, ptr + 4, 4); + uint32_t version = tmp; + if (version != GlbVersion) + { + return version < GlbVersion ? cgltf_result_legacy_gltf : cgltf_result_unknown_format; + } + + // Total length + memcpy(&tmp, ptr + 8, 4); + if (tmp > size) + { + return cgltf_result_data_too_short; + } + + const uint8_t* json_chunk = ptr + GlbHeaderSize; + + if (GlbHeaderSize + GlbChunkHeaderSize > size) + { + return cgltf_result_data_too_short; + } + + // JSON chunk: length + uint32_t json_length; + memcpy(&json_length, json_chunk, 4); + if (GlbHeaderSize + GlbChunkHeaderSize + json_length > size) + { + return cgltf_result_data_too_short; + } + + // JSON chunk: magic + memcpy(&tmp, json_chunk + 4, 4); + if (tmp != GlbMagicJsonChunk) + { + return cgltf_result_unknown_format; + } + + json_chunk += GlbChunkHeaderSize; + + const void* bin = 0; + cgltf_size bin_size = 0; + + if (GlbHeaderSize + GlbChunkHeaderSize + json_length + GlbChunkHeaderSize <= size) + { + // We can read another chunk + const uint8_t* bin_chunk = json_chunk + json_length; + + // Bin chunk: length + uint32_t bin_length; + memcpy(&bin_length, bin_chunk, 4); + if (GlbHeaderSize + GlbChunkHeaderSize + json_length + GlbChunkHeaderSize + bin_length > size) + { + return cgltf_result_data_too_short; + } + + // Bin chunk: magic + memcpy(&tmp, bin_chunk + 4, 4); + if (tmp != GlbMagicBinChunk) + { + return cgltf_result_unknown_format; + } + + bin_chunk += GlbChunkHeaderSize; + + bin = bin_chunk; + bin_size = bin_length; + } + + cgltf_result json_result = cgltf_parse_json(&fixed_options, json_chunk, json_length, out_data); + if (json_result != cgltf_result_success) + { + return json_result; + } + + (*out_data)->file_type = cgltf_file_type_glb; + (*out_data)->bin = bin; + (*out_data)->bin_size = bin_size; + + return cgltf_result_success; +} + +cgltf_result cgltf_parse_file(const cgltf_options* options, const char* path, cgltf_data** out_data) +{ + if (options == NULL) + { + return cgltf_result_invalid_options; + } + + cgltf_result (*file_read)(const struct cgltf_memory_options*, const struct cgltf_file_options*, const char*, cgltf_size*, void**) = options->file.read ? options->file.read : &cgltf_default_file_read; + void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data) = options->file.release ? options->file.release : cgltf_default_file_release; + + void* file_data = NULL; + cgltf_size file_size = 0; + cgltf_result result = file_read(&options->memory, &options->file, path, &file_size, &file_data); + if (result != cgltf_result_success) + { + return result; + } + + result = cgltf_parse(options, file_data, file_size, out_data); + + if (result != cgltf_result_success) + { + file_release(&options->memory, &options->file, file_data); + return result; + } + + (*out_data)->file_data = file_data; + + return cgltf_result_success; +} + +static void cgltf_combine_paths(char* path, const char* base, const char* uri) +{ + const char* s0 = strrchr(base, '/'); + const char* s1 = strrchr(base, '\\'); + const char* slash = s0 ? (s1 && s1 > s0 ? s1 : s0) : s1; + + if (slash) + { + size_t prefix = slash - base + 1; + + strncpy(path, base, prefix); + strcpy(path + prefix, uri); + } + else + { + strcpy(path, uri); + } +} + +static cgltf_result cgltf_load_buffer_file(const cgltf_options* options, cgltf_size size, const char* uri, const char* gltf_path, void** out_data) +{ + void* (*memory_alloc)(void*, cgltf_size) = options->memory.alloc_func ? options->memory.alloc_func : &cgltf_default_alloc; + void (*memory_free)(void*, void*) = options->memory.free_func ? options->memory.free_func : &cgltf_default_free; + cgltf_result (*file_read)(const struct cgltf_memory_options*, const struct cgltf_file_options*, const char*, cgltf_size*, void**) = options->file.read ? options->file.read : &cgltf_default_file_read; + + char* path = (char*)memory_alloc(options->memory.user_data, strlen(uri) + strlen(gltf_path) + 1); + if (!path) + { + return cgltf_result_out_of_memory; + } + + cgltf_combine_paths(path, gltf_path, uri); + + // after combining, the tail of the resulting path is a uri; decode_uri converts it into path + cgltf_decode_uri(path + strlen(path) - strlen(uri)); + + void* file_data = NULL; + cgltf_result result = file_read(&options->memory, &options->file, path, &size, &file_data); + + memory_free(options->memory.user_data, path); + + *out_data = (result == cgltf_result_success) ? file_data : NULL; + + return result; +} + +cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data) +{ + void* (*memory_alloc)(void*, cgltf_size) = options->memory.alloc_func ? options->memory.alloc_func : &cgltf_default_alloc; + void (*memory_free)(void*, void*) = options->memory.free_func ? options->memory.free_func : &cgltf_default_free; + + unsigned char* data = (unsigned char*)memory_alloc(options->memory.user_data, size); + if (!data) + { + return cgltf_result_out_of_memory; + } + + unsigned int buffer = 0; + unsigned int buffer_bits = 0; + + for (cgltf_size i = 0; i < size; ++i) + { + while (buffer_bits < 8) + { + char ch = *base64++; + + int index = + (unsigned)(ch - 'A') < 26 ? (ch - 'A') : + (unsigned)(ch - 'a') < 26 ? (ch - 'a') + 26 : + (unsigned)(ch - '0') < 10 ? (ch - '0') + 52 : + ch == '+' ? 62 : + ch == '/' ? 63 : + -1; + + if (index < 0) + { + memory_free(options->memory.user_data, data); + return cgltf_result_io_error; + } + + buffer = (buffer << 6) | index; + buffer_bits += 6; + } + + data[i] = (unsigned char)(buffer >> (buffer_bits - 8)); + buffer_bits -= 8; + } + + *out_data = data; + + return cgltf_result_success; +} + +static int cgltf_unhex(char ch) +{ + return + (unsigned)(ch - '0') < 10 ? (ch - '0') : + (unsigned)(ch - 'A') < 6 ? (ch - 'A') + 10 : + (unsigned)(ch - 'a') < 6 ? (ch - 'a') + 10 : + -1; +} + +cgltf_size cgltf_decode_string(char* string) +{ + char* read = string + strcspn(string, "\\"); + if (*read == 0) + { + return read - string; + } + char* write = string; + char* last = string; + + for (;;) + { + // Copy characters since last escaped sequence + cgltf_size written = read - last; + memmove(write, last, written); + write += written; + + if (*read++ == 0) + { + break; + } + + // jsmn already checked that all escape sequences are valid + switch (*read++) + { + case '\"': *write++ = '\"'; break; + case '/': *write++ = '/'; break; + case '\\': *write++ = '\\'; break; + case 'b': *write++ = '\b'; break; + case 'f': *write++ = '\f'; break; + case 'r': *write++ = '\r'; break; + case 'n': *write++ = '\n'; break; + case 't': *write++ = '\t'; break; + case 'u': + { + // UCS-2 codepoint \uXXXX to UTF-8 + int character = 0; + for (cgltf_size i = 0; i < 4; ++i) + { + character = (character << 4) + cgltf_unhex(*read++); + } + + if (character <= 0x7F) + { + *write++ = character & 0xFF; + } + else if (character <= 0x7FF) + { + *write++ = 0xC0 | ((character >> 6) & 0xFF); + *write++ = 0x80 | (character & 0x3F); + } + else + { + *write++ = 0xE0 | ((character >> 12) & 0xFF); + *write++ = 0x80 | ((character >> 6) & 0x3F); + *write++ = 0x80 | (character & 0x3F); + } + break; + } + default: + break; + } + + last = read; + read += strcspn(read, "\\"); + } + + *write = 0; + return write - string; +} + +cgltf_size cgltf_decode_uri(char* uri) +{ + char* write = uri; + char* i = uri; + + while (*i) + { + if (*i == '%') + { + int ch1 = cgltf_unhex(i[1]); + + if (ch1 >= 0) + { + int ch2 = cgltf_unhex(i[2]); + + if (ch2 >= 0) + { + *write++ = (char)(ch1 * 16 + ch2); + i += 3; + continue; + } + } + } + + *write++ = *i++; + } + + *write = 0; + return write - uri; +} + +cgltf_result cgltf_load_buffers(const cgltf_options* options, cgltf_data* data, const char* gltf_path) +{ + if (options == NULL) + { + return cgltf_result_invalid_options; + } + + if (data->buffers_count && data->buffers[0].data == NULL && data->buffers[0].uri == NULL && data->bin) + { + if (data->bin_size < data->buffers[0].size) + { + return cgltf_result_data_too_short; + } + + data->buffers[0].data = (void*)data->bin; + data->buffers[0].data_free_method = cgltf_data_free_method_none; + } + + for (cgltf_size i = 0; i < data->buffers_count; ++i) + { + if (data->buffers[i].data) + { + continue; + } + + const char* uri = data->buffers[i].uri; + + if (uri == NULL) + { + continue; + } + + if (strncmp(uri, "data:", 5) == 0) + { + const char* comma = strchr(uri, ','); + + if (comma && comma - uri >= 7 && strncmp(comma - 7, ";base64", 7) == 0) + { + cgltf_result res = cgltf_load_buffer_base64(options, data->buffers[i].size, comma + 1, &data->buffers[i].data); + data->buffers[i].data_free_method = cgltf_data_free_method_memory_free; + + if (res != cgltf_result_success) + { + return res; + } + } + else + { + return cgltf_result_unknown_format; + } + } + else if (strstr(uri, "://") == NULL && gltf_path) + { + cgltf_result res = cgltf_load_buffer_file(options, data->buffers[i].size, uri, gltf_path, &data->buffers[i].data); + data->buffers[i].data_free_method = cgltf_data_free_method_file_release; + + if (res != cgltf_result_success) + { + return res; + } + } + else + { + return cgltf_result_unknown_format; + } + } + + return cgltf_result_success; +} + +static cgltf_size cgltf_calc_index_bound(cgltf_buffer_view* buffer_view, cgltf_size offset, cgltf_component_type component_type, cgltf_size count) +{ + char* data = (char*)buffer_view->buffer->data + offset + buffer_view->offset; + cgltf_size bound = 0; + + switch (component_type) + { + case cgltf_component_type_r_8u: + for (size_t i = 0; i < count; ++i) + { + cgltf_size v = ((unsigned char*)data)[i]; + bound = bound > v ? bound : v; + } + break; + + case cgltf_component_type_r_16u: + for (size_t i = 0; i < count; ++i) + { + cgltf_size v = ((unsigned short*)data)[i]; + bound = bound > v ? bound : v; + } + break; + + case cgltf_component_type_r_32u: + for (size_t i = 0; i < count; ++i) + { + cgltf_size v = ((unsigned int*)data)[i]; + bound = bound > v ? bound : v; + } + break; + + default: + ; + } + + return bound; +} + +#if CGLTF_VALIDATE_ENABLE_ASSERTS +#define CGLTF_ASSERT_IF(cond, result) assert(!(cond)); if (cond) return result; +#else +#define CGLTF_ASSERT_IF(cond, result) if (cond) return result; +#endif + +cgltf_result cgltf_validate(cgltf_data* data) +{ + for (cgltf_size i = 0; i < data->accessors_count; ++i) + { + cgltf_accessor* accessor = &data->accessors[i]; + + cgltf_size element_size = cgltf_calc_size(accessor->type, accessor->component_type); + + if (accessor->buffer_view) + { + cgltf_size req_size = accessor->offset + accessor->stride * (accessor->count - 1) + element_size; + + CGLTF_ASSERT_IF(accessor->buffer_view->size < req_size, cgltf_result_data_too_short); + } + + if (accessor->is_sparse) + { + cgltf_accessor_sparse* sparse = &accessor->sparse; + + cgltf_size indices_component_size = cgltf_calc_size(cgltf_type_scalar, sparse->indices_component_type); + cgltf_size indices_req_size = sparse->indices_byte_offset + indices_component_size * sparse->count; + cgltf_size values_req_size = sparse->values_byte_offset + element_size * sparse->count; + + CGLTF_ASSERT_IF(sparse->indices_buffer_view->size < indices_req_size || + sparse->values_buffer_view->size < values_req_size, cgltf_result_data_too_short); + + CGLTF_ASSERT_IF(sparse->indices_component_type != cgltf_component_type_r_8u && + sparse->indices_component_type != cgltf_component_type_r_16u && + sparse->indices_component_type != cgltf_component_type_r_32u, cgltf_result_invalid_gltf); + + if (sparse->indices_buffer_view->buffer->data) + { + cgltf_size index_bound = cgltf_calc_index_bound(sparse->indices_buffer_view, sparse->indices_byte_offset, sparse->indices_component_type, sparse->count); + + CGLTF_ASSERT_IF(index_bound >= accessor->count, cgltf_result_data_too_short); + } + } + } + + for (cgltf_size i = 0; i < data->buffer_views_count; ++i) + { + cgltf_size req_size = data->buffer_views[i].offset + data->buffer_views[i].size; + + CGLTF_ASSERT_IF(data->buffer_views[i].buffer && data->buffer_views[i].buffer->size < req_size, cgltf_result_data_too_short); + + if (data->buffer_views[i].has_meshopt_compression) + { + cgltf_meshopt_compression* mc = &data->buffer_views[i].meshopt_compression; + + CGLTF_ASSERT_IF(mc->buffer == NULL || mc->buffer->size < mc->offset + mc->size, cgltf_result_data_too_short); + + CGLTF_ASSERT_IF(data->buffer_views[i].stride && mc->stride != data->buffer_views[i].stride, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(data->buffer_views[i].size != mc->stride * mc->count, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_invalid, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_attributes && !(mc->stride % 4 == 0 && mc->stride <= 256), cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_triangles && mc->count % 3 != 0, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF((mc->mode == cgltf_meshopt_compression_mode_triangles || mc->mode == cgltf_meshopt_compression_mode_indices) && mc->stride != 2 && mc->stride != 4, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF((mc->mode == cgltf_meshopt_compression_mode_triangles || mc->mode == cgltf_meshopt_compression_mode_indices) && mc->filter != cgltf_meshopt_compression_filter_none, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_octahedral && mc->stride != 4 && mc->stride != 8, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_quaternion && mc->stride != 8, cgltf_result_invalid_gltf); + } + } + + for (cgltf_size i = 0; i < data->meshes_count; ++i) + { + if (data->meshes[i].weights) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives_count && data->meshes[i].primitives[0].targets_count != data->meshes[i].weights_count, cgltf_result_invalid_gltf); + } + + if (data->meshes[i].target_names) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives_count && data->meshes[i].primitives[0].targets_count != data->meshes[i].target_names_count, cgltf_result_invalid_gltf); + } + + for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].targets_count != data->meshes[i].primitives[0].targets_count, cgltf_result_invalid_gltf); + + if (data->meshes[i].primitives[j].attributes_count) + { + cgltf_accessor* first = data->meshes[i].primitives[j].attributes[0].data; + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].attributes[k].data->count != first->count, cgltf_result_invalid_gltf); + } + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k) + { + for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].targets[k].attributes[m].data->count != first->count, cgltf_result_invalid_gltf); + } + } + + cgltf_accessor* indices = data->meshes[i].primitives[j].indices; + + CGLTF_ASSERT_IF(indices && + indices->component_type != cgltf_component_type_r_8u && + indices->component_type != cgltf_component_type_r_16u && + indices->component_type != cgltf_component_type_r_32u, cgltf_result_invalid_gltf); + + if (indices && indices->buffer_view && indices->buffer_view->buffer->data) + { + cgltf_size index_bound = cgltf_calc_index_bound(indices->buffer_view, indices->offset, indices->component_type, indices->count); + + CGLTF_ASSERT_IF(index_bound >= first->count, cgltf_result_data_too_short); + } + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].mappings[k].variant >= data->variants_count, cgltf_result_invalid_gltf); + } + } + } + } + + for (cgltf_size i = 0; i < data->nodes_count; ++i) + { + if (data->nodes[i].weights && data->nodes[i].mesh) + { + CGLTF_ASSERT_IF (data->nodes[i].mesh->primitives_count && data->nodes[i].mesh->primitives[0].targets_count != data->nodes[i].weights_count, cgltf_result_invalid_gltf); + } + } + + for (cgltf_size i = 0; i < data->nodes_count; ++i) + { + cgltf_node* p1 = data->nodes[i].parent; + cgltf_node* p2 = p1 ? p1->parent : NULL; + + while (p1 && p2) + { + CGLTF_ASSERT_IF(p1 == p2, cgltf_result_invalid_gltf); + + p1 = p1->parent; + p2 = p2->parent ? p2->parent->parent : NULL; + } + } + + for (cgltf_size i = 0; i < data->scenes_count; ++i) + { + for (cgltf_size j = 0; j < data->scenes[i].nodes_count; ++j) + { + CGLTF_ASSERT_IF(data->scenes[i].nodes[j]->parent, cgltf_result_invalid_gltf); + } + } + + for (cgltf_size i = 0; i < data->animations_count; ++i) + { + for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j) + { + cgltf_animation_channel* channel = &data->animations[i].channels[j]; + + if (!channel->target_node) + { + continue; + } + + cgltf_size components = 1; + + if (channel->target_path == cgltf_animation_path_type_weights) + { + CGLTF_ASSERT_IF(!channel->target_node->mesh || !channel->target_node->mesh->primitives_count, cgltf_result_invalid_gltf); + + components = channel->target_node->mesh->primitives[0].targets_count; + } + + cgltf_size values = channel->sampler->interpolation == cgltf_interpolation_type_cubic_spline ? 3 : 1; + + CGLTF_ASSERT_IF(channel->sampler->input->count * components * values != channel->sampler->output->count, cgltf_result_data_too_short); + } + } + + return cgltf_result_success; +} + +cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size) +{ + cgltf_size json_size = extras->end_offset - extras->start_offset; + + if (!dest) + { + if (dest_size) + { + *dest_size = json_size + 1; + return cgltf_result_success; + } + return cgltf_result_invalid_options; + } + + if (*dest_size + 1 < json_size) + { + strncpy(dest, data->json + extras->start_offset, *dest_size - 1); + dest[*dest_size - 1] = 0; + } + else + { + strncpy(dest, data->json + extras->start_offset, json_size); + dest[json_size] = 0; + } + + return cgltf_result_success; +} + +static void cgltf_free_extras(cgltf_data* data, cgltf_extras* extras) +{ + data->memory.free_func(data->memory.user_data, extras->data); +} + +static void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, cgltf_size extensions_count) +{ + for (cgltf_size i = 0; i < extensions_count; ++i) + { + data->memory.free_func(data->memory.user_data, extensions[i].name); + data->memory.free_func(data->memory.user_data, extensions[i].data); + } + data->memory.free_func(data->memory.user_data, extensions); +} + +static void cgltf_free_texture_view(cgltf_data* data, cgltf_texture_view* view) +{ + cgltf_free_extensions(data, view->extensions, view->extensions_count); + cgltf_free_extras(data, &view->extras); +} + +void cgltf_free(cgltf_data* data) +{ + if (!data) + { + return; + } + + void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data) = data->file.release ? data->file.release : cgltf_default_file_release; + + data->memory.free_func(data->memory.user_data, data->asset.copyright); + data->memory.free_func(data->memory.user_data, data->asset.generator); + data->memory.free_func(data->memory.user_data, data->asset.version); + data->memory.free_func(data->memory.user_data, data->asset.min_version); + + cgltf_free_extensions(data, data->asset.extensions, data->asset.extensions_count); + cgltf_free_extras(data, &data->asset.extras); + + for (cgltf_size i = 0; i < data->accessors_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->accessors[i].name); + + if(data->accessors[i].is_sparse) + { + cgltf_free_extensions(data, data->accessors[i].sparse.extensions, data->accessors[i].sparse.extensions_count); + cgltf_free_extensions(data, data->accessors[i].sparse.indices_extensions, data->accessors[i].sparse.indices_extensions_count); + cgltf_free_extensions(data, data->accessors[i].sparse.values_extensions, data->accessors[i].sparse.values_extensions_count); + cgltf_free_extras(data, &data->accessors[i].sparse.extras); + cgltf_free_extras(data, &data->accessors[i].sparse.indices_extras); + cgltf_free_extras(data, &data->accessors[i].sparse.values_extras); + } + cgltf_free_extensions(data, data->accessors[i].extensions, data->accessors[i].extensions_count); + cgltf_free_extras(data, &data->accessors[i].extras); + } + data->memory.free_func(data->memory.user_data, data->accessors); + + for (cgltf_size i = 0; i < data->buffer_views_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->buffer_views[i].name); + data->memory.free_func(data->memory.user_data, data->buffer_views[i].data); + + cgltf_free_extensions(data, data->buffer_views[i].extensions, data->buffer_views[i].extensions_count); + cgltf_free_extras(data, &data->buffer_views[i].extras); + } + data->memory.free_func(data->memory.user_data, data->buffer_views); + + for (cgltf_size i = 0; i < data->buffers_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->buffers[i].name); + + if (data->buffers[i].data_free_method == cgltf_data_free_method_file_release) + { + file_release(&data->memory, &data->file, data->buffers[i].data); + } + else if (data->buffers[i].data_free_method == cgltf_data_free_method_memory_free) + { + data->memory.free_func(data->memory.user_data, data->buffers[i].data); + } + + data->memory.free_func(data->memory.user_data, data->buffers[i].uri); + + cgltf_free_extensions(data, data->buffers[i].extensions, data->buffers[i].extensions_count); + cgltf_free_extras(data, &data->buffers[i].extras); + } + data->memory.free_func(data->memory.user_data, data->buffers); + + for (cgltf_size i = 0; i < data->meshes_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->meshes[i].name); + + for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) + { + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k) + { + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].attributes[k].name); + } + + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].attributes); + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k) + { + for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m) + { + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].targets[k].attributes[m].name); + } + + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].targets[k].attributes); + } + + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].targets); + + if (data->meshes[i].primitives[j].has_draco_mesh_compression) + { + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].draco_mesh_compression.attributes_count; ++k) + { + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].draco_mesh_compression.attributes[k].name); + } + + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].draco_mesh_compression.attributes); + } + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k) + { + cgltf_free_extras(data, &data->meshes[i].primitives[j].mappings[k].extras); + } + + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].mappings); + + cgltf_free_extensions(data, data->meshes[i].primitives[j].extensions, data->meshes[i].primitives[j].extensions_count); + cgltf_free_extras(data, &data->meshes[i].primitives[j].extras); + } + + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives); + data->memory.free_func(data->memory.user_data, data->meshes[i].weights); + + for (cgltf_size j = 0; j < data->meshes[i].target_names_count; ++j) + { + data->memory.free_func(data->memory.user_data, data->meshes[i].target_names[j]); + } + + cgltf_free_extensions(data, data->meshes[i].extensions, data->meshes[i].extensions_count); + cgltf_free_extras(data, &data->meshes[i].extras); + + data->memory.free_func(data->memory.user_data, data->meshes[i].target_names); + } + + data->memory.free_func(data->memory.user_data, data->meshes); + + for (cgltf_size i = 0; i < data->materials_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->materials[i].name); + + if(data->materials[i].has_pbr_metallic_roughness) + { + cgltf_free_texture_view(data, &data->materials[i].pbr_metallic_roughness.metallic_roughness_texture); + cgltf_free_texture_view(data, &data->materials[i].pbr_metallic_roughness.base_color_texture); + } + if(data->materials[i].has_pbr_specular_glossiness) + { + cgltf_free_texture_view(data, &data->materials[i].pbr_specular_glossiness.diffuse_texture); + cgltf_free_texture_view(data, &data->materials[i].pbr_specular_glossiness.specular_glossiness_texture); + } + if(data->materials[i].has_clearcoat) + { + cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_texture); + cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_roughness_texture); + cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_normal_texture); + } + if(data->materials[i].has_specular) + { + cgltf_free_texture_view(data, &data->materials[i].specular.specular_texture); + cgltf_free_texture_view(data, &data->materials[i].specular.specular_color_texture); + } + if(data->materials[i].has_transmission) + { + cgltf_free_texture_view(data, &data->materials[i].transmission.transmission_texture); + } + if (data->materials[i].has_volume) + { + cgltf_free_texture_view(data, &data->materials[i].volume.thickness_texture); + } + if(data->materials[i].has_sheen) + { + cgltf_free_texture_view(data, &data->materials[i].sheen.sheen_color_texture); + cgltf_free_texture_view(data, &data->materials[i].sheen.sheen_roughness_texture); + } + if(data->materials[i].has_iridescence) + { + cgltf_free_texture_view(data, &data->materials[i].iridescence.iridescence_texture); + cgltf_free_texture_view(data, &data->materials[i].iridescence.iridescence_thickness_texture); + } + if (data->materials[i].has_anisotropy) + { + cgltf_free_texture_view(data, &data->materials[i].anisotropy.anisotropy_texture); + } + + cgltf_free_texture_view(data, &data->materials[i].normal_texture); + cgltf_free_texture_view(data, &data->materials[i].occlusion_texture); + cgltf_free_texture_view(data, &data->materials[i].emissive_texture); + + cgltf_free_extensions(data, data->materials[i].extensions, data->materials[i].extensions_count); + cgltf_free_extras(data, &data->materials[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->materials); + + for (cgltf_size i = 0; i < data->images_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->images[i].name); + data->memory.free_func(data->memory.user_data, data->images[i].uri); + data->memory.free_func(data->memory.user_data, data->images[i].mime_type); + + cgltf_free_extensions(data, data->images[i].extensions, data->images[i].extensions_count); + cgltf_free_extras(data, &data->images[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->images); + + for (cgltf_size i = 0; i < data->textures_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->textures[i].name); + + cgltf_free_extensions(data, data->textures[i].extensions, data->textures[i].extensions_count); + cgltf_free_extras(data, &data->textures[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->textures); + + for (cgltf_size i = 0; i < data->samplers_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->samplers[i].name); + + cgltf_free_extensions(data, data->samplers[i].extensions, data->samplers[i].extensions_count); + cgltf_free_extras(data, &data->samplers[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->samplers); + + for (cgltf_size i = 0; i < data->skins_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->skins[i].name); + data->memory.free_func(data->memory.user_data, data->skins[i].joints); + + cgltf_free_extensions(data, data->skins[i].extensions, data->skins[i].extensions_count); + cgltf_free_extras(data, &data->skins[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->skins); + + for (cgltf_size i = 0; i < data->cameras_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->cameras[i].name); + + if (data->cameras[i].type == cgltf_camera_type_perspective) + { + cgltf_free_extras(data, &data->cameras[i].data.perspective.extras); + } + else if (data->cameras[i].type == cgltf_camera_type_orthographic) + { + cgltf_free_extras(data, &data->cameras[i].data.orthographic.extras); + } + + cgltf_free_extensions(data, data->cameras[i].extensions, data->cameras[i].extensions_count); + cgltf_free_extras(data, &data->cameras[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->cameras); + + for (cgltf_size i = 0; i < data->lights_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->lights[i].name); + + cgltf_free_extras(data, &data->lights[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->lights); + + for (cgltf_size i = 0; i < data->nodes_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->nodes[i].name); + data->memory.free_func(data->memory.user_data, data->nodes[i].children); + data->memory.free_func(data->memory.user_data, data->nodes[i].weights); + + if (data->nodes[i].has_mesh_gpu_instancing) + { + for (cgltf_size j = 0; j < data->nodes[i].mesh_gpu_instancing.attributes_count; ++j) + { + data->memory.free_func(data->memory.user_data, data->nodes[i].mesh_gpu_instancing.attributes[j].name); + } + + data->memory.free_func(data->memory.user_data, data->nodes[i].mesh_gpu_instancing.attributes); + } + + cgltf_free_extensions(data, data->nodes[i].extensions, data->nodes[i].extensions_count); + cgltf_free_extras(data, &data->nodes[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->nodes); + + for (cgltf_size i = 0; i < data->scenes_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->scenes[i].name); + data->memory.free_func(data->memory.user_data, data->scenes[i].nodes); + + cgltf_free_extensions(data, data->scenes[i].extensions, data->scenes[i].extensions_count); + cgltf_free_extras(data, &data->scenes[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->scenes); + + for (cgltf_size i = 0; i < data->animations_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->animations[i].name); + for (cgltf_size j = 0; j < data->animations[i].samplers_count; ++j) + { + cgltf_free_extensions(data, data->animations[i].samplers[j].extensions, data->animations[i].samplers[j].extensions_count); + cgltf_free_extras(data, &data->animations[i].samplers[j].extras); + } + data->memory.free_func(data->memory.user_data, data->animations[i].samplers); + + for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j) + { + cgltf_free_extensions(data, data->animations[i].channels[j].extensions, data->animations[i].channels[j].extensions_count); + cgltf_free_extras(data, &data->animations[i].channels[j].extras); + } + data->memory.free_func(data->memory.user_data, data->animations[i].channels); + + cgltf_free_extensions(data, data->animations[i].extensions, data->animations[i].extensions_count); + cgltf_free_extras(data, &data->animations[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->animations); + + for (cgltf_size i = 0; i < data->variants_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->variants[i].name); + + cgltf_free_extras(data, &data->variants[i].extras); + } + + data->memory.free_func(data->memory.user_data, data->variants); + + cgltf_free_extensions(data, data->data_extensions, data->data_extensions_count); + cgltf_free_extras(data, &data->extras); + + for (cgltf_size i = 0; i < data->extensions_used_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->extensions_used[i]); + } + + data->memory.free_func(data->memory.user_data, data->extensions_used); + + for (cgltf_size i = 0; i < data->extensions_required_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->extensions_required[i]); + } + + data->memory.free_func(data->memory.user_data, data->extensions_required); + + file_release(&data->memory, &data->file, data->file_data); + + data->memory.free_func(data->memory.user_data, data); +} + +void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix) +{ + cgltf_float* lm = out_matrix; + + if (node->has_matrix) + { + memcpy(lm, node->matrix, sizeof(float) * 16); + } + else + { + float tx = node->translation[0]; + float ty = node->translation[1]; + float tz = node->translation[2]; + + float qx = node->rotation[0]; + float qy = node->rotation[1]; + float qz = node->rotation[2]; + float qw = node->rotation[3]; + + float sx = node->scale[0]; + float sy = node->scale[1]; + float sz = node->scale[2]; + + lm[0] = (1 - 2 * qy*qy - 2 * qz*qz) * sx; + lm[1] = (2 * qx*qy + 2 * qz*qw) * sx; + lm[2] = (2 * qx*qz - 2 * qy*qw) * sx; + lm[3] = 0.f; + + lm[4] = (2 * qx*qy - 2 * qz*qw) * sy; + lm[5] = (1 - 2 * qx*qx - 2 * qz*qz) * sy; + lm[6] = (2 * qy*qz + 2 * qx*qw) * sy; + lm[7] = 0.f; + + lm[8] = (2 * qx*qz + 2 * qy*qw) * sz; + lm[9] = (2 * qy*qz - 2 * qx*qw) * sz; + lm[10] = (1 - 2 * qx*qx - 2 * qy*qy) * sz; + lm[11] = 0.f; + + lm[12] = tx; + lm[13] = ty; + lm[14] = tz; + lm[15] = 1.f; + } +} + +void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix) +{ + cgltf_float* lm = out_matrix; + cgltf_node_transform_local(node, lm); + + const cgltf_node* parent = node->parent; + + while (parent) + { + float pm[16]; + cgltf_node_transform_local(parent, pm); + + for (int i = 0; i < 4; ++i) + { + float l0 = lm[i * 4 + 0]; + float l1 = lm[i * 4 + 1]; + float l2 = lm[i * 4 + 2]; + + float r0 = l0 * pm[0] + l1 * pm[4] + l2 * pm[8]; + float r1 = l0 * pm[1] + l1 * pm[5] + l2 * pm[9]; + float r2 = l0 * pm[2] + l1 * pm[6] + l2 * pm[10]; + + lm[i * 4 + 0] = r0; + lm[i * 4 + 1] = r1; + lm[i * 4 + 2] = r2; + } + + lm[12] += pm[12]; + lm[13] += pm[13]; + lm[14] += pm[14]; + + parent = parent->parent; + } +} + +static cgltf_ssize cgltf_component_read_integer(const void* in, cgltf_component_type component_type) +{ + switch (component_type) + { + case cgltf_component_type_r_16: + return *((const int16_t*) in); + case cgltf_component_type_r_16u: + return *((const uint16_t*) in); + case cgltf_component_type_r_32u: + return *((const uint32_t*) in); + case cgltf_component_type_r_32f: + return (cgltf_ssize)*((const float*) in); + case cgltf_component_type_r_8: + return *((const int8_t*) in); + case cgltf_component_type_r_8u: + return *((const uint8_t*) in); + default: + return 0; + } +} + +static cgltf_size cgltf_component_read_index(const void* in, cgltf_component_type component_type) +{ + switch (component_type) + { + case cgltf_component_type_r_16u: + return *((const uint16_t*) in); + case cgltf_component_type_r_32u: + return *((const uint32_t*) in); + case cgltf_component_type_r_32f: + return (cgltf_size)((cgltf_ssize)*((const float*) in)); + case cgltf_component_type_r_8u: + return *((const uint8_t*) in); + default: + return 0; + } +} + +static cgltf_float cgltf_component_read_float(const void* in, cgltf_component_type component_type, cgltf_bool normalized) +{ + if (component_type == cgltf_component_type_r_32f) + { + return *((const float*) in); + } + + if (normalized) + { + switch (component_type) + { + // note: glTF spec doesn't currently define normalized conversions for 32-bit integers + case cgltf_component_type_r_16: + return *((const int16_t*) in) / (cgltf_float)32767; + case cgltf_component_type_r_16u: + return *((const uint16_t*) in) / (cgltf_float)65535; + case cgltf_component_type_r_8: + return *((const int8_t*) in) / (cgltf_float)127; + case cgltf_component_type_r_8u: + return *((const uint8_t*) in) / (cgltf_float)255; + default: + return 0; + } + } + + return (cgltf_float)cgltf_component_read_integer(in, component_type); +} + +static cgltf_bool cgltf_element_read_float(const uint8_t* element, cgltf_type type, cgltf_component_type component_type, cgltf_bool normalized, cgltf_float* out, cgltf_size element_size) +{ + cgltf_size num_components = cgltf_num_components(type); + + if (element_size < num_components) { + return 0; + } + + // There are three special cases for component extraction, see #data-alignment in the 2.0 spec. + + cgltf_size component_size = cgltf_component_size(component_type); + + if (type == cgltf_type_mat2 && component_size == 1) + { + out[0] = cgltf_component_read_float(element, component_type, normalized); + out[1] = cgltf_component_read_float(element + 1, component_type, normalized); + out[2] = cgltf_component_read_float(element + 4, component_type, normalized); + out[3] = cgltf_component_read_float(element + 5, component_type, normalized); + return 1; + } + + if (type == cgltf_type_mat3 && component_size == 1) + { + out[0] = cgltf_component_read_float(element, component_type, normalized); + out[1] = cgltf_component_read_float(element + 1, component_type, normalized); + out[2] = cgltf_component_read_float(element + 2, component_type, normalized); + out[3] = cgltf_component_read_float(element + 4, component_type, normalized); + out[4] = cgltf_component_read_float(element + 5, component_type, normalized); + out[5] = cgltf_component_read_float(element + 6, component_type, normalized); + out[6] = cgltf_component_read_float(element + 8, component_type, normalized); + out[7] = cgltf_component_read_float(element + 9, component_type, normalized); + out[8] = cgltf_component_read_float(element + 10, component_type, normalized); + return 1; + } + + if (type == cgltf_type_mat3 && component_size == 2) + { + out[0] = cgltf_component_read_float(element, component_type, normalized); + out[1] = cgltf_component_read_float(element + 2, component_type, normalized); + out[2] = cgltf_component_read_float(element + 4, component_type, normalized); + out[3] = cgltf_component_read_float(element + 8, component_type, normalized); + out[4] = cgltf_component_read_float(element + 10, component_type, normalized); + out[5] = cgltf_component_read_float(element + 12, component_type, normalized); + out[6] = cgltf_component_read_float(element + 16, component_type, normalized); + out[7] = cgltf_component_read_float(element + 18, component_type, normalized); + out[8] = cgltf_component_read_float(element + 20, component_type, normalized); + return 1; + } + + for (cgltf_size i = 0; i < num_components; ++i) + { + out[i] = cgltf_component_read_float(element + component_size * i, component_type, normalized); + } + return 1; +} + +const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view) +{ + if (view->data) + return (const uint8_t*)view->data; + + if (!view->buffer->data) + return NULL; + + const uint8_t* result = (const uint8_t*)view->buffer->data; + result += view->offset; + return result; +} + +cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size) +{ + if (accessor->is_sparse) + { + return 0; + } + if (accessor->buffer_view == NULL) + { + memset(out, 0, element_size * sizeof(cgltf_float)); + return 1; + } + const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); + if (element == NULL) + { + return 0; + } + element += accessor->offset + accessor->stride * index; + return cgltf_element_read_float(element, accessor->type, accessor->component_type, accessor->normalized, out, element_size); +} + +cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count) +{ + cgltf_size floats_per_element = cgltf_num_components(accessor->type); + cgltf_size available_floats = accessor->count * floats_per_element; + if (out == NULL) + { + return available_floats; + } + + float_count = available_floats < float_count ? available_floats : float_count; + cgltf_size element_count = float_count / floats_per_element; + + // First pass: convert each element in the base accessor. + if (accessor->buffer_view == NULL) + { + memset(out, 0, element_count * floats_per_element * sizeof(cgltf_float)); + } + else + { + const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); + if (element == NULL) + { + return 0; + } + element += accessor->offset; + + if (accessor->component_type == cgltf_component_type_r_32f && accessor->stride == floats_per_element * sizeof(cgltf_float)) + { + memcpy(out, element, element_count * floats_per_element * sizeof(cgltf_float)); + } + else + { + cgltf_float* dest = out; + + for (cgltf_size index = 0; index < element_count; index++, dest += floats_per_element, element += accessor->stride) + { + if (!cgltf_element_read_float(element, accessor->type, accessor->component_type, accessor->normalized, dest, floats_per_element)) + { + return 0; + } + } + } + } + + // Second pass: write out each element in the sparse accessor. + if (accessor->is_sparse) + { + const cgltf_accessor_sparse* sparse = &accessor->sparse; + + const uint8_t* index_data = cgltf_buffer_view_data(sparse->indices_buffer_view); + const uint8_t* reader_head = cgltf_buffer_view_data(sparse->values_buffer_view); + + if (index_data == NULL || reader_head == NULL) + { + return 0; + } + + index_data += sparse->indices_byte_offset; + reader_head += sparse->values_byte_offset; + + cgltf_size index_stride = cgltf_component_size(sparse->indices_component_type); + for (cgltf_size reader_index = 0; reader_index < sparse->count; reader_index++, index_data += index_stride, reader_head += accessor->stride) + { + size_t writer_index = cgltf_component_read_index(index_data, sparse->indices_component_type); + float* writer_head = out + writer_index * floats_per_element; + + if (!cgltf_element_read_float(reader_head, accessor->type, accessor->component_type, accessor->normalized, writer_head, floats_per_element)) + { + return 0; + } + } + } + + return element_count * floats_per_element; +} + +static cgltf_uint cgltf_component_read_uint(const void* in, cgltf_component_type component_type) +{ + switch (component_type) + { + case cgltf_component_type_r_8: + return *((const int8_t*) in); + + case cgltf_component_type_r_8u: + return *((const uint8_t*) in); + + case cgltf_component_type_r_16: + return *((const int16_t*) in); + + case cgltf_component_type_r_16u: + return *((const uint16_t*) in); + + case cgltf_component_type_r_32u: + return *((const uint32_t*) in); + + default: + return 0; + } +} + +static cgltf_bool cgltf_element_read_uint(const uint8_t* element, cgltf_type type, cgltf_component_type component_type, cgltf_uint* out, cgltf_size element_size) +{ + cgltf_size num_components = cgltf_num_components(type); + + if (element_size < num_components) + { + return 0; + } + + // Reading integer matrices is not a valid use case + if (type == cgltf_type_mat2 || type == cgltf_type_mat3 || type == cgltf_type_mat4) + { + return 0; + } + + cgltf_size component_size = cgltf_component_size(component_type); + + for (cgltf_size i = 0; i < num_components; ++i) + { + out[i] = cgltf_component_read_uint(element + component_size * i, component_type); + } + return 1; +} + +cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size) +{ + if (accessor->is_sparse) + { + return 0; + } + if (accessor->buffer_view == NULL) + { + memset(out, 0, element_size * sizeof( cgltf_uint )); + return 1; + } + const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); + if (element == NULL) + { + return 0; + } + element += accessor->offset + accessor->stride * index; + return cgltf_element_read_uint(element, accessor->type, accessor->component_type, out, element_size); +} + +cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index) +{ + if (accessor->is_sparse) + { + return 0; // This is an error case, but we can't communicate the error with existing interface. + } + if (accessor->buffer_view == NULL) + { + return 0; + } + const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); + if (element == NULL) + { + return 0; // This is an error case, but we can't communicate the error with existing interface. + } + element += accessor->offset + accessor->stride * index; + return cgltf_component_read_index(element, accessor->component_type); +} + +cgltf_size cgltf_mesh_index(const cgltf_data* data, const cgltf_mesh* object) +{ + assert(object && (cgltf_size)(object - data->meshes) < data->meshes_count); + return (cgltf_size)(object - data->meshes); +} + +cgltf_size cgltf_material_index(const cgltf_data* data, const cgltf_material* object) +{ + assert(object && (cgltf_size)(object - data->materials) < data->materials_count); + return (cgltf_size)(object - data->materials); +} + +cgltf_size cgltf_accessor_index(const cgltf_data* data, const cgltf_accessor* object) +{ + assert(object && (cgltf_size)(object - data->accessors) < data->accessors_count); + return (cgltf_size)(object - data->accessors); +} + +cgltf_size cgltf_buffer_view_index(const cgltf_data* data, const cgltf_buffer_view* object) +{ + assert(object && (cgltf_size)(object - data->buffer_views) < data->buffer_views_count); + return (cgltf_size)(object - data->buffer_views); +} + +cgltf_size cgltf_buffer_index(const cgltf_data* data, const cgltf_buffer* object) +{ + assert(object && (cgltf_size)(object - data->buffers) < data->buffers_count); + return (cgltf_size)(object - data->buffers); +} + +cgltf_size cgltf_image_index(const cgltf_data* data, const cgltf_image* object) +{ + assert(object && (cgltf_size)(object - data->images) < data->images_count); + return (cgltf_size)(object - data->images); +} + +cgltf_size cgltf_texture_index(const cgltf_data* data, const cgltf_texture* object) +{ + assert(object && (cgltf_size)(object - data->textures) < data->textures_count); + return (cgltf_size)(object - data->textures); +} + +cgltf_size cgltf_sampler_index(const cgltf_data* data, const cgltf_sampler* object) +{ + assert(object && (cgltf_size)(object - data->samplers) < data->samplers_count); + return (cgltf_size)(object - data->samplers); +} + +cgltf_size cgltf_skin_index(const cgltf_data* data, const cgltf_skin* object) +{ + assert(object && (cgltf_size)(object - data->skins) < data->skins_count); + return (cgltf_size)(object - data->skins); +} + +cgltf_size cgltf_camera_index(const cgltf_data* data, const cgltf_camera* object) +{ + assert(object && (cgltf_size)(object - data->cameras) < data->cameras_count); + return (cgltf_size)(object - data->cameras); +} + +cgltf_size cgltf_light_index(const cgltf_data* data, const cgltf_light* object) +{ + assert(object && (cgltf_size)(object - data->lights) < data->lights_count); + return (cgltf_size)(object - data->lights); +} + +cgltf_size cgltf_node_index(const cgltf_data* data, const cgltf_node* object) +{ + assert(object && (cgltf_size)(object - data->nodes) < data->nodes_count); + return (cgltf_size)(object - data->nodes); +} + +cgltf_size cgltf_scene_index(const cgltf_data* data, const cgltf_scene* object) +{ + assert(object && (cgltf_size)(object - data->scenes) < data->scenes_count); + return (cgltf_size)(object - data->scenes); +} + +cgltf_size cgltf_animation_index(const cgltf_data* data, const cgltf_animation* object) +{ + assert(object && (cgltf_size)(object - data->animations) < data->animations_count); + return (cgltf_size)(object - data->animations); +} + +cgltf_size cgltf_animation_sampler_index(const cgltf_animation* animation, const cgltf_animation_sampler* object) +{ + assert(object && (cgltf_size)(object - animation->samplers) < animation->samplers_count); + return (cgltf_size)(object - animation->samplers); +} + +cgltf_size cgltf_animation_channel_index(const cgltf_animation* animation, const cgltf_animation_channel* object) +{ + assert(object && (cgltf_size)(object - animation->channels) < animation->channels_count); + return (cgltf_size)(object - animation->channels); +} + +cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, cgltf_uint* out, cgltf_size index_count) +{ + if (out == NULL) + { + return accessor->count; + } + + index_count = accessor->count < index_count ? accessor->count : index_count; + + if (accessor->is_sparse) + { + return 0; + } + if (accessor->buffer_view == NULL) + { + return 0; + } + const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); + if (element == NULL) + { + return 0; + } + element += accessor->offset; + + if (accessor->component_type == cgltf_component_type_r_32u && accessor->stride == sizeof(cgltf_uint)) + { + memcpy(out, element, index_count * sizeof(cgltf_uint)); + } + else + { + cgltf_uint* dest = out; + + for (cgltf_size index = 0; index < index_count; index++, dest++, element += accessor->stride) + { + *dest = (cgltf_uint)cgltf_component_read_index(element, accessor->component_type); + } + } + + return index_count; +} + +#define CGLTF_ERROR_JSON -1 +#define CGLTF_ERROR_NOMEM -2 +#define CGLTF_ERROR_LEGACY -3 + +#define CGLTF_CHECK_TOKTYPE(tok_, type_) if ((tok_).type != (type_)) { return CGLTF_ERROR_JSON; } +#define CGLTF_CHECK_TOKTYPE_RETTYPE(tok_, type_, ret_) if ((tok_).type != (type_)) { return (ret_)CGLTF_ERROR_JSON; } +#define CGLTF_CHECK_KEY(tok_) if ((tok_).type != JSMN_STRING || (tok_).size == 0) { return CGLTF_ERROR_JSON; } /* checking size for 0 verifies that a value follows the key */ + +#define CGLTF_PTRINDEX(type, idx) (type*)((cgltf_size)idx + 1) +#define CGLTF_PTRFIXUP(var, data, size) if (var) { if ((cgltf_size)var > size) { return CGLTF_ERROR_JSON; } var = &data[(cgltf_size)var-1]; } +#define CGLTF_PTRFIXUP_REQ(var, data, size) if (!var || (cgltf_size)var > size) { return CGLTF_ERROR_JSON; } var = &data[(cgltf_size)var-1]; + +static int cgltf_json_strcmp(jsmntok_t const* tok, const uint8_t* json_chunk, const char* str) +{ + CGLTF_CHECK_TOKTYPE(*tok, JSMN_STRING); + size_t const str_len = strlen(str); + size_t const name_length = (size_t)(tok->end - tok->start); + return (str_len == name_length) ? strncmp((const char*)json_chunk + tok->start, str, str_len) : 128; +} + +static int cgltf_json_to_int(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); + char tmp[128]; + int size = (size_t)(tok->end - tok->start) < sizeof(tmp) ? (int)(tok->end - tok->start) : (int)(sizeof(tmp) - 1); + strncpy(tmp, (const char*)json_chunk + tok->start, size); + tmp[size] = 0; + return CGLTF_ATOI(tmp); +} + +static cgltf_size cgltf_json_to_size(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + CGLTF_CHECK_TOKTYPE_RETTYPE(*tok, JSMN_PRIMITIVE, cgltf_size); + char tmp[128]; + int size = (size_t)(tok->end - tok->start) < sizeof(tmp) ? (int)(tok->end - tok->start) : (int)(sizeof(tmp) - 1); + strncpy(tmp, (const char*)json_chunk + tok->start, size); + tmp[size] = 0; + return (cgltf_size)CGLTF_ATOLL(tmp); +} + +static cgltf_float cgltf_json_to_float(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); + char tmp[128]; + int size = (size_t)(tok->end - tok->start) < sizeof(tmp) ? (int)(tok->end - tok->start) : (int)(sizeof(tmp) - 1); + strncpy(tmp, (const char*)json_chunk + tok->start, size); + tmp[size] = 0; + return (cgltf_float)CGLTF_ATOF(tmp); +} + +static cgltf_bool cgltf_json_to_bool(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + int size = (int)(tok->end - tok->start); + return size == 4 && memcmp(json_chunk + tok->start, "true", 4) == 0; +} + +static int cgltf_skip_json(jsmntok_t const* tokens, int i) +{ + int end = i + 1; + + while (i < end) + { + switch (tokens[i].type) + { + case JSMN_OBJECT: + end += tokens[i].size * 2; + break; + + case JSMN_ARRAY: + end += tokens[i].size; + break; + + case JSMN_PRIMITIVE: + case JSMN_STRING: + break; + + default: + return -1; + } + + i++; + } + + return i; +} + +static void cgltf_fill_float_array(float* out_array, int size, float value) +{ + for (int j = 0; j < size; ++j) + { + out_array[j] = value; + } +} + +static int cgltf_parse_json_float_array(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, float* out_array, int size) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + if (tokens[i].size != size) + { + return CGLTF_ERROR_JSON; + } + ++i; + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_array[j] = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + return i; +} + +static int cgltf_parse_json_string(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, char** out_string) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_STRING); + if (*out_string) + { + return CGLTF_ERROR_JSON; + } + int size = (int)(tokens[i].end - tokens[i].start); + char* result = (char*)options->memory.alloc_func(options->memory.user_data, size + 1); + if (!result) + { + return CGLTF_ERROR_NOMEM; + } + strncpy(result, (const char*)json_chunk + tokens[i].start, size); + result[size] = 0; + *out_string = result; + return i + 1; +} + +static int cgltf_parse_json_array(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, size_t element_size, void** out_array, cgltf_size* out_size) +{ + (void)json_chunk; + if (tokens[i].type != JSMN_ARRAY) + { + return tokens[i].type == JSMN_OBJECT ? CGLTF_ERROR_LEGACY : CGLTF_ERROR_JSON; + } + if (*out_array) + { + return CGLTF_ERROR_JSON; + } + int size = tokens[i].size; + void* result = cgltf_calloc(options, element_size, size); + if (!result) + { + return CGLTF_ERROR_NOMEM; + } + *out_array = result; + *out_size = size; + return i + 1; +} + +static int cgltf_parse_json_string_array(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, char*** out_array, cgltf_size* out_size) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(char*), (void**)out_array, out_size); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < *out_size; ++j) + { + i = cgltf_parse_json_string(options, tokens, i, json_chunk, j + (*out_array)); + if (i < 0) + { + return i; + } + } + return i; +} + +static void cgltf_parse_attribute_type(const char* name, cgltf_attribute_type* out_type, int* out_index) +{ + if (*name == '_') + { + *out_type = cgltf_attribute_type_custom; + return; + } + + const char* us = strchr(name, '_'); + size_t len = us ? (size_t)(us - name) : strlen(name); + + if (len == 8 && strncmp(name, "POSITION", 8) == 0) + { + *out_type = cgltf_attribute_type_position; + } + else if (len == 6 && strncmp(name, "NORMAL", 6) == 0) + { + *out_type = cgltf_attribute_type_normal; + } + else if (len == 7 && strncmp(name, "TANGENT", 7) == 0) + { + *out_type = cgltf_attribute_type_tangent; + } + else if (len == 8 && strncmp(name, "TEXCOORD", 8) == 0) + { + *out_type = cgltf_attribute_type_texcoord; + } + else if (len == 5 && strncmp(name, "COLOR", 5) == 0) + { + *out_type = cgltf_attribute_type_color; + } + else if (len == 6 && strncmp(name, "JOINTS", 6) == 0) + { + *out_type = cgltf_attribute_type_joints; + } + else if (len == 7 && strncmp(name, "WEIGHTS", 7) == 0) + { + *out_type = cgltf_attribute_type_weights; + } + else + { + *out_type = cgltf_attribute_type_invalid; + } + + if (us && *out_type != cgltf_attribute_type_invalid) + { + *out_index = CGLTF_ATOI(us + 1); + } +} + +static int cgltf_parse_json_attribute_list(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_attribute** out_attributes, cgltf_size* out_attributes_count) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + if (*out_attributes) + { + return CGLTF_ERROR_JSON; + } + + *out_attributes_count = tokens[i].size; + *out_attributes = (cgltf_attribute*)cgltf_calloc(options, sizeof(cgltf_attribute), *out_attributes_count); + ++i; + + if (!*out_attributes) + { + return CGLTF_ERROR_NOMEM; + } + + for (cgltf_size j = 0; j < *out_attributes_count; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + i = cgltf_parse_json_string(options, tokens, i, json_chunk, &(*out_attributes)[j].name); + if (i < 0) + { + return CGLTF_ERROR_JSON; + } + + cgltf_parse_attribute_type((*out_attributes)[j].name, &(*out_attributes)[j].type, &(*out_attributes)[j].index); + + (*out_attributes)[j].data = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + + return i; +} + +static int cgltf_parse_json_extras(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extras* out_extras) +{ + if (out_extras->data) + { + return CGLTF_ERROR_JSON; + } + + /* fill deprecated fields for now, this will be removed in the future */ + out_extras->start_offset = tokens[i].start; + out_extras->end_offset = tokens[i].end; + + size_t start = tokens[i].start; + size_t size = tokens[i].end - start; + out_extras->data = (char*)options->memory.alloc_func(options->memory.user_data, size + 1); + if (!out_extras->data) + { + return CGLTF_ERROR_NOMEM; + } + strncpy(out_extras->data, (const char*)json_chunk + start, size); + out_extras->data[size] = '\0'; + + i = cgltf_skip_json(tokens, i); + return i; +} + +static int cgltf_parse_json_unprocessed_extension(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extension* out_extension) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_STRING); + CGLTF_CHECK_TOKTYPE(tokens[i+1], JSMN_OBJECT); + if (out_extension->name) + { + return CGLTF_ERROR_JSON; + } + + cgltf_size name_length = tokens[i].end - tokens[i].start; + out_extension->name = (char*)options->memory.alloc_func(options->memory.user_data, name_length + 1); + if (!out_extension->name) + { + return CGLTF_ERROR_NOMEM; + } + strncpy(out_extension->name, (const char*)json_chunk + tokens[i].start, name_length); + out_extension->name[name_length] = 0; + i++; + + size_t start = tokens[i].start; + size_t size = tokens[i].end - start; + out_extension->data = (char*)options->memory.alloc_func(options->memory.user_data, size + 1); + if (!out_extension->data) + { + return CGLTF_ERROR_NOMEM; + } + strncpy(out_extension->data, (const char*)json_chunk + start, size); + out_extension->data[size] = '\0'; + + i = cgltf_skip_json(tokens, i); + + return i; +} + +static int cgltf_parse_json_unprocessed_extensions(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_size* out_extensions_count, cgltf_extension** out_extensions) +{ + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(*out_extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + *out_extensions_count = 0; + *out_extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!*out_extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + + for (int j = 0; j < extensions_size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + cgltf_size extension_index = (*out_extensions_count)++; + cgltf_extension* extension = &((*out_extensions)[extension_index]); + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, extension); + + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_draco_mesh_compression(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_draco_mesh_compression* out_draco_mesh_compression) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "attributes") == 0) + { + i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_draco_mesh_compression->attributes, &out_draco_mesh_compression->attributes_count); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "bufferView") == 0) + { + ++i; + out_draco_mesh_compression->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_mesh_gpu_instancing(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_mesh_gpu_instancing* out_mesh_gpu_instancing) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "attributes") == 0) + { + i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_mesh_gpu_instancing->attributes, &out_mesh_gpu_instancing->attributes_count); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material_mapping* out_mappings, cgltf_size* offset) +{ + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int obj_size = tokens[i].size; + ++i; + + int material = -1; + int variants_tok = -1; + int extras_tok = -1; + + for (int k = 0; k < obj_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "material") == 0) + { + ++i; + material = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "variants") == 0) + { + variants_tok = i+1; + CGLTF_CHECK_TOKTYPE(tokens[variants_tok], JSMN_ARRAY); + + i = cgltf_skip_json(tokens, i+1); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + extras_tok = i + 1; + i = cgltf_skip_json(tokens, extras_tok); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + if (material < 0 || variants_tok < 0) + { + return CGLTF_ERROR_JSON; + } + + if (out_mappings) + { + for (int k = 0; k < tokens[variants_tok].size; ++k) + { + int variant = cgltf_json_to_int(&tokens[variants_tok + 1 + k], json_chunk); + if (variant < 0) + return variant; + + out_mappings[*offset].material = CGLTF_PTRINDEX(cgltf_material, material); + out_mappings[*offset].variant = variant; + + if (extras_tok >= 0) + { + int e = cgltf_parse_json_extras(options, tokens, extras_tok, json_chunk, &out_mappings[*offset].extras); + if (e < 0) + return e; + } + + (*offset)++; + } + } + else + { + (*offset) += tokens[variants_tok].size; + } + } + + return i; +} + +static int cgltf_parse_json_material_mappings(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_primitive* out_prim) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "mappings") == 0) + { + if (out_prim->mappings) + { + return CGLTF_ERROR_JSON; + } + + cgltf_size mappings_offset = 0; + int k = cgltf_parse_json_material_mapping_data(options, tokens, i + 1, json_chunk, NULL, &mappings_offset); + if (k < 0) + { + return k; + } + + out_prim->mappings_count = mappings_offset; + out_prim->mappings = (cgltf_material_mapping*)cgltf_calloc(options, sizeof(cgltf_material_mapping), out_prim->mappings_count); + + mappings_offset = 0; + i = cgltf_parse_json_material_mapping_data(options, tokens, i + 1, json_chunk, out_prim->mappings, &mappings_offset); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_primitive* out_prim) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_prim->type = cgltf_primitive_type_triangles; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0) + { + ++i; + out_prim->type + = (cgltf_primitive_type) + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0) + { + ++i; + out_prim->indices = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "material") == 0) + { + ++i; + out_prim->material = CGLTF_PTRINDEX(cgltf_material, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "attributes") == 0) + { + i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_prim->attributes, &out_prim->attributes_count); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "targets") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_morph_target), (void**)&out_prim->targets, &out_prim->targets_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_prim->targets_count; ++k) + { + i = cgltf_parse_json_attribute_list(options, tokens, i, json_chunk, &out_prim->targets[k].attributes, &out_prim->targets[k].attributes_count); + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_prim->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_prim->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + out_prim->extensions_count = 0; + out_prim->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!out_prim->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_draco_mesh_compression") == 0) + { + out_prim->has_draco_mesh_compression = 1; + i = cgltf_parse_json_draco_mesh_compression(options, tokens, i + 1, json_chunk, &out_prim->draco_mesh_compression); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_variants") == 0) + { + i = cgltf_parse_json_material_mappings(options, tokens, i + 1, json_chunk, out_prim); + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_prim->extensions[out_prim->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_mesh(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_mesh* out_mesh) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_mesh->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "primitives") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_primitive), (void**)&out_mesh->primitives, &out_mesh->primitives_count); + if (i < 0) + { + return i; + } + + for (cgltf_size prim_index = 0; prim_index < out_mesh->primitives_count; ++prim_index) + { + i = cgltf_parse_json_primitive(options, tokens, i, json_chunk, &out_mesh->primitives[prim_index]); + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "weights") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_float), (void**)&out_mesh->weights, &out_mesh->weights_count); + if (i < 0) + { + return i; + } + + i = cgltf_parse_json_float_array(tokens, i - 1, json_chunk, out_mesh->weights, (int)out_mesh->weights_count); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + ++i; + + out_mesh->extras.start_offset = tokens[i].start; + out_mesh->extras.end_offset = tokens[i].end; + + if (tokens[i].type == JSMN_OBJECT) + { + int extras_size = tokens[i].size; + ++i; + + for (int k = 0; k < extras_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "targetNames") == 0 && tokens[i+1].type == JSMN_ARRAY) + { + i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_mesh->target_names, &out_mesh->target_names_count); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i); + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_mesh->extensions_count, &out_mesh->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_meshes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_mesh), (void**)&out_data->meshes, &out_data->meshes_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->meshes_count; ++j) + { + i = cgltf_parse_json_mesh(options, tokens, i, json_chunk, &out_data->meshes[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static cgltf_component_type cgltf_json_to_component_type(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + int type = cgltf_json_to_int(tok, json_chunk); + + switch (type) + { + case 5120: + return cgltf_component_type_r_8; + case 5121: + return cgltf_component_type_r_8u; + case 5122: + return cgltf_component_type_r_16; + case 5123: + return cgltf_component_type_r_16u; + case 5125: + return cgltf_component_type_r_32u; + case 5126: + return cgltf_component_type_r_32f; + default: + return cgltf_component_type_invalid; + } +} + +static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_accessor_sparse* out_sparse) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) + { + ++i; + out_sparse->count = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int indices_size = tokens[i].size; + ++i; + + for (int k = 0; k < indices_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) + { + ++i; + out_sparse->indices_buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_sparse->indices_byte_offset = cgltf_json_to_size(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0) + { + ++i; + out_sparse->indices_component_type = cgltf_json_to_component_type(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->indices_extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sparse->indices_extensions_count, &out_sparse->indices_extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "values") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int values_size = tokens[i].size; + ++i; + + for (int k = 0; k < values_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) + { + ++i; + out_sparse->values_buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_sparse->values_byte_offset = cgltf_json_to_size(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->values_extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sparse->values_extensions_count, &out_sparse->values_extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sparse->extensions_count, &out_sparse->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_accessor(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_accessor* out_accessor) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_accessor->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) + { + ++i; + out_accessor->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_accessor->offset = + cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0) + { + ++i; + out_accessor->component_type = cgltf_json_to_component_type(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "normalized") == 0) + { + ++i; + out_accessor->normalized = cgltf_json_to_bool(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) + { + ++i; + out_accessor->count = + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens+i, json_chunk, "SCALAR") == 0) + { + out_accessor->type = cgltf_type_scalar; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC2") == 0) + { + out_accessor->type = cgltf_type_vec2; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC3") == 0) + { + out_accessor->type = cgltf_type_vec3; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC4") == 0) + { + out_accessor->type = cgltf_type_vec4; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT2") == 0) + { + out_accessor->type = cgltf_type_mat2; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT3") == 0) + { + out_accessor->type = cgltf_type_mat3; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT4") == 0) + { + out_accessor->type = cgltf_type_mat4; + } + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "min") == 0) + { + ++i; + out_accessor->has_min = 1; + // note: we can't parse the precise number of elements since type may not have been computed yet + int min_size = tokens[i].size > 16 ? 16 : tokens[i].size; + i = cgltf_parse_json_float_array(tokens, i, json_chunk, out_accessor->min, min_size); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "max") == 0) + { + ++i; + out_accessor->has_max = 1; + // note: we can't parse the precise number of elements since type may not have been computed yet + int max_size = tokens[i].size > 16 ? 16 : tokens[i].size; + i = cgltf_parse_json_float_array(tokens, i, json_chunk, out_accessor->max, max_size); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "sparse") == 0) + { + out_accessor->is_sparse = 1; + i = cgltf_parse_json_accessor_sparse(options, tokens, i + 1, json_chunk, &out_accessor->sparse); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_accessor->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_accessor->extensions_count, &out_accessor->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_texture_transform(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture_transform* out_texture_transform) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "offset") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_texture_transform->offset, 2); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "rotation") == 0) + { + ++i; + out_texture_transform->rotation = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_texture_transform->scale, 2); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0) + { + ++i; + out_texture_transform->has_texcoord = 1; + out_texture_transform->texcoord = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_texture_view(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture_view* out_texture_view) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_texture_view->scale = 1.0f; + cgltf_fill_float_array(out_texture_view->transform.scale, 2, 1.0f); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "index") == 0) + { + ++i; + out_texture_view->texture = CGLTF_PTRINDEX(cgltf_texture, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0) + { + ++i; + out_texture_view->texcoord = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0) + { + ++i; + out_texture_view->scale = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "strength") == 0) + { + ++i; + out_texture_view->scale = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_texture_view->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_texture_view->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + out_texture_view->extensions_count = 0; + out_texture_view->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!out_texture_view->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_texture_transform") == 0) + { + out_texture_view->has_transform = 1; + i = cgltf_parse_json_texture_transform(tokens, i + 1, json_chunk, &out_texture_view->transform); + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_texture_view->extensions[out_texture_view->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_pbr_metallic_roughness(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_pbr_metallic_roughness* out_pbr) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "metallicFactor") == 0) + { + ++i; + out_pbr->metallic_factor = + cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "roughnessFactor") == 0) + { + ++i; + out_pbr->roughness_factor = + cgltf_json_to_float(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->base_color_factor, 4); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, + &out_pbr->base_color_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "metallicRoughnessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, + &out_pbr->metallic_roughness_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_pbr_specular_glossiness(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_pbr_specular_glossiness* out_pbr) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "diffuseFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->diffuse_factor, 4); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->specular_factor, 3); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "glossinessFactor") == 0) + { + ++i; + out_pbr->glossiness_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "diffuseTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->diffuse_texture); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularGlossinessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->specular_glossiness_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_clearcoat(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_clearcoat* out_clearcoat) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatFactor") == 0) + { + ++i; + out_clearcoat->clearcoat_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatRoughnessFactor") == 0) + { + ++i; + out_clearcoat->clearcoat_roughness_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_texture); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatRoughnessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_roughness_texture); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatNormalTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_normal_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_ior(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_ior* out_ior) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + // Default values + out_ior->ior = 1.5f; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "ior") == 0) + { + ++i; + out_ior->ior = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_specular(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_specular* out_specular) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + // Default values + out_specular->specular_factor = 1.0f; + cgltf_fill_float_array(out_specular->specular_color_factor, 3, 1.0f); + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "specularFactor") == 0) + { + ++i; + out_specular->specular_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularColorFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_specular->specular_color_factor, 3); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_specular->specular_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "specularColorTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_specular->specular_color_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_transmission(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_transmission* out_transmission) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "transmissionFactor") == 0) + { + ++i; + out_transmission->transmission_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "transmissionTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_transmission->transmission_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_volume(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_volume* out_volume) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "thicknessFactor") == 0) + { + ++i; + out_volume->thickness_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "thicknessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_volume->thickness_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "attenuationColor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_volume->attenuation_color, 3); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "attenuationDistance") == 0) + { + ++i; + out_volume->attenuation_distance = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_sheen(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_sheen* out_sheen) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenColorFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_sheen->sheen_color_factor, 3); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenColorTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_sheen->sheen_color_texture); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenRoughnessFactor") == 0) + { + ++i; + out_sheen->sheen_roughness_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenRoughnessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_sheen->sheen_roughness_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_emissive_strength(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_emissive_strength* out_emissive_strength) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + // Default + out_emissive_strength->emissive_strength = 1.f; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "emissiveStrength") == 0) + { + ++i; + out_emissive_strength->emissive_strength = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_iridescence(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_iridescence* out_iridescence) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + // Default + out_iridescence->iridescence_ior = 1.3f; + out_iridescence->iridescence_thickness_min = 100.f; + out_iridescence->iridescence_thickness_max = 400.f; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceFactor") == 0) + { + ++i; + out_iridescence->iridescence_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_iridescence->iridescence_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceIor") == 0) + { + ++i; + out_iridescence->iridescence_ior = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceThicknessMinimum") == 0) + { + ++i; + out_iridescence->iridescence_thickness_min = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceThicknessMaximum") == 0) + { + ++i; + out_iridescence->iridescence_thickness_max = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceThicknessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_iridescence->iridescence_thickness_texture); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_anisotropy(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_anisotropy* out_anisotropy) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "anisotropyStrength") == 0) + { + ++i; + out_anisotropy->anisotropy_strength = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "anisotropyRotation") == 0) + { + ++i; + out_anisotropy->anisotropy_rotation = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "anisotropyTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_anisotropy->anisotropy_texture); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_image* out_image) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "uri") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->uri); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) + { + ++i; + out_image->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "mimeType") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->mime_type); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->name); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_image->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_image->extensions_count, &out_image->extensions); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_sampler* out_sampler) +{ + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_sampler->wrap_s = 10497; + out_sampler->wrap_t = 10497; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_sampler->name); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "magFilter") == 0) + { + ++i; + out_sampler->mag_filter + = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "minFilter") == 0) + { + ++i; + out_sampler->min_filter + = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapS") == 0) + { + ++i; + out_sampler->wrap_s + = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapT") == 0) + { + ++i; + out_sampler->wrap_t + = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sampler->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sampler->extensions_count, &out_sampler->extensions); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture* out_texture) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_texture->name); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "sampler") == 0) + { + ++i; + out_texture->sampler = CGLTF_PTRINDEX(cgltf_sampler, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) + { + ++i; + out_texture->image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_texture->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if (out_texture->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + ++i; + out_texture->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + out_texture->extensions_count = 0; + + if (!out_texture->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_texture_basisu") == 0) + { + out_texture->has_basisu = 1; + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int num_properties = tokens[i].size; + ++i; + + for (int t = 0; t < num_properties; ++t) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) + { + ++i; + out_texture->basisu_image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_texture->extensions[out_texture->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material* out_material) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + cgltf_fill_float_array(out_material->pbr_metallic_roughness.base_color_factor, 4, 1.0f); + out_material->pbr_metallic_roughness.metallic_factor = 1.0f; + out_material->pbr_metallic_roughness.roughness_factor = 1.0f; + + cgltf_fill_float_array(out_material->pbr_specular_glossiness.diffuse_factor, 4, 1.0f); + cgltf_fill_float_array(out_material->pbr_specular_glossiness.specular_factor, 3, 1.0f); + out_material->pbr_specular_glossiness.glossiness_factor = 1.0f; + + cgltf_fill_float_array(out_material->volume.attenuation_color, 3, 1.0f); + out_material->volume.attenuation_distance = FLT_MAX; + + out_material->alpha_cutoff = 0.5f; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_material->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "pbrMetallicRoughness") == 0) + { + out_material->has_pbr_metallic_roughness = 1; + i = cgltf_parse_json_pbr_metallic_roughness(options, tokens, i + 1, json_chunk, &out_material->pbr_metallic_roughness); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "emissiveFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_material->emissive_factor, 3); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "normalTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, + &out_material->normal_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "occlusionTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, + &out_material->occlusion_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "emissiveTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, + &out_material->emissive_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "alphaMode") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens + i, json_chunk, "OPAQUE") == 0) + { + out_material->alpha_mode = cgltf_alpha_mode_opaque; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "MASK") == 0) + { + out_material->alpha_mode = cgltf_alpha_mode_mask; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "BLEND") == 0) + { + out_material->alpha_mode = cgltf_alpha_mode_blend; + } + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "alphaCutoff") == 0) + { + ++i; + out_material->alpha_cutoff = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "doubleSided") == 0) + { + ++i; + out_material->double_sided = + cgltf_json_to_bool(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_material->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_material->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + ++i; + out_material->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + out_material->extensions_count= 0; + + if (!out_material->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_pbrSpecularGlossiness") == 0) + { + out_material->has_pbr_specular_glossiness = 1; + i = cgltf_parse_json_pbr_specular_glossiness(options, tokens, i + 1, json_chunk, &out_material->pbr_specular_glossiness); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_unlit") == 0) + { + out_material->unlit = 1; + i = cgltf_skip_json(tokens, i+1); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_clearcoat") == 0) + { + out_material->has_clearcoat = 1; + i = cgltf_parse_json_clearcoat(options, tokens, i + 1, json_chunk, &out_material->clearcoat); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_ior") == 0) + { + out_material->has_ior = 1; + i = cgltf_parse_json_ior(tokens, i + 1, json_chunk, &out_material->ior); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_specular") == 0) + { + out_material->has_specular = 1; + i = cgltf_parse_json_specular(options, tokens, i + 1, json_chunk, &out_material->specular); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_transmission") == 0) + { + out_material->has_transmission = 1; + i = cgltf_parse_json_transmission(options, tokens, i + 1, json_chunk, &out_material->transmission); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_volume") == 0) + { + out_material->has_volume = 1; + i = cgltf_parse_json_volume(options, tokens, i + 1, json_chunk, &out_material->volume); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_sheen") == 0) + { + out_material->has_sheen = 1; + i = cgltf_parse_json_sheen(options, tokens, i + 1, json_chunk, &out_material->sheen); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_emissive_strength") == 0) + { + out_material->has_emissive_strength = 1; + i = cgltf_parse_json_emissive_strength(tokens, i + 1, json_chunk, &out_material->emissive_strength); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_iridescence") == 0) + { + out_material->has_iridescence = 1; + i = cgltf_parse_json_iridescence(options, tokens, i + 1, json_chunk, &out_material->iridescence); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_anisotropy") == 0) + { + out_material->has_anisotropy = 1; + i = cgltf_parse_json_anisotropy(options, tokens, i + 1, json_chunk, &out_material->anisotropy); + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_material->extensions[out_material->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_accessors(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_accessor), (void**)&out_data->accessors, &out_data->accessors_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->accessors_count; ++j) + { + i = cgltf_parse_json_accessor(options, tokens, i, json_chunk, &out_data->accessors[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_materials(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_material), (void**)&out_data->materials, &out_data->materials_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->materials_count; ++j) + { + i = cgltf_parse_json_material(options, tokens, i, json_chunk, &out_data->materials[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_images(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_image), (void**)&out_data->images, &out_data->images_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->images_count; ++j) + { + i = cgltf_parse_json_image(options, tokens, i, json_chunk, &out_data->images[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_textures(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_texture), (void**)&out_data->textures, &out_data->textures_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->textures_count; ++j) + { + i = cgltf_parse_json_texture(options, tokens, i, json_chunk, &out_data->textures[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_samplers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_sampler), (void**)&out_data->samplers, &out_data->samplers_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->samplers_count; ++j) + { + i = cgltf_parse_json_sampler(options, tokens, i, json_chunk, &out_data->samplers[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_meshopt_compression(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_meshopt_compression* out_meshopt_compression) +{ + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "buffer") == 0) + { + ++i; + out_meshopt_compression->buffer = CGLTF_PTRINDEX(cgltf_buffer, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_meshopt_compression->offset = cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) + { + ++i; + out_meshopt_compression->size = cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0) + { + ++i; + out_meshopt_compression->stride = cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) + { + ++i; + out_meshopt_compression->count = cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens+i, json_chunk, "ATTRIBUTES") == 0) + { + out_meshopt_compression->mode = cgltf_meshopt_compression_mode_attributes; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "TRIANGLES") == 0) + { + out_meshopt_compression->mode = cgltf_meshopt_compression_mode_triangles; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "INDICES") == 0) + { + out_meshopt_compression->mode = cgltf_meshopt_compression_mode_indices; + } + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "filter") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens+i, json_chunk, "NONE") == 0) + { + out_meshopt_compression->filter = cgltf_meshopt_compression_filter_none; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "OCTAHEDRAL") == 0) + { + out_meshopt_compression->filter = cgltf_meshopt_compression_filter_octahedral; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "QUATERNION") == 0) + { + out_meshopt_compression->filter = cgltf_meshopt_compression_filter_quaternion; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "EXPONENTIAL") == 0) + { + out_meshopt_compression->filter = cgltf_meshopt_compression_filter_exponential; + } + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_buffer_view(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_buffer_view* out_buffer_view) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer_view->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "buffer") == 0) + { + ++i; + out_buffer_view->buffer = CGLTF_PTRINDEX(cgltf_buffer, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_buffer_view->offset = + cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) + { + ++i; + out_buffer_view->size = + cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0) + { + ++i; + out_buffer_view->stride = + cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "target") == 0) + { + ++i; + int type = cgltf_json_to_int(tokens+i, json_chunk); + switch (type) + { + case 34962: + type = cgltf_buffer_view_type_vertices; + break; + case 34963: + type = cgltf_buffer_view_type_indices; + break; + default: + type = cgltf_buffer_view_type_invalid; + break; + } + out_buffer_view->type = (cgltf_buffer_view_type)type; + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_buffer_view->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_buffer_view->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + out_buffer_view->extensions_count = 0; + out_buffer_view->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!out_buffer_view->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "EXT_meshopt_compression") == 0) + { + out_buffer_view->has_meshopt_compression = 1; + i = cgltf_parse_json_meshopt_compression(options, tokens, i + 1, json_chunk, &out_buffer_view->meshopt_compression); + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_buffer_view->extensions[out_buffer_view->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_buffer_views(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_buffer_view), (void**)&out_data->buffer_views, &out_data->buffer_views_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->buffer_views_count; ++j) + { + i = cgltf_parse_json_buffer_view(options, tokens, i, json_chunk, &out_data->buffer_views[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_buffer(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_buffer* out_buffer) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) + { + ++i; + out_buffer->size = + cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "uri") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer->uri); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_buffer->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_buffer->extensions_count, &out_buffer->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_buffers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_buffer), (void**)&out_data->buffers, &out_data->buffers_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->buffers_count; ++j) + { + i = cgltf_parse_json_buffer(options, tokens, i, json_chunk, &out_data->buffers[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_skin(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_skin* out_skin) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_skin->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "joints") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_skin->joints, &out_skin->joints_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_skin->joints_count; ++k) + { + out_skin->joints[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "skeleton") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_skin->skeleton = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "inverseBindMatrices") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_skin->inverse_bind_matrices = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_skin->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_skin->extensions_count, &out_skin->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_skins(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_skin), (void**)&out_data->skins, &out_data->skins_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->skins_count; ++j) + { + i = cgltf_parse_json_skin(options, tokens, i, json_chunk, &out_data->skins[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_camera* out_camera) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_camera->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "perspective") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + if (out_camera->type != cgltf_camera_type_invalid) + { + return CGLTF_ERROR_JSON; + } + + out_camera->type = cgltf_camera_type_perspective; + + for (int k = 0; k < data_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "aspectRatio") == 0) + { + ++i; + out_camera->data.perspective.has_aspect_ratio = 1; + out_camera->data.perspective.aspect_ratio = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "yfov") == 0) + { + ++i; + out_camera->data.perspective.yfov = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "zfar") == 0) + { + ++i; + out_camera->data.perspective.has_zfar = 1; + out_camera->data.perspective.zfar = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "znear") == 0) + { + ++i; + out_camera->data.perspective.znear = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->data.perspective.extras); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "orthographic") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + if (out_camera->type != cgltf_camera_type_invalid) + { + return CGLTF_ERROR_JSON; + } + + out_camera->type = cgltf_camera_type_orthographic; + + for (int k = 0; k < data_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "xmag") == 0) + { + ++i; + out_camera->data.orthographic.xmag = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "ymag") == 0) + { + ++i; + out_camera->data.orthographic.ymag = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "zfar") == 0) + { + ++i; + out_camera->data.orthographic.zfar = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "znear") == 0) + { + ++i; + out_camera->data.orthographic.znear = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->data.orthographic.extras); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_camera->extensions_count, &out_camera->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_cameras(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_camera), (void**)&out_data->cameras, &out_data->cameras_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->cameras_count; ++j) + { + i = cgltf_parse_json_camera(options, tokens, i, json_chunk, &out_data->cameras[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_light(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_light* out_light) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_light->color[0] = 1.f; + out_light->color[1] = 1.f; + out_light->color[2] = 1.f; + out_light->intensity = 1.f; + + out_light->spot_inner_cone_angle = 0.f; + out_light->spot_outer_cone_angle = 3.1415926535f / 4.0f; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_light->name); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "color") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_light->color, 3); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "intensity") == 0) + { + ++i; + out_light->intensity = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens + i, json_chunk, "directional") == 0) + { + out_light->type = cgltf_light_type_directional; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "point") == 0) + { + out_light->type = cgltf_light_type_point; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "spot") == 0) + { + out_light->type = cgltf_light_type_spot; + } + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "range") == 0) + { + ++i; + out_light->range = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "spot") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + for (int k = 0; k < data_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "innerConeAngle") == 0) + { + ++i; + out_light->spot_inner_cone_angle = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "outerConeAngle") == 0) + { + ++i; + out_light->spot_outer_cone_angle = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_light->extras); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_lights(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_light), (void**)&out_data->lights, &out_data->lights_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->lights_count; ++j) + { + i = cgltf_parse_json_light(options, tokens, i, json_chunk, &out_data->lights[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_node(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_node* out_node) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_node->rotation[3] = 1.0f; + out_node->scale[0] = 1.0f; + out_node->scale[1] = 1.0f; + out_node->scale[2] = 1.0f; + out_node->matrix[0] = 1.0f; + out_node->matrix[5] = 1.0f; + out_node->matrix[10] = 1.0f; + out_node->matrix[15] = 1.0f; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_node->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "children") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_node->children, &out_node->children_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_node->children_count; ++k) + { + out_node->children[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "mesh") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_node->mesh = CGLTF_PTRINDEX(cgltf_mesh, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "skin") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_node->skin = CGLTF_PTRINDEX(cgltf_skin, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "camera") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_node->camera = CGLTF_PTRINDEX(cgltf_camera, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "translation") == 0) + { + out_node->has_translation = 1; + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->translation, 3); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "rotation") == 0) + { + out_node->has_rotation = 1; + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->rotation, 4); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "scale") == 0) + { + out_node->has_scale = 1; + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->scale, 3); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "matrix") == 0) + { + out_node->has_matrix = 1; + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->matrix, 16); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "weights") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_float), (void**)&out_node->weights, &out_node->weights_count); + if (i < 0) + { + return i; + } + + i = cgltf_parse_json_float_array(tokens, i - 1, json_chunk, out_node->weights, (int)out_node->weights_count); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_node->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_node->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + out_node->extensions_count= 0; + out_node->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!out_node->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_lights_punctual") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + for (int m = 0; m < data_size; ++m) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "light") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_node->light = CGLTF_PTRINDEX(cgltf_light, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "EXT_mesh_gpu_instancing") == 0) + { + out_node->has_mesh_gpu_instancing = 1; + i = cgltf_parse_json_mesh_gpu_instancing(options, tokens, i + 1, json_chunk, &out_node->mesh_gpu_instancing); + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_node->extensions[out_node->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_nodes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_node), (void**)&out_data->nodes, &out_data->nodes_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->nodes_count; ++j) + { + i = cgltf_parse_json_node(options, tokens, i, json_chunk, &out_data->nodes[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_scene(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_scene* out_scene) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_scene->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "nodes") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_scene->nodes, &out_scene->nodes_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_scene->nodes_count; ++k) + { + out_scene->nodes[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_scene->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_scene->extensions_count, &out_scene->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_scenes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_scene), (void**)&out_data->scenes, &out_data->scenes_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->scenes_count; ++j) + { + i = cgltf_parse_json_scene(options, tokens, i, json_chunk, &out_data->scenes[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_animation_sampler(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation_sampler* out_sampler) +{ + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "input") == 0) + { + ++i; + out_sampler->input = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "output") == 0) + { + ++i; + out_sampler->output = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "interpolation") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens + i, json_chunk, "LINEAR") == 0) + { + out_sampler->interpolation = cgltf_interpolation_type_linear; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "STEP") == 0) + { + out_sampler->interpolation = cgltf_interpolation_type_step; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "CUBICSPLINE") == 0) + { + out_sampler->interpolation = cgltf_interpolation_type_cubic_spline; + } + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sampler->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sampler->extensions_count, &out_sampler->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_animation_channel(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation_channel* out_channel) +{ + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "sampler") == 0) + { + ++i; + out_channel->sampler = CGLTF_PTRINDEX(cgltf_animation_sampler, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "target") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int target_size = tokens[i].size; + ++i; + + for (int k = 0; k < target_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "node") == 0) + { + ++i; + out_channel->target_node = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "path") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens+i, json_chunk, "translation") == 0) + { + out_channel->target_path = cgltf_animation_path_type_translation; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "rotation") == 0) + { + out_channel->target_path = cgltf_animation_path_type_rotation; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "scale") == 0) + { + out_channel->target_path = cgltf_animation_path_type_scale; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "weights") == 0) + { + out_channel->target_path = cgltf_animation_path_type_weights; + } + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_channel->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_channel->extensions_count, &out_channel->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_animation(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation* out_animation) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_animation->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "samplers") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_animation_sampler), (void**)&out_animation->samplers, &out_animation->samplers_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_animation->samplers_count; ++k) + { + i = cgltf_parse_json_animation_sampler(options, tokens, i, json_chunk, &out_animation->samplers[k]); + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "channels") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_animation_channel), (void**)&out_animation->channels, &out_animation->channels_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_animation->channels_count; ++k) + { + i = cgltf_parse_json_animation_channel(options, tokens, i, json_chunk, &out_animation->channels[k]); + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_animation->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_animation->extensions_count, &out_animation->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_animations(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_animation), (void**)&out_data->animations, &out_data->animations_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->animations_count; ++j) + { + i = cgltf_parse_json_animation(options, tokens, i, json_chunk, &out_data->animations[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_variant(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material_variant* out_variant) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_variant->name); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_variant->extras); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_variants(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_material_variant), (void**)&out_data->variants, &out_data->variants_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->variants_count; ++j) + { + i = cgltf_parse_json_variant(options, tokens, i, json_chunk, &out_data->variants[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_asset(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_asset* out_asset) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "copyright") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->copyright); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "generator") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->generator); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "version") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->version); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "minVersion") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->min_version); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_asset->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_asset->extensions_count, &out_asset->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + if (out_asset->version && CGLTF_ATOF(out_asset->version) < 2) + { + return CGLTF_ERROR_LEGACY; + } + + return i; +} + +cgltf_size cgltf_num_components(cgltf_type type) { + switch (type) + { + case cgltf_type_vec2: + return 2; + case cgltf_type_vec3: + return 3; + case cgltf_type_vec4: + return 4; + case cgltf_type_mat2: + return 4; + case cgltf_type_mat3: + return 9; + case cgltf_type_mat4: + return 16; + case cgltf_type_invalid: + case cgltf_type_scalar: + default: + return 1; + } +} + +cgltf_size cgltf_component_size(cgltf_component_type component_type) { + switch (component_type) + { + case cgltf_component_type_r_8: + case cgltf_component_type_r_8u: + return 1; + case cgltf_component_type_r_16: + case cgltf_component_type_r_16u: + return 2; + case cgltf_component_type_r_32u: + case cgltf_component_type_r_32f: + return 4; + case cgltf_component_type_invalid: + default: + return 0; + } +} + +cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type) +{ + cgltf_size component_size = cgltf_component_size(component_type); + if (type == cgltf_type_mat2 && component_size == 1) + { + return 8 * component_size; + } + else if (type == cgltf_type_mat3 && (component_size == 1 || component_size == 2)) + { + return 12 * component_size; + } + return component_size * cgltf_num_components(type); +} + +static int cgltf_fixup_pointers(cgltf_data* out_data); + +static int cgltf_parse_json_root(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "asset") == 0) + { + i = cgltf_parse_json_asset(options, tokens, i + 1, json_chunk, &out_data->asset); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "meshes") == 0) + { + i = cgltf_parse_json_meshes(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "accessors") == 0) + { + i = cgltf_parse_json_accessors(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "bufferViews") == 0) + { + i = cgltf_parse_json_buffer_views(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "buffers") == 0) + { + i = cgltf_parse_json_buffers(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "materials") == 0) + { + i = cgltf_parse_json_materials(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "images") == 0) + { + i = cgltf_parse_json_images(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "textures") == 0) + { + i = cgltf_parse_json_textures(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "samplers") == 0) + { + i = cgltf_parse_json_samplers(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "skins") == 0) + { + i = cgltf_parse_json_skins(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "cameras") == 0) + { + i = cgltf_parse_json_cameras(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "nodes") == 0) + { + i = cgltf_parse_json_nodes(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "scenes") == 0) + { + i = cgltf_parse_json_scenes(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "scene") == 0) + { + ++i; + out_data->scene = CGLTF_PTRINDEX(cgltf_scene, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "animations") == 0) + { + i = cgltf_parse_json_animations(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_data->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_data->data_extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + out_data->data_extensions_count = 0; + out_data->data_extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!out_data->data_extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_lights_punctual") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + for (int m = 0; m < data_size; ++m) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "lights") == 0) + { + i = cgltf_parse_json_lights(options, tokens, i + 1, json_chunk, out_data); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_variants") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + for (int m = 0; m < data_size; ++m) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "variants") == 0) + { + i = cgltf_parse_json_variants(options, tokens, i + 1, json_chunk, out_data); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_data->data_extensions[out_data->data_extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensionsUsed") == 0) + { + i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_data->extensions_used, &out_data->extensions_used_count); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensionsRequired") == 0) + { + i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_data->extensions_required, &out_data->extensions_required_count); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data** out_data) +{ + jsmn_parser parser = { 0, 0, 0 }; + + if (options->json_token_count == 0) + { + int token_count = jsmn_parse(&parser, (const char*)json_chunk, size, NULL, 0); + + if (token_count <= 0) + { + return cgltf_result_invalid_json; + } + + options->json_token_count = token_count; + } + + jsmntok_t* tokens = (jsmntok_t*)options->memory.alloc_func(options->memory.user_data, sizeof(jsmntok_t) * (options->json_token_count + 1)); + + if (!tokens) + { + return cgltf_result_out_of_memory; + } + + jsmn_init(&parser); + + int token_count = jsmn_parse(&parser, (const char*)json_chunk, size, tokens, options->json_token_count); + + if (token_count <= 0) + { + options->memory.free_func(options->memory.user_data, tokens); + return cgltf_result_invalid_json; + } + + // this makes sure that we always have an UNDEFINED token at the end of the stream + // for invalid JSON inputs this makes sure we don't perform out of bound reads of token data + tokens[token_count].type = JSMN_UNDEFINED; + + cgltf_data* data = (cgltf_data*)options->memory.alloc_func(options->memory.user_data, sizeof(cgltf_data)); + + if (!data) + { + options->memory.free_func(options->memory.user_data, tokens); + return cgltf_result_out_of_memory; + } + + memset(data, 0, sizeof(cgltf_data)); + data->memory = options->memory; + data->file = options->file; + + int i = cgltf_parse_json_root(options, tokens, 0, json_chunk, data); + + options->memory.free_func(options->memory.user_data, tokens); + + if (i < 0) + { + cgltf_free(data); + + switch (i) + { + case CGLTF_ERROR_NOMEM: return cgltf_result_out_of_memory; + case CGLTF_ERROR_LEGACY: return cgltf_result_legacy_gltf; + default: return cgltf_result_invalid_gltf; + } + } + + if (cgltf_fixup_pointers(data) < 0) + { + cgltf_free(data); + return cgltf_result_invalid_gltf; + } + + data->json = (const char*)json_chunk; + data->json_size = size; + + *out_data = data; + + return cgltf_result_success; +} + +static int cgltf_fixup_pointers(cgltf_data* data) +{ + for (cgltf_size i = 0; i < data->meshes_count; ++i) + { + for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) + { + CGLTF_PTRFIXUP(data->meshes[i].primitives[j].indices, data->accessors, data->accessors_count); + CGLTF_PTRFIXUP(data->meshes[i].primitives[j].material, data->materials, data->materials_count); + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k) + { + CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].attributes[k].data, data->accessors, data->accessors_count); + } + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k) + { + for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m) + { + CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].targets[k].attributes[m].data, data->accessors, data->accessors_count); + } + } + + if (data->meshes[i].primitives[j].has_draco_mesh_compression) + { + CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].draco_mesh_compression.buffer_view, data->buffer_views, data->buffer_views_count); + for (cgltf_size m = 0; m < data->meshes[i].primitives[j].draco_mesh_compression.attributes_count; ++m) + { + CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].draco_mesh_compression.attributes[m].data, data->accessors, data->accessors_count); + } + } + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k) + { + CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].mappings[k].material, data->materials, data->materials_count); + } + } + } + + for (cgltf_size i = 0; i < data->accessors_count; ++i) + { + CGLTF_PTRFIXUP(data->accessors[i].buffer_view, data->buffer_views, data->buffer_views_count); + + if (data->accessors[i].is_sparse) + { + CGLTF_PTRFIXUP_REQ(data->accessors[i].sparse.indices_buffer_view, data->buffer_views, data->buffer_views_count); + CGLTF_PTRFIXUP_REQ(data->accessors[i].sparse.values_buffer_view, data->buffer_views, data->buffer_views_count); + } + + if (data->accessors[i].buffer_view) + { + data->accessors[i].stride = data->accessors[i].buffer_view->stride; + } + + if (data->accessors[i].stride == 0) + { + data->accessors[i].stride = cgltf_calc_size(data->accessors[i].type, data->accessors[i].component_type); + } + } + + for (cgltf_size i = 0; i < data->textures_count; ++i) + { + CGLTF_PTRFIXUP(data->textures[i].image, data->images, data->images_count); + CGLTF_PTRFIXUP(data->textures[i].basisu_image, data->images, data->images_count); + CGLTF_PTRFIXUP(data->textures[i].sampler, data->samplers, data->samplers_count); + } + + for (cgltf_size i = 0; i < data->images_count; ++i) + { + CGLTF_PTRFIXUP(data->images[i].buffer_view, data->buffer_views, data->buffer_views_count); + } + + for (cgltf_size i = 0; i < data->materials_count; ++i) + { + CGLTF_PTRFIXUP(data->materials[i].normal_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].emissive_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].occlusion_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].pbr_metallic_roughness.base_color_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].pbr_specular_glossiness.diffuse_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_roughness_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_normal_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].specular.specular_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].specular.specular_color_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].transmission.transmission_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].volume.thickness_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].sheen.sheen_color_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].sheen.sheen_roughness_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].iridescence.iridescence_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].iridescence.iridescence_thickness_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].anisotropy.anisotropy_texture.texture, data->textures, data->textures_count); + } + + for (cgltf_size i = 0; i < data->buffer_views_count; ++i) + { + CGLTF_PTRFIXUP_REQ(data->buffer_views[i].buffer, data->buffers, data->buffers_count); + + if (data->buffer_views[i].has_meshopt_compression) + { + CGLTF_PTRFIXUP_REQ(data->buffer_views[i].meshopt_compression.buffer, data->buffers, data->buffers_count); + } + } + + for (cgltf_size i = 0; i < data->skins_count; ++i) + { + for (cgltf_size j = 0; j < data->skins[i].joints_count; ++j) + { + CGLTF_PTRFIXUP_REQ(data->skins[i].joints[j], data->nodes, data->nodes_count); + } + + CGLTF_PTRFIXUP(data->skins[i].skeleton, data->nodes, data->nodes_count); + CGLTF_PTRFIXUP(data->skins[i].inverse_bind_matrices, data->accessors, data->accessors_count); + } + + for (cgltf_size i = 0; i < data->nodes_count; ++i) + { + for (cgltf_size j = 0; j < data->nodes[i].children_count; ++j) + { + CGLTF_PTRFIXUP_REQ(data->nodes[i].children[j], data->nodes, data->nodes_count); + + if (data->nodes[i].children[j]->parent) + { + return CGLTF_ERROR_JSON; + } + + data->nodes[i].children[j]->parent = &data->nodes[i]; + } + + CGLTF_PTRFIXUP(data->nodes[i].mesh, data->meshes, data->meshes_count); + CGLTF_PTRFIXUP(data->nodes[i].skin, data->skins, data->skins_count); + CGLTF_PTRFIXUP(data->nodes[i].camera, data->cameras, data->cameras_count); + CGLTF_PTRFIXUP(data->nodes[i].light, data->lights, data->lights_count); + + if (data->nodes[i].has_mesh_gpu_instancing) + { + for (cgltf_size m = 0; m < data->nodes[i].mesh_gpu_instancing.attributes_count; ++m) + { + CGLTF_PTRFIXUP_REQ(data->nodes[i].mesh_gpu_instancing.attributes[m].data, data->accessors, data->accessors_count); + } + } + } + + for (cgltf_size i = 0; i < data->scenes_count; ++i) + { + for (cgltf_size j = 0; j < data->scenes[i].nodes_count; ++j) + { + CGLTF_PTRFIXUP_REQ(data->scenes[i].nodes[j], data->nodes, data->nodes_count); + + if (data->scenes[i].nodes[j]->parent) + { + return CGLTF_ERROR_JSON; + } + } + } + + CGLTF_PTRFIXUP(data->scene, data->scenes, data->scenes_count); + + for (cgltf_size i = 0; i < data->animations_count; ++i) + { + for (cgltf_size j = 0; j < data->animations[i].samplers_count; ++j) + { + CGLTF_PTRFIXUP_REQ(data->animations[i].samplers[j].input, data->accessors, data->accessors_count); + CGLTF_PTRFIXUP_REQ(data->animations[i].samplers[j].output, data->accessors, data->accessors_count); + } + + for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j) + { + CGLTF_PTRFIXUP_REQ(data->animations[i].channels[j].sampler, data->animations[i].samplers, data->animations[i].samplers_count); + CGLTF_PTRFIXUP(data->animations[i].channels[j].target_node, data->nodes, data->nodes_count); + } + } + + return 0; +} + +/* + * -- jsmn.c start -- + * Source: https://github.com/zserge/jsmn + * License: MIT + * + * Copyright (c) 2010 Serge A. Zaitsev + + * 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. + */ + +/** + * Allocates a fresh unused token from the token pull. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, + jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, + ptrdiff_t start, ptrdiff_t end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + ptrdiff_t start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + + ptrdiff_t start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { + /* If it isn't a hex character we have an error */ + if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, size_t num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) { + tokens[parser->toksuper].size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': case ']': + if (tokens == NULL) + break; + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if(token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) return JSMN_ERROR_INVAL; + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + case '\t' : case '\r' : case '\n' : case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +static void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} +/* + * -- jsmn.c end -- + */ + +#endif /* #ifdef CGLTF_IMPLEMENTATION */ + +/* cgltf is distributed under MIT license: + * + * Copyright (c) 2018-2021 Johannes Kuhlmann + + * 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. + */ diff --git a/external/cgltf_write.h b/external/cgltf_write.h new file mode 100644 index 0000000..be22b88 --- /dev/null +++ b/external/cgltf_write.h @@ -0,0 +1,1506 @@ +/** + * cgltf_write - a single-file glTF 2.0 writer written in C99. + * + * Version: 1.13 + * + * Website: https://github.com/jkuhlmann/cgltf + * + * Distributed under the MIT License, see notice at the end of this file. + * + * Building: + * Include this file where you need the struct and function + * declarations. Have exactly one source file where you define + * `CGLTF_WRITE_IMPLEMENTATION` before including this file to get the + * function definitions. + * + * Reference: + * `cgltf_result cgltf_write_file(const cgltf_options* options, const char* + * path, const cgltf_data* data)` writes a glTF data to the given file path. + * If `options->type` is `cgltf_file_type_glb`, both JSON content and binary + * buffer of the given glTF data will be written in a GLB format. + * Otherwise, only the JSON part will be written. + * External buffers and images are not written out. `data` is not deallocated. + * + * `cgltf_size cgltf_write(const cgltf_options* options, char* buffer, + * cgltf_size size, const cgltf_data* data)` writes JSON into the given memory + * buffer. Returns the number of bytes written to `buffer`, including a null + * terminator. If buffer is null, returns the number of bytes that would have + * been written. `data` is not deallocated. + */ +#ifndef CGLTF_WRITE_H_INCLUDED__ +#define CGLTF_WRITE_H_INCLUDED__ + +#include "cgltf.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +cgltf_result cgltf_write_file(const cgltf_options* options, const char* path, const cgltf_data* data); +cgltf_size cgltf_write(const cgltf_options* options, char* buffer, cgltf_size size, const cgltf_data* data); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef CGLTF_WRITE_H_INCLUDED__ */ + +/* + * + * Stop now, if you are only interested in the API. + * Below, you find the implementation. + * + */ + +#if defined(__INTELLISENSE__) || defined(__JETBRAINS_IDE__) +/* This makes MSVC/CLion intellisense work. */ +#define CGLTF_WRITE_IMPLEMENTATION +#endif + +#ifdef CGLTF_WRITE_IMPLEMENTATION + +#include +#include +#include +#include +#include +#include + +#define CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM (1 << 0) +#define CGLTF_EXTENSION_FLAG_MATERIALS_UNLIT (1 << 1) +#define CGLTF_EXTENSION_FLAG_SPECULAR_GLOSSINESS (1 << 2) +#define CGLTF_EXTENSION_FLAG_LIGHTS_PUNCTUAL (1 << 3) +#define CGLTF_EXTENSION_FLAG_DRACO_MESH_COMPRESSION (1 << 4) +#define CGLTF_EXTENSION_FLAG_MATERIALS_CLEARCOAT (1 << 5) +#define CGLTF_EXTENSION_FLAG_MATERIALS_IOR (1 << 6) +#define CGLTF_EXTENSION_FLAG_MATERIALS_SPECULAR (1 << 7) +#define CGLTF_EXTENSION_FLAG_MATERIALS_TRANSMISSION (1 << 8) +#define CGLTF_EXTENSION_FLAG_MATERIALS_SHEEN (1 << 9) +#define CGLTF_EXTENSION_FLAG_MATERIALS_VARIANTS (1 << 10) +#define CGLTF_EXTENSION_FLAG_MATERIALS_VOLUME (1 << 11) +#define CGLTF_EXTENSION_FLAG_TEXTURE_BASISU (1 << 12) +#define CGLTF_EXTENSION_FLAG_MATERIALS_EMISSIVE_STRENGTH (1 << 13) +#define CGLTF_EXTENSION_FLAG_MESH_GPU_INSTANCING (1 << 14) +#define CGLTF_EXTENSION_FLAG_MATERIALS_IRIDESCENCE (1 << 15) +#define CGLTF_EXTENSION_FLAG_MATERIALS_ANISOTROPY (1 << 16) + +typedef struct { + char* buffer; + cgltf_size buffer_size; + cgltf_size remaining; + char* cursor; + cgltf_size tmp; + cgltf_size chars_written; + const cgltf_data* data; + int depth; + const char* indent; + int needs_comma; + uint32_t extension_flags; + uint32_t required_extension_flags; +} cgltf_write_context; + +#define CGLTF_MIN(a, b) (a < b ? a : b) + +#ifdef FLT_DECIMAL_DIG + // FLT_DECIMAL_DIG is C11 + #define CGLTF_DECIMAL_DIG (FLT_DECIMAL_DIG) +#else + #define CGLTF_DECIMAL_DIG 9 +#endif + +#define CGLTF_SPRINTF(...) { \ + assert(context->cursor || (!context->cursor && context->remaining == 0)); \ + context->tmp = snprintf ( context->cursor, context->remaining, __VA_ARGS__ ); \ + context->chars_written += context->tmp; \ + if (context->cursor) { \ + context->cursor += context->tmp; \ + context->remaining -= context->tmp; \ + } } + +#define CGLTF_SNPRINTF(length, ...) { \ + assert(context->cursor || (!context->cursor && context->remaining == 0)); \ + context->tmp = snprintf ( context->cursor, CGLTF_MIN(length + 1, context->remaining), __VA_ARGS__ ); \ + context->chars_written += length; \ + if (context->cursor) { \ + context->cursor += length; \ + context->remaining -= length; \ + } } + +#define CGLTF_WRITE_IDXPROP(label, val, start) if (val) { \ + cgltf_write_indent(context); \ + CGLTF_SPRINTF("\"%s\": %d", label, (int) (val - start)); \ + context->needs_comma = 1; } + +#define CGLTF_WRITE_IDXARRPROP(label, dim, vals, start) if (vals) { \ + cgltf_write_indent(context); \ + CGLTF_SPRINTF("\"%s\": [", label); \ + for (int i = 0; i < (int)(dim); ++i) { \ + int idx = (int) (vals[i] - start); \ + if (i != 0) CGLTF_SPRINTF(","); \ + CGLTF_SPRINTF(" %d", idx); \ + } \ + CGLTF_SPRINTF(" ]"); \ + context->needs_comma = 1; } + +#define CGLTF_WRITE_TEXTURE_INFO(label, info) if (info.texture) { \ + cgltf_write_line(context, "\"" label "\": {"); \ + CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \ + cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \ + if (info.has_transform) { \ + context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \ + cgltf_write_texture_transform(context, &info.transform); \ + } \ + cgltf_write_extras(context, &info.extras); \ + cgltf_write_line(context, "}"); } + +#define CGLTF_WRITE_NORMAL_TEXTURE_INFO(label, info) if (info.texture) { \ + cgltf_write_line(context, "\"" label "\": {"); \ + CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \ + cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \ + cgltf_write_floatprop(context, "scale", info.scale, 1.0f); \ + if (info.has_transform) { \ + context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \ + cgltf_write_texture_transform(context, &info.transform); \ + } \ + cgltf_write_extras(context, &info.extras); \ + cgltf_write_line(context, "}"); } + +#define CGLTF_WRITE_OCCLUSION_TEXTURE_INFO(label, info) if (info.texture) { \ + cgltf_write_line(context, "\"" label "\": {"); \ + CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \ + cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \ + cgltf_write_floatprop(context, "strength", info.scale, 1.0f); \ + if (info.has_transform) { \ + context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \ + cgltf_write_texture_transform(context, &info.transform); \ + } \ + cgltf_write_extras(context, &info.extras); \ + cgltf_write_line(context, "}"); } + +#ifndef CGLTF_CONSTS +static const cgltf_size GlbHeaderSize = 12; +static const cgltf_size GlbChunkHeaderSize = 8; +static const uint32_t GlbVersion = 2; +static const uint32_t GlbMagic = 0x46546C67; +static const uint32_t GlbMagicJsonChunk = 0x4E4F534A; +static const uint32_t GlbMagicBinChunk = 0x004E4942; +#define CGLTF_CONSTS +#endif + +static void cgltf_write_indent(cgltf_write_context* context) +{ + if (context->needs_comma) + { + CGLTF_SPRINTF(",\n"); + context->needs_comma = 0; + } + else + { + CGLTF_SPRINTF("\n"); + } + for (int i = 0; i < context->depth; ++i) + { + CGLTF_SPRINTF("%s", context->indent); + } +} + +static void cgltf_write_line(cgltf_write_context* context, const char* line) +{ + if (line[0] == ']' || line[0] == '}') + { + --context->depth; + context->needs_comma = 0; + } + cgltf_write_indent(context); + CGLTF_SPRINTF("%s", line); + cgltf_size last = (cgltf_size)(strlen(line) - 1); + if (line[0] == ']' || line[0] == '}') + { + context->needs_comma = 1; + } + if (line[last] == '[' || line[last] == '{') + { + ++context->depth; + context->needs_comma = 0; + } +} + +static void cgltf_write_strprop(cgltf_write_context* context, const char* label, const char* val) +{ + if (val) + { + cgltf_write_indent(context); + CGLTF_SPRINTF("\"%s\": \"%s\"", label, val); + context->needs_comma = 1; + } +} + +static void cgltf_write_extras(cgltf_write_context* context, const cgltf_extras* extras) +{ + if (extras->data) + { + cgltf_write_indent(context); + CGLTF_SPRINTF("\"extras\": %s", extras->data); + context->needs_comma = 1; + } + else + { + cgltf_size length = extras->end_offset - extras->start_offset; + if (length > 0 && context->data->json) + { + char* json_string = ((char*) context->data->json) + extras->start_offset; + cgltf_write_indent(context); + CGLTF_SPRINTF("%s", "\"extras\": "); + CGLTF_SNPRINTF(length, "%.*s", (int)(extras->end_offset - extras->start_offset), json_string); + context->needs_comma = 1; + } + } +} + +static void cgltf_write_stritem(cgltf_write_context* context, const char* item) +{ + cgltf_write_indent(context); + CGLTF_SPRINTF("\"%s\"", item); + context->needs_comma = 1; +} + +static void cgltf_write_intprop(cgltf_write_context* context, const char* label, int val, int def) +{ + if (val != def) + { + cgltf_write_indent(context); + CGLTF_SPRINTF("\"%s\": %d", label, val); + context->needs_comma = 1; + } +} + +static void cgltf_write_sizeprop(cgltf_write_context* context, const char* label, cgltf_size val, cgltf_size def) +{ + if (val != def) + { + cgltf_write_indent(context); + CGLTF_SPRINTF("\"%s\": %zu", label, val); + context->needs_comma = 1; + } +} + +static void cgltf_write_floatprop(cgltf_write_context* context, const char* label, float val, float def) +{ + if (val != def) + { + cgltf_write_indent(context); + CGLTF_SPRINTF("\"%s\": ", label); + CGLTF_SPRINTF("%.*g", CGLTF_DECIMAL_DIG, val); + context->needs_comma = 1; + + if (context->cursor) + { + char *decimal_comma = strchr(context->cursor - context->tmp, ','); + if (decimal_comma) + { + *decimal_comma = '.'; + } + } + } +} + +static void cgltf_write_boolprop_optional(cgltf_write_context* context, const char* label, bool val, bool def) +{ + if (val != def) + { + cgltf_write_indent(context); + CGLTF_SPRINTF("\"%s\": %s", label, val ? "true" : "false"); + context->needs_comma = 1; + } +} + +static void cgltf_write_floatarrayprop(cgltf_write_context* context, const char* label, const cgltf_float* vals, cgltf_size dim) +{ + cgltf_write_indent(context); + CGLTF_SPRINTF("\"%s\": [", label); + for (cgltf_size i = 0; i < dim; ++i) + { + if (i != 0) + { + CGLTF_SPRINTF(", %.*g", CGLTF_DECIMAL_DIG, vals[i]); + } + else + { + CGLTF_SPRINTF("%.*g", CGLTF_DECIMAL_DIG, vals[i]); + } + } + CGLTF_SPRINTF("]"); + context->needs_comma = 1; +} + +static bool cgltf_check_floatarray(const float* vals, int dim, float val) { + while (dim--) + { + if (vals[dim] != val) + { + return true; + } + } + return false; +} + +static int cgltf_int_from_component_type(cgltf_component_type ctype) +{ + switch (ctype) + { + case cgltf_component_type_r_8: return 5120; + case cgltf_component_type_r_8u: return 5121; + case cgltf_component_type_r_16: return 5122; + case cgltf_component_type_r_16u: return 5123; + case cgltf_component_type_r_32u: return 5125; + case cgltf_component_type_r_32f: return 5126; + default: return 0; + } +} + +static const char* cgltf_str_from_alpha_mode(cgltf_alpha_mode alpha_mode) +{ + switch (alpha_mode) + { + case cgltf_alpha_mode_mask: return "MASK"; + case cgltf_alpha_mode_blend: return "BLEND"; + default: return NULL; + } +} + +static const char* cgltf_str_from_type(cgltf_type type) +{ + switch (type) + { + case cgltf_type_scalar: return "SCALAR"; + case cgltf_type_vec2: return "VEC2"; + case cgltf_type_vec3: return "VEC3"; + case cgltf_type_vec4: return "VEC4"; + case cgltf_type_mat2: return "MAT2"; + case cgltf_type_mat3: return "MAT3"; + case cgltf_type_mat4: return "MAT4"; + default: return NULL; + } +} + +static cgltf_size cgltf_dim_from_type(cgltf_type type) +{ + switch (type) + { + case cgltf_type_scalar: return 1; + case cgltf_type_vec2: return 2; + case cgltf_type_vec3: return 3; + case cgltf_type_vec4: return 4; + case cgltf_type_mat2: return 4; + case cgltf_type_mat3: return 9; + case cgltf_type_mat4: return 16; + default: return 0; + } +} + +static const char* cgltf_str_from_camera_type(cgltf_camera_type camera_type) +{ + switch (camera_type) + { + case cgltf_camera_type_perspective: return "perspective"; + case cgltf_camera_type_orthographic: return "orthographic"; + default: return NULL; + } +} + +static const char* cgltf_str_from_light_type(cgltf_light_type light_type) +{ + switch (light_type) + { + case cgltf_light_type_directional: return "directional"; + case cgltf_light_type_point: return "point"; + case cgltf_light_type_spot: return "spot"; + default: return NULL; + } +} + +static void cgltf_write_texture_transform(cgltf_write_context* context, const cgltf_texture_transform* transform) +{ + cgltf_write_line(context, "\"extensions\": {"); + cgltf_write_line(context, "\"KHR_texture_transform\": {"); + if (cgltf_check_floatarray(transform->offset, 2, 0.0f)) + { + cgltf_write_floatarrayprop(context, "offset", transform->offset, 2); + } + cgltf_write_floatprop(context, "rotation", transform->rotation, 0.0f); + if (cgltf_check_floatarray(transform->scale, 2, 1.0f)) + { + cgltf_write_floatarrayprop(context, "scale", transform->scale, 2); + } + if (transform->has_texcoord) + { + cgltf_write_intprop(context, "texCoord", transform->texcoord, -1); + } + cgltf_write_line(context, "}"); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_asset(cgltf_write_context* context, const cgltf_asset* asset) +{ + cgltf_write_line(context, "\"asset\": {"); + cgltf_write_strprop(context, "copyright", asset->copyright); + cgltf_write_strprop(context, "generator", asset->generator); + cgltf_write_strprop(context, "version", asset->version); + cgltf_write_strprop(context, "min_version", asset->min_version); + cgltf_write_extras(context, &asset->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_primitive(cgltf_write_context* context, const cgltf_primitive* prim) +{ + cgltf_write_intprop(context, "mode", (int) prim->type, 4); + CGLTF_WRITE_IDXPROP("indices", prim->indices, context->data->accessors); + CGLTF_WRITE_IDXPROP("material", prim->material, context->data->materials); + cgltf_write_line(context, "\"attributes\": {"); + for (cgltf_size i = 0; i < prim->attributes_count; ++i) + { + const cgltf_attribute* attr = prim->attributes + i; + CGLTF_WRITE_IDXPROP(attr->name, attr->data, context->data->accessors); + } + cgltf_write_line(context, "}"); + + if (prim->targets_count) + { + cgltf_write_line(context, "\"targets\": ["); + for (cgltf_size i = 0; i < prim->targets_count; ++i) + { + cgltf_write_line(context, "{"); + for (cgltf_size j = 0; j < prim->targets[i].attributes_count; ++j) + { + const cgltf_attribute* attr = prim->targets[i].attributes + j; + CGLTF_WRITE_IDXPROP(attr->name, attr->data, context->data->accessors); + } + cgltf_write_line(context, "}"); + } + cgltf_write_line(context, "]"); + } + cgltf_write_extras(context, &prim->extras); + + if (prim->has_draco_mesh_compression || prim->mappings_count > 0) + { + cgltf_write_line(context, "\"extensions\": {"); + + if (prim->has_draco_mesh_compression) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_DRACO_MESH_COMPRESSION; + if (prim->attributes_count == 0 || prim->indices == 0) + { + context->required_extension_flags |= CGLTF_EXTENSION_FLAG_DRACO_MESH_COMPRESSION; + } + + cgltf_write_line(context, "\"KHR_draco_mesh_compression\": {"); + CGLTF_WRITE_IDXPROP("bufferView", prim->draco_mesh_compression.buffer_view, context->data->buffer_views); + cgltf_write_line(context, "\"attributes\": {"); + for (cgltf_size i = 0; i < prim->draco_mesh_compression.attributes_count; ++i) + { + const cgltf_attribute* attr = prim->draco_mesh_compression.attributes + i; + CGLTF_WRITE_IDXPROP(attr->name, attr->data, context->data->accessors); + } + cgltf_write_line(context, "}"); + cgltf_write_line(context, "}"); + } + + if (prim->mappings_count > 0) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_VARIANTS; + cgltf_write_line(context, "\"KHR_materials_variants\": {"); + cgltf_write_line(context, "\"mappings\": ["); + for (cgltf_size i = 0; i < prim->mappings_count; ++i) + { + const cgltf_material_mapping* map = prim->mappings + i; + cgltf_write_line(context, "{"); + CGLTF_WRITE_IDXPROP("material", map->material, context->data->materials); + + cgltf_write_indent(context); + CGLTF_SPRINTF("\"variants\": [%d]", (int)map->variant); + context->needs_comma = 1; + + cgltf_write_extras(context, &map->extras); + cgltf_write_line(context, "}"); + } + cgltf_write_line(context, "]"); + cgltf_write_line(context, "}"); + } + + cgltf_write_line(context, "}"); + } +} + +static void cgltf_write_mesh(cgltf_write_context* context, const cgltf_mesh* mesh) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", mesh->name); + + cgltf_write_line(context, "\"primitives\": ["); + for (cgltf_size i = 0; i < mesh->primitives_count; ++i) + { + cgltf_write_line(context, "{"); + cgltf_write_primitive(context, mesh->primitives + i); + cgltf_write_line(context, "}"); + } + cgltf_write_line(context, "]"); + + if (mesh->weights_count > 0) + { + cgltf_write_floatarrayprop(context, "weights", mesh->weights, mesh->weights_count); + } + + cgltf_write_extras(context, &mesh->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_buffer_view(cgltf_write_context* context, const cgltf_buffer_view* view) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", view->name); + CGLTF_WRITE_IDXPROP("buffer", view->buffer, context->data->buffers); + cgltf_write_sizeprop(context, "byteLength", view->size, (cgltf_size)-1); + cgltf_write_sizeprop(context, "byteOffset", view->offset, 0); + cgltf_write_sizeprop(context, "byteStride", view->stride, 0); + // NOTE: We skip writing "target" because the spec says its usage can be inferred. + cgltf_write_extras(context, &view->extras); + cgltf_write_line(context, "}"); +} + + +static void cgltf_write_buffer(cgltf_write_context* context, const cgltf_buffer* buffer) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", buffer->name); + cgltf_write_strprop(context, "uri", buffer->uri); + cgltf_write_sizeprop(context, "byteLength", buffer->size, (cgltf_size)-1); + cgltf_write_extras(context, &buffer->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_material(cgltf_write_context* context, const cgltf_material* material) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", material->name); + if (material->alpha_mode == cgltf_alpha_mode_mask) + { + cgltf_write_floatprop(context, "alphaCutoff", material->alpha_cutoff, 0.5f); + } + cgltf_write_boolprop_optional(context, "doubleSided", (bool)material->double_sided, false); + // cgltf_write_boolprop_optional(context, "unlit", material->unlit, false); + + if (material->unlit) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_UNLIT; + } + + if (material->has_pbr_specular_glossiness) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_SPECULAR_GLOSSINESS; + } + + if (material->has_clearcoat) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_CLEARCOAT; + } + + if (material->has_transmission) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_TRANSMISSION; + } + + if (material->has_volume) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_VOLUME; + } + + if (material->has_ior) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_IOR; + } + + if (material->has_specular) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_SPECULAR; + } + + if (material->has_sheen) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_SHEEN; + } + + if (material->has_emissive_strength) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_EMISSIVE_STRENGTH; + } + + if (material->has_iridescence) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_IRIDESCENCE; + } + + if (material->has_anisotropy) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_ANISOTROPY; + } + + if (material->has_pbr_metallic_roughness) + { + const cgltf_pbr_metallic_roughness* params = &material->pbr_metallic_roughness; + cgltf_write_line(context, "\"pbrMetallicRoughness\": {"); + CGLTF_WRITE_TEXTURE_INFO("baseColorTexture", params->base_color_texture); + CGLTF_WRITE_TEXTURE_INFO("metallicRoughnessTexture", params->metallic_roughness_texture); + cgltf_write_floatprop(context, "metallicFactor", params->metallic_factor, 1.0f); + cgltf_write_floatprop(context, "roughnessFactor", params->roughness_factor, 1.0f); + if (cgltf_check_floatarray(params->base_color_factor, 4, 1.0f)) + { + cgltf_write_floatarrayprop(context, "baseColorFactor", params->base_color_factor, 4); + } + cgltf_write_line(context, "}"); + } + + if (material->unlit || material->has_pbr_specular_glossiness || material->has_clearcoat || material->has_ior || material->has_specular || material->has_transmission || material->has_sheen || material->has_volume || material->has_emissive_strength || material->has_iridescence || material->has_anisotropy) + { + cgltf_write_line(context, "\"extensions\": {"); + if (material->has_clearcoat) + { + const cgltf_clearcoat* params = &material->clearcoat; + cgltf_write_line(context, "\"KHR_materials_clearcoat\": {"); + CGLTF_WRITE_TEXTURE_INFO("clearcoatTexture", params->clearcoat_texture); + CGLTF_WRITE_TEXTURE_INFO("clearcoatRoughnessTexture", params->clearcoat_roughness_texture); + CGLTF_WRITE_NORMAL_TEXTURE_INFO("clearcoatNormalTexture", params->clearcoat_normal_texture); + cgltf_write_floatprop(context, "clearcoatFactor", params->clearcoat_factor, 0.0f); + cgltf_write_floatprop(context, "clearcoatRoughnessFactor", params->clearcoat_roughness_factor, 0.0f); + cgltf_write_line(context, "}"); + } + if (material->has_ior) + { + const cgltf_ior* params = &material->ior; + cgltf_write_line(context, "\"KHR_materials_ior\": {"); + cgltf_write_floatprop(context, "ior", params->ior, 1.5f); + cgltf_write_line(context, "}"); + } + if (material->has_specular) + { + const cgltf_specular* params = &material->specular; + cgltf_write_line(context, "\"KHR_materials_specular\": {"); + CGLTF_WRITE_TEXTURE_INFO("specularTexture", params->specular_texture); + CGLTF_WRITE_TEXTURE_INFO("specularColorTexture", params->specular_color_texture); + cgltf_write_floatprop(context, "specularFactor", params->specular_factor, 1.0f); + if (cgltf_check_floatarray(params->specular_color_factor, 3, 1.0f)) + { + cgltf_write_floatarrayprop(context, "specularColorFactor", params->specular_color_factor, 3); + } + cgltf_write_line(context, "}"); + } + if (material->has_transmission) + { + const cgltf_transmission* params = &material->transmission; + cgltf_write_line(context, "\"KHR_materials_transmission\": {"); + CGLTF_WRITE_TEXTURE_INFO("transmissionTexture", params->transmission_texture); + cgltf_write_floatprop(context, "transmissionFactor", params->transmission_factor, 0.0f); + cgltf_write_line(context, "}"); + } + if (material->has_volume) + { + const cgltf_volume* params = &material->volume; + cgltf_write_line(context, "\"KHR_materials_volume\": {"); + CGLTF_WRITE_TEXTURE_INFO("thicknessTexture", params->thickness_texture); + cgltf_write_floatprop(context, "thicknessFactor", params->thickness_factor, 0.0f); + if (cgltf_check_floatarray(params->attenuation_color, 3, 1.0f)) + { + cgltf_write_floatarrayprop(context, "attenuationColor", params->attenuation_color, 3); + } + if (params->attenuation_distance < FLT_MAX) + { + cgltf_write_floatprop(context, "attenuationDistance", params->attenuation_distance, FLT_MAX); + } + cgltf_write_line(context, "}"); + } + if (material->has_sheen) + { + const cgltf_sheen* params = &material->sheen; + cgltf_write_line(context, "\"KHR_materials_sheen\": {"); + CGLTF_WRITE_TEXTURE_INFO("sheenColorTexture", params->sheen_color_texture); + CGLTF_WRITE_TEXTURE_INFO("sheenRoughnessTexture", params->sheen_roughness_texture); + if (cgltf_check_floatarray(params->sheen_color_factor, 3, 0.0f)) + { + cgltf_write_floatarrayprop(context, "sheenColorFactor", params->sheen_color_factor, 3); + } + cgltf_write_floatprop(context, "sheenRoughnessFactor", params->sheen_roughness_factor, 0.0f); + cgltf_write_line(context, "}"); + } + if (material->has_pbr_specular_glossiness) + { + const cgltf_pbr_specular_glossiness* params = &material->pbr_specular_glossiness; + cgltf_write_line(context, "\"KHR_materials_pbrSpecularGlossiness\": {"); + CGLTF_WRITE_TEXTURE_INFO("diffuseTexture", params->diffuse_texture); + CGLTF_WRITE_TEXTURE_INFO("specularGlossinessTexture", params->specular_glossiness_texture); + if (cgltf_check_floatarray(params->diffuse_factor, 4, 1.0f)) + { + cgltf_write_floatarrayprop(context, "diffuseFactor", params->diffuse_factor, 4); + } + if (cgltf_check_floatarray(params->specular_factor, 3, 1.0f)) + { + cgltf_write_floatarrayprop(context, "specularFactor", params->specular_factor, 3); + } + cgltf_write_floatprop(context, "glossinessFactor", params->glossiness_factor, 1.0f); + cgltf_write_line(context, "}"); + } + if (material->unlit) + { + cgltf_write_line(context, "\"KHR_materials_unlit\": {}"); + } + if (material->has_emissive_strength) + { + cgltf_write_line(context, "\"KHR_materials_emissive_strength\": {"); + const cgltf_emissive_strength* params = &material->emissive_strength; + cgltf_write_floatprop(context, "emissiveStrength", params->emissive_strength, 1.f); + cgltf_write_line(context, "}"); + } + if (material->has_iridescence) + { + cgltf_write_line(context, "\"KHR_materials_iridescence\": {"); + const cgltf_iridescence* params = &material->iridescence; + cgltf_write_floatprop(context, "iridescenceFactor", params->iridescence_factor, 0.f); + CGLTF_WRITE_TEXTURE_INFO("iridescenceTexture", params->iridescence_texture); + cgltf_write_floatprop(context, "iridescenceIor", params->iridescence_ior, 1.3f); + cgltf_write_floatprop(context, "iridescenceThicknessMinimum", params->iridescence_thickness_min, 100.f); + cgltf_write_floatprop(context, "iridescenceThicknessMaximum", params->iridescence_thickness_max, 400.f); + CGLTF_WRITE_TEXTURE_INFO("iridescenceThicknessTexture", params->iridescence_thickness_texture); + cgltf_write_line(context, "}"); + } + if (material->has_anisotropy) + { + cgltf_write_line(context, "\"KHR_materials_anisotropy\": {"); + const cgltf_anisotropy* params = &material->anisotropy; + cgltf_write_floatprop(context, "anisotropyFactor", params->anisotropy_strength, 0.f); + cgltf_write_floatprop(context, "anisotropyRotation", params->anisotropy_rotation, 0.f); + CGLTF_WRITE_TEXTURE_INFO("anisotropyTexture", params->anisotropy_texture); + cgltf_write_line(context, "}"); + } + cgltf_write_line(context, "}"); + } + + CGLTF_WRITE_NORMAL_TEXTURE_INFO("normalTexture", material->normal_texture); + CGLTF_WRITE_OCCLUSION_TEXTURE_INFO("occlusionTexture", material->occlusion_texture); + CGLTF_WRITE_TEXTURE_INFO("emissiveTexture", material->emissive_texture); + if (cgltf_check_floatarray(material->emissive_factor, 3, 0.0f)) + { + cgltf_write_floatarrayprop(context, "emissiveFactor", material->emissive_factor, 3); + } + cgltf_write_strprop(context, "alphaMode", cgltf_str_from_alpha_mode(material->alpha_mode)); + cgltf_write_extras(context, &material->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_image(cgltf_write_context* context, const cgltf_image* image) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", image->name); + cgltf_write_strprop(context, "uri", image->uri); + CGLTF_WRITE_IDXPROP("bufferView", image->buffer_view, context->data->buffer_views); + cgltf_write_strprop(context, "mimeType", image->mime_type); + cgltf_write_extras(context, &image->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_texture(cgltf_write_context* context, const cgltf_texture* texture) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", texture->name); + CGLTF_WRITE_IDXPROP("source", texture->image, context->data->images); + CGLTF_WRITE_IDXPROP("sampler", texture->sampler, context->data->samplers); + + if (texture->has_basisu) + { + cgltf_write_line(context, "\"extensions\": {"); + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_BASISU; + cgltf_write_line(context, "\"KHR_texture_basisu\": {"); + CGLTF_WRITE_IDXPROP("source", texture->basisu_image, context->data->images); + cgltf_write_line(context, "}"); + } + cgltf_write_line(context, "}"); + } + cgltf_write_extras(context, &texture->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_skin(cgltf_write_context* context, const cgltf_skin* skin) +{ + cgltf_write_line(context, "{"); + CGLTF_WRITE_IDXPROP("skeleton", skin->skeleton, context->data->nodes); + CGLTF_WRITE_IDXPROP("inverseBindMatrices", skin->inverse_bind_matrices, context->data->accessors); + CGLTF_WRITE_IDXARRPROP("joints", skin->joints_count, skin->joints, context->data->nodes); + cgltf_write_strprop(context, "name", skin->name); + cgltf_write_extras(context, &skin->extras); + cgltf_write_line(context, "}"); +} + +static const char* cgltf_write_str_path_type(cgltf_animation_path_type path_type) +{ + switch (path_type) + { + case cgltf_animation_path_type_translation: + return "translation"; + case cgltf_animation_path_type_rotation: + return "rotation"; + case cgltf_animation_path_type_scale: + return "scale"; + case cgltf_animation_path_type_weights: + return "weights"; + default: + break; + } + return "invalid"; +} + +static const char* cgltf_write_str_interpolation_type(cgltf_interpolation_type interpolation_type) +{ + switch (interpolation_type) + { + case cgltf_interpolation_type_linear: + return "LINEAR"; + case cgltf_interpolation_type_step: + return "STEP"; + case cgltf_interpolation_type_cubic_spline: + return "CUBICSPLINE"; + default: + break; + } + return "invalid"; +} + +static void cgltf_write_path_type(cgltf_write_context* context, const char *label, cgltf_animation_path_type path_type) +{ + cgltf_write_strprop(context, label, cgltf_write_str_path_type(path_type)); +} + +static void cgltf_write_interpolation_type(cgltf_write_context* context, const char *label, cgltf_interpolation_type interpolation_type) +{ + cgltf_write_strprop(context, label, cgltf_write_str_interpolation_type(interpolation_type)); +} + +static void cgltf_write_animation_sampler(cgltf_write_context* context, const cgltf_animation_sampler* animation_sampler) +{ + cgltf_write_line(context, "{"); + cgltf_write_interpolation_type(context, "interpolation", animation_sampler->interpolation); + CGLTF_WRITE_IDXPROP("input", animation_sampler->input, context->data->accessors); + CGLTF_WRITE_IDXPROP("output", animation_sampler->output, context->data->accessors); + cgltf_write_extras(context, &animation_sampler->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_animation_channel(cgltf_write_context* context, const cgltf_animation* animation, const cgltf_animation_channel* animation_channel) +{ + cgltf_write_line(context, "{"); + CGLTF_WRITE_IDXPROP("sampler", animation_channel->sampler, animation->samplers); + cgltf_write_line(context, "\"target\": {"); + CGLTF_WRITE_IDXPROP("node", animation_channel->target_node, context->data->nodes); + cgltf_write_path_type(context, "path", animation_channel->target_path); + cgltf_write_line(context, "}"); + cgltf_write_extras(context, &animation_channel->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_animation(cgltf_write_context* context, const cgltf_animation* animation) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", animation->name); + + if (animation->samplers_count > 0) + { + cgltf_write_line(context, "\"samplers\": ["); + for (cgltf_size i = 0; i < animation->samplers_count; ++i) + { + cgltf_write_animation_sampler(context, animation->samplers + i); + } + cgltf_write_line(context, "]"); + } + if (animation->channels_count > 0) + { + cgltf_write_line(context, "\"channels\": ["); + for (cgltf_size i = 0; i < animation->channels_count; ++i) + { + cgltf_write_animation_channel(context, animation, animation->channels + i); + } + cgltf_write_line(context, "]"); + } + cgltf_write_extras(context, &animation->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_sampler(cgltf_write_context* context, const cgltf_sampler* sampler) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", sampler->name); + cgltf_write_intprop(context, "magFilter", sampler->mag_filter, 0); + cgltf_write_intprop(context, "minFilter", sampler->min_filter, 0); + cgltf_write_intprop(context, "wrapS", sampler->wrap_s, 10497); + cgltf_write_intprop(context, "wrapT", sampler->wrap_t, 10497); + cgltf_write_extras(context, &sampler->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_node(cgltf_write_context* context, const cgltf_node* node) +{ + cgltf_write_line(context, "{"); + CGLTF_WRITE_IDXARRPROP("children", node->children_count, node->children, context->data->nodes); + CGLTF_WRITE_IDXPROP("mesh", node->mesh, context->data->meshes); + cgltf_write_strprop(context, "name", node->name); + if (node->has_matrix) + { + cgltf_write_floatarrayprop(context, "matrix", node->matrix, 16); + } + if (node->has_translation) + { + cgltf_write_floatarrayprop(context, "translation", node->translation, 3); + } + if (node->has_rotation) + { + cgltf_write_floatarrayprop(context, "rotation", node->rotation, 4); + } + if (node->has_scale) + { + cgltf_write_floatarrayprop(context, "scale", node->scale, 3); + } + if (node->skin) + { + CGLTF_WRITE_IDXPROP("skin", node->skin, context->data->skins); + } + + bool has_extension = node->light || (node->has_mesh_gpu_instancing && node->mesh_gpu_instancing.attributes_count > 0); + if(has_extension) + cgltf_write_line(context, "\"extensions\": {"); + + if (node->light) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_LIGHTS_PUNCTUAL; + cgltf_write_line(context, "\"KHR_lights_punctual\": {"); + CGLTF_WRITE_IDXPROP("light", node->light, context->data->lights); + cgltf_write_line(context, "}"); + } + + if (node->has_mesh_gpu_instancing && node->mesh_gpu_instancing.attributes_count > 0) + { + context->extension_flags |= CGLTF_EXTENSION_FLAG_MESH_GPU_INSTANCING; + context->required_extension_flags |= CGLTF_EXTENSION_FLAG_MESH_GPU_INSTANCING; + + cgltf_write_line(context, "\"EXT_mesh_gpu_instancing\": {"); + { + cgltf_write_line(context, "\"attributes\": {"); + { + for (cgltf_size i = 0; i < node->mesh_gpu_instancing.attributes_count; ++i) + { + const cgltf_attribute* attr = node->mesh_gpu_instancing.attributes + i; + CGLTF_WRITE_IDXPROP(attr->name, attr->data, context->data->accessors); + } + } + cgltf_write_line(context, "}"); + } + cgltf_write_line(context, "}"); + } + + if (has_extension) + cgltf_write_line(context, "}"); + + if (node->weights_count > 0) + { + cgltf_write_floatarrayprop(context, "weights", node->weights, node->weights_count); + } + + if (node->camera) + { + CGLTF_WRITE_IDXPROP("camera", node->camera, context->data->cameras); + } + + cgltf_write_extras(context, &node->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_scene(cgltf_write_context* context, const cgltf_scene* scene) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", scene->name); + CGLTF_WRITE_IDXARRPROP("nodes", scene->nodes_count, scene->nodes, context->data->nodes); + cgltf_write_extras(context, &scene->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_accessor(cgltf_write_context* context, const cgltf_accessor* accessor) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", accessor->name); + CGLTF_WRITE_IDXPROP("bufferView", accessor->buffer_view, context->data->buffer_views); + cgltf_write_intprop(context, "componentType", cgltf_int_from_component_type(accessor->component_type), 0); + cgltf_write_strprop(context, "type", cgltf_str_from_type(accessor->type)); + cgltf_size dim = cgltf_dim_from_type(accessor->type); + cgltf_write_boolprop_optional(context, "normalized", (bool)accessor->normalized, false); + cgltf_write_sizeprop(context, "byteOffset", (int)accessor->offset, 0); + cgltf_write_intprop(context, "count", (int)accessor->count, -1); + if (accessor->has_min) + { + cgltf_write_floatarrayprop(context, "min", accessor->min, dim); + } + if (accessor->has_max) + { + cgltf_write_floatarrayprop(context, "max", accessor->max, dim); + } + if (accessor->is_sparse) + { + cgltf_write_line(context, "\"sparse\": {"); + cgltf_write_intprop(context, "count", (int)accessor->sparse.count, 0); + cgltf_write_line(context, "\"indices\": {"); + cgltf_write_sizeprop(context, "byteOffset", (int)accessor->sparse.indices_byte_offset, 0); + CGLTF_WRITE_IDXPROP("bufferView", accessor->sparse.indices_buffer_view, context->data->buffer_views); + cgltf_write_intprop(context, "componentType", cgltf_int_from_component_type(accessor->sparse.indices_component_type), 0); + cgltf_write_extras(context, &accessor->sparse.indices_extras); + cgltf_write_line(context, "}"); + cgltf_write_line(context, "\"values\": {"); + cgltf_write_sizeprop(context, "byteOffset", (int)accessor->sparse.values_byte_offset, 0); + CGLTF_WRITE_IDXPROP("bufferView", accessor->sparse.values_buffer_view, context->data->buffer_views); + cgltf_write_extras(context, &accessor->sparse.values_extras); + cgltf_write_line(context, "}"); + cgltf_write_extras(context, &accessor->sparse.extras); + cgltf_write_line(context, "}"); + } + cgltf_write_extras(context, &accessor->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_camera(cgltf_write_context* context, const cgltf_camera* camera) +{ + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "type", cgltf_str_from_camera_type(camera->type)); + if (camera->name) + { + cgltf_write_strprop(context, "name", camera->name); + } + + if (camera->type == cgltf_camera_type_orthographic) + { + cgltf_write_line(context, "\"orthographic\": {"); + cgltf_write_floatprop(context, "xmag", camera->data.orthographic.xmag, -1.0f); + cgltf_write_floatprop(context, "ymag", camera->data.orthographic.ymag, -1.0f); + cgltf_write_floatprop(context, "zfar", camera->data.orthographic.zfar, -1.0f); + cgltf_write_floatprop(context, "znear", camera->data.orthographic.znear, -1.0f); + cgltf_write_extras(context, &camera->data.orthographic.extras); + cgltf_write_line(context, "}"); + } + else if (camera->type == cgltf_camera_type_perspective) + { + cgltf_write_line(context, "\"perspective\": {"); + + if (camera->data.perspective.has_aspect_ratio) { + cgltf_write_floatprop(context, "aspectRatio", camera->data.perspective.aspect_ratio, -1.0f); + } + + cgltf_write_floatprop(context, "yfov", camera->data.perspective.yfov, -1.0f); + + if (camera->data.perspective.has_zfar) { + cgltf_write_floatprop(context, "zfar", camera->data.perspective.zfar, -1.0f); + } + + cgltf_write_floatprop(context, "znear", camera->data.perspective.znear, -1.0f); + cgltf_write_extras(context, &camera->data.perspective.extras); + cgltf_write_line(context, "}"); + } + cgltf_write_extras(context, &camera->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_light(cgltf_write_context* context, const cgltf_light* light) +{ + context->extension_flags |= CGLTF_EXTENSION_FLAG_LIGHTS_PUNCTUAL; + + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "type", cgltf_str_from_light_type(light->type)); + if (light->name) + { + cgltf_write_strprop(context, "name", light->name); + } + if (cgltf_check_floatarray(light->color, 3, 1.0f)) + { + cgltf_write_floatarrayprop(context, "color", light->color, 3); + } + cgltf_write_floatprop(context, "intensity", light->intensity, 1.0f); + cgltf_write_floatprop(context, "range", light->range, 0.0f); + + if (light->type == cgltf_light_type_spot) + { + cgltf_write_line(context, "\"spot\": {"); + cgltf_write_floatprop(context, "innerConeAngle", light->spot_inner_cone_angle, 0.0f); + cgltf_write_floatprop(context, "outerConeAngle", light->spot_outer_cone_angle, 3.14159265358979323846f/4.0f); + cgltf_write_line(context, "}"); + } + cgltf_write_extras( context, &light->extras ); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_variant(cgltf_write_context* context, const cgltf_material_variant* variant) +{ + context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_VARIANTS; + + cgltf_write_line(context, "{"); + cgltf_write_strprop(context, "name", variant->name); + cgltf_write_extras(context, &variant->extras); + cgltf_write_line(context, "}"); +} + +static void cgltf_write_glb(FILE* file, const void* json_buf, const cgltf_size json_size, const void* bin_buf, const cgltf_size bin_size) +{ + char header[GlbHeaderSize]; + char chunk_header[GlbChunkHeaderSize]; + char json_pad[3] = { 0x20, 0x20, 0x20 }; + char bin_pad[3] = { 0, 0, 0 }; + + cgltf_size json_padsize = (json_size % 4 != 0) ? 4 - json_size % 4 : 0; + cgltf_size bin_padsize = (bin_size % 4 != 0) ? 4 - bin_size % 4 : 0; + cgltf_size total_size = GlbHeaderSize + GlbChunkHeaderSize + json_size + json_padsize; + if (bin_buf != NULL && bin_size > 0) { + total_size += GlbChunkHeaderSize + bin_size + bin_padsize; + } + + // Write a GLB header + memcpy(header, &GlbMagic, 4); + memcpy(header + 4, &GlbVersion, 4); + memcpy(header + 8, &total_size, 4); + fwrite(header, 1, GlbHeaderSize, file); + + // Write a JSON chunk (header & data) + uint32_t json_chunk_size = (uint32_t)(json_size + json_padsize); + memcpy(chunk_header, &json_chunk_size, 4); + memcpy(chunk_header + 4, &GlbMagicJsonChunk, 4); + fwrite(chunk_header, 1, GlbChunkHeaderSize, file); + + fwrite(json_buf, 1, json_size, file); + fwrite(json_pad, 1, json_padsize, file); + + if (bin_buf != NULL && bin_size > 0) { + // Write a binary chunk (header & data) + uint32_t bin_chunk_size = (uint32_t)(bin_size + bin_padsize); + memcpy(chunk_header, &bin_chunk_size, 4); + memcpy(chunk_header + 4, &GlbMagicBinChunk, 4); + fwrite(chunk_header, 1, GlbChunkHeaderSize, file); + + fwrite(bin_buf, 1, bin_size, file); + fwrite(bin_pad, 1, bin_padsize, file); + } +} + +cgltf_result cgltf_write_file(const cgltf_options* options, const char* path, const cgltf_data* data) +{ + cgltf_size expected = cgltf_write(options, NULL, 0, data); + char* buffer = (char*) malloc(expected); + cgltf_size actual = cgltf_write(options, buffer, expected, data); + if (expected != actual) { + fprintf(stderr, "Error: expected %zu bytes but wrote %zu bytes.\n", expected, actual); + } + FILE* file = fopen(path, "wb"); + if (!file) + { + return cgltf_result_file_not_found; + } + // Note that cgltf_write() includes a null terminator, which we omit from the file content. + if (options->type == cgltf_file_type_glb) { + cgltf_write_glb(file, buffer, actual - 1, data->bin, data->bin_size); + } else { + // Write a plain JSON file. + fwrite(buffer, actual - 1, 1, file); + } + fclose(file); + free(buffer); + return cgltf_result_success; +} + +static void cgltf_write_extensions(cgltf_write_context* context, uint32_t extension_flags) +{ + if (extension_flags & CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM) { + cgltf_write_stritem(context, "KHR_texture_transform"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_UNLIT) { + cgltf_write_stritem(context, "KHR_materials_unlit"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_SPECULAR_GLOSSINESS) { + cgltf_write_stritem(context, "KHR_materials_pbrSpecularGlossiness"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_LIGHTS_PUNCTUAL) { + cgltf_write_stritem(context, "KHR_lights_punctual"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_DRACO_MESH_COMPRESSION) { + cgltf_write_stritem(context, "KHR_draco_mesh_compression"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_CLEARCOAT) { + cgltf_write_stritem(context, "KHR_materials_clearcoat"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_IOR) { + cgltf_write_stritem(context, "KHR_materials_ior"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_SPECULAR) { + cgltf_write_stritem(context, "KHR_materials_specular"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_TRANSMISSION) { + cgltf_write_stritem(context, "KHR_materials_transmission"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_SHEEN) { + cgltf_write_stritem(context, "KHR_materials_sheen"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_VARIANTS) { + cgltf_write_stritem(context, "KHR_materials_variants"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_VOLUME) { + cgltf_write_stritem(context, "KHR_materials_volume"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_TEXTURE_BASISU) { + cgltf_write_stritem(context, "KHR_texture_basisu"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_EMISSIVE_STRENGTH) { + cgltf_write_stritem(context, "KHR_materials_emissive_strength"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_IRIDESCENCE) { + cgltf_write_stritem(context, "KHR_materials_iridescence"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_ANISOTROPY) { + cgltf_write_stritem(context, "KHR_materials_anisotropy"); + } + if (extension_flags & CGLTF_EXTENSION_FLAG_MESH_GPU_INSTANCING) { + cgltf_write_stritem(context, "EXT_mesh_gpu_instancing"); + } +} + +cgltf_size cgltf_write(const cgltf_options* options, char* buffer, cgltf_size size, const cgltf_data* data) +{ + (void)options; + cgltf_write_context ctx; + ctx.buffer = buffer; + ctx.buffer_size = size; + ctx.remaining = size; + ctx.cursor = buffer; + ctx.chars_written = 0; + ctx.data = data; + ctx.depth = 1; + ctx.indent = " "; + ctx.needs_comma = 0; + ctx.extension_flags = 0; + ctx.required_extension_flags = 0; + + cgltf_write_context* context = &ctx; + + CGLTF_SPRINTF("{"); + + if (data->accessors_count > 0) + { + cgltf_write_line(context, "\"accessors\": ["); + for (cgltf_size i = 0; i < data->accessors_count; ++i) + { + cgltf_write_accessor(context, data->accessors + i); + } + cgltf_write_line(context, "]"); + } + + cgltf_write_asset(context, &data->asset); + + if (data->buffer_views_count > 0) + { + cgltf_write_line(context, "\"bufferViews\": ["); + for (cgltf_size i = 0; i < data->buffer_views_count; ++i) + { + cgltf_write_buffer_view(context, data->buffer_views + i); + } + cgltf_write_line(context, "]"); + } + + if (data->buffers_count > 0) + { + cgltf_write_line(context, "\"buffers\": ["); + for (cgltf_size i = 0; i < data->buffers_count; ++i) + { + cgltf_write_buffer(context, data->buffers + i); + } + cgltf_write_line(context, "]"); + } + + if (data->images_count > 0) + { + cgltf_write_line(context, "\"images\": ["); + for (cgltf_size i = 0; i < data->images_count; ++i) + { + cgltf_write_image(context, data->images + i); + } + cgltf_write_line(context, "]"); + } + + if (data->meshes_count > 0) + { + cgltf_write_line(context, "\"meshes\": ["); + for (cgltf_size i = 0; i < data->meshes_count; ++i) + { + cgltf_write_mesh(context, data->meshes + i); + } + cgltf_write_line(context, "]"); + } + + if (data->materials_count > 0) + { + cgltf_write_line(context, "\"materials\": ["); + for (cgltf_size i = 0; i < data->materials_count; ++i) + { + cgltf_write_material(context, data->materials + i); + } + cgltf_write_line(context, "]"); + } + + if (data->nodes_count > 0) + { + cgltf_write_line(context, "\"nodes\": ["); + for (cgltf_size i = 0; i < data->nodes_count; ++i) + { + cgltf_write_node(context, data->nodes + i); + } + cgltf_write_line(context, "]"); + } + + if (data->samplers_count > 0) + { + cgltf_write_line(context, "\"samplers\": ["); + for (cgltf_size i = 0; i < data->samplers_count; ++i) + { + cgltf_write_sampler(context, data->samplers + i); + } + cgltf_write_line(context, "]"); + } + + CGLTF_WRITE_IDXPROP("scene", data->scene, data->scenes); + + if (data->scenes_count > 0) + { + cgltf_write_line(context, "\"scenes\": ["); + for (cgltf_size i = 0; i < data->scenes_count; ++i) + { + cgltf_write_scene(context, data->scenes + i); + } + cgltf_write_line(context, "]"); + } + + if (data->textures_count > 0) + { + cgltf_write_line(context, "\"textures\": ["); + for (cgltf_size i = 0; i < data->textures_count; ++i) + { + cgltf_write_texture(context, data->textures + i); + } + cgltf_write_line(context, "]"); + } + + if (data->skins_count > 0) + { + cgltf_write_line(context, "\"skins\": ["); + for (cgltf_size i = 0; i < data->skins_count; ++i) + { + cgltf_write_skin(context, data->skins + i); + } + cgltf_write_line(context, "]"); + } + + if (data->animations_count > 0) + { + cgltf_write_line(context, "\"animations\": ["); + for (cgltf_size i = 0; i < data->animations_count; ++i) + { + cgltf_write_animation(context, data->animations + i); + } + cgltf_write_line(context, "]"); + } + + if (data->cameras_count > 0) + { + cgltf_write_line(context, "\"cameras\": ["); + for (cgltf_size i = 0; i < data->cameras_count; ++i) + { + cgltf_write_camera(context, data->cameras + i); + } + cgltf_write_line(context, "]"); + } + + if (data->lights_count > 0 || data->variants_count > 0) + { + cgltf_write_line(context, "\"extensions\": {"); + + if (data->lights_count > 0) + { + cgltf_write_line(context, "\"KHR_lights_punctual\": {"); + cgltf_write_line(context, "\"lights\": ["); + for (cgltf_size i = 0; i < data->lights_count; ++i) + { + cgltf_write_light(context, data->lights + i); + } + cgltf_write_line(context, "]"); + cgltf_write_line(context, "}"); + } + + if (data->variants_count) + { + cgltf_write_line(context, "\"KHR_materials_variants\": {"); + cgltf_write_line(context, "\"variants\": ["); + for (cgltf_size i = 0; i < data->variants_count; ++i) + { + cgltf_write_variant(context, data->variants + i); + } + cgltf_write_line(context, "]"); + cgltf_write_line(context, "}"); + } + + cgltf_write_line(context, "}"); + } + + if (context->extension_flags != 0) + { + cgltf_write_line(context, "\"extensionsUsed\": ["); + cgltf_write_extensions(context, context->extension_flags); + cgltf_write_line(context, "]"); + } + + if (context->required_extension_flags != 0) + { + cgltf_write_line(context, "\"extensionsRequired\": ["); + cgltf_write_extensions(context, context->required_extension_flags); + cgltf_write_line(context, "]"); + } + + cgltf_write_extras(context, &data->extras); + + CGLTF_SPRINTF("\n}\n"); + + // snprintf does not include the null terminator in its return value, so be sure to include it + // in the returned byte count. + return 1 + ctx.chars_written; +} + +#endif /* #ifdef CGLTF_WRITE_IMPLEMENTATION */ + +/* cgltf is distributed under MIT license: + * + * Copyright (c) 2019-2021 Philip Rideout + + * 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. + */ diff --git a/external/stb_image.cpp b/external/stb_image.cpp new file mode 100644 index 0000000..8ddfd1f --- /dev/null +++ b/external/stb_image.cpp @@ -0,0 +1,2 @@ +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" diff --git a/external/stb_image.h b/external/stb_image.h new file mode 100644 index 0000000..5e807a0 --- /dev/null +++ b/external/stb_image.h @@ -0,0 +1,7987 @@ +/* stb_image - v2.28 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.28 (2023-01-29) many error fixes, security errors, just tons of stuff + 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes + 2.26 (2020-07-13) many minor fixes + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine Simon Breuss (16-bit PNM) + John-Mark Allen + Carmelo J Fdez-Aguera + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski + Phil Jordan Dave Moore Roy Eltham + Hayaki Saito Nathan Reed Won Chun + Luke Graham Johan Duparc Nick Verigakis the Horde3D community + Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Eugene Golushkov Laurent Gomila Cort Stratton github:snagar + Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex + Cass Everitt Ryamond Barbiero github:grim210 + Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw + Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus + Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo + Julian Raschke Gregory Mullen Christian Floisand github:darealshinji + Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 + Brad Weinberger Matvey Cherevko github:mosra + Luca Sas Alexander Veselov Zack Middleton [reserved] + Ryan C. Gordon [reserved] [reserved] + DO NOT ADD YOUR NAME HERE + + Jacko Dirks + + To add your name to the credits, pick a random blank space in the middle and fill it. + 80% of merge conflicts on stb PRs are due to people adding their name at the end + of the credits. +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data); +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// To query the width, height and component count of an image without having to +// decode the full file, you can use the stbi_info family of functions: +// +// int x,y,n,ok; +// ok = stbi_info(filename, &x, &y, &n); +// // returns ok=1 and sets x, y, n if image is a supported format, +// // 0 otherwise. +// +// Note that stb_image pervasively uses ints in its public API for sizes, +// including sizes of memory buffers. This is now part of the API and thus +// hard to change without causing breakage. As a result, the various image +// loaders all have certain limits on image size; these differ somewhat +// by format but generally boil down to either just under 2GB or just under +// 1GB. When the decoded image would be larger than this, stb_image decoding +// will fail. +// +// Additionally, stb_image will reject image files that have any of their +// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, +// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, +// the only way to have an image with such dimensions load correctly +// is for it to have a rather extreme aspect ratio. Either way, the +// assumption here is that such larger images are likely to be malformed +// or malicious. If you do need to load an image with individual dimensions +// larger than that, and it still fits in the overall size limit, you can +// #define STBI_MAX_DIMENSIONS on your own to be something larger. +// +// =========================================================================== +// +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy-to-use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// provide more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small source code footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// We optionally support converting iPhone-formatted PNGs (which store +// premultiplied BGRA) back to RGB, even though they're internally encoded +// differently. To enable this conversion, call +// stbi_convert_iphone_png_to_rgb(1). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// +// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater +// than that size (in either width or height) without further processing. +// This is to let programs in the wild set an upper bound to prevent +// denial-of-service attacks on untrusted data, as one could generate a +// valid image of gigantic dimensions and force stb_image to allocate a +// huge block of memory and spend disproportionate time decoding it. By +// default this is set to (1 << 24), which is 16777216, but that's still +// very big. + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +#include +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STBIDEF +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// on most compilers (and ALL modern mainstream compilers) this is threadsafe +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined(__GNUC__) && __GNUC__ < 5 + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define STBI_THREAD_LOCAL _Thread_local + #endif + + #ifndef STBI_THREAD_LOCAL + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif + #endif +#endif + +#if defined(_MSC_VER) || defined(__SYMBIAN32__) +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#endif + +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif + +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +#ifdef _MSC_VER +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#else +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + int callback_already_read; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + int ch; + fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user) || ferror((FILE *) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__pnm_is16(stbi__context *s); +#endif + +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +#ifndef STBI_NO_FAILURE_STRINGS +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} +#endif + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} +#endif + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} +#endif + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow. +static int stbi__addints_valid(int a, int b) +{ + if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow + if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. + return a <= INT_MAX - b; +} + +// returns 1 if the product of two signed shorts is valid, 0 on overflow. +static int stbi__mul2shorts_valid(short a, short b) +{ + if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow + if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid + if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN + return a >= SHRT_MIN / b; +} + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load_global = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + // test the formats with a very explicit header first (at least a FOURCC + // or distinctive magic number first) + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + + // then the formats that can end up attempting to load with just 1 or 2 + // bytes matching expectations; these are prone to false positives, so + // try them later + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +#ifndef STBI_NO_GIF +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} +#endif + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 8) { + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 16) { + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + return 0; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else +static void stbi__skip(stbi__context *s, int n) +{ + if (n == 0) return; // already there! + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} +#endif + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + z += (stbi__uint32)stbi__get16le(s) << 16; + return z; +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) { + for (j=0; j < count[i]; ++j) { + h->size[k++] = (stbi_uc) (i+1); + if(k >= 257) return stbi__err("bad size list","Corrupt JPEG"); + } + } + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + if(c < 0 || c >= 256) // symbol id out of bounds! + return -1; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing + + sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & (sgn - 1)); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static const stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG"); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + diff = t ? stbi__extend_receive(j, t) : 0; + + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG"); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short) (dc * (1 << j->succ_low)); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * (1 << shift)); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values! + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i=0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); + if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +static int stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) +{ + // some JPEGs have junk at end, skip over it but if we find what looks + // like a valid marker, resume there + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + while (x == 255) { // might be a marker + if (stbi__at_eof(j->s)) return STBI__MARKER_none; + x = stbi__get8(j->s); + if (x != 0x00 && x != 0xff) { + // not a stuffed zero or lead-in to another marker, looks + // like an actual marker, return it + return x; + } + // stuffed zero has x=0 now which ends the loop, meaning we go + // back to regular scan loop. + // repeated 0xff keeps trying to read the next byte of the marker. + } + } + return STBI__MARKER_none; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + j->marker = stbi__skip_jpeg_junk_at_end(j); + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + m = stbi__get_marker(j); + if (STBI__RESTART(m)) + m = stbi__get_marker(j); + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + m = stbi__get_marker(j); + } else { + if (!stbi__process_marker(j, m)) return 1; + m = stbi__get_marker(j); + } + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[STBI__ZNSYMS]; + stbi__uint16 value[STBI__ZNSYMS]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + return stbi__zeof(z) ? 0 : *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s >= 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + return -1; /* report error for unexpected end of data. */ + } + stbi__fill_bits(a); + } + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + unsigned int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static const int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + } else if (c == 18) { + c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) return stbi__err("outofmem", "Out of memory"); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load_global = 0; +static int stbi__de_iphone_flag_global = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_global = flag_true_if_should_convert; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global +#define stbi__de_iphone_flag stbi__de_iphone_flag_global +#else +static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; +static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; + +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; +} + +#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ + ? stbi__unpremultiply_on_load_local \ + : stbi__unpremultiply_on_load_global) +#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ + ? stbi__de_iphone_flag_local \ + : stbi__de_iphone_flag_global) +#endif // STBI_THREAD_LOCAL + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]={0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + } + // even with SCAN_header, have to scan to see if we have a tRNS + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. + if (scan == STBI__SCAN_header) { ++s->img_n; return 1; } + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { + // header scan definitely stops at first IDAT + if (pal_img_n) + s->img_n = pal_img_n; + return 1; + } + if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth <= 8) + ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; + else + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(unsigned int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; + int extra_read; +} stbi__bmp_data; + +static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) +{ + // BI_BITFIELDS specifies masks explicitly, don't override + if (compress == 3) + return 1; + + if (compress == 0) { + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; + } + return 0; // error +} + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + stbi__bmp_set_mask_defaults(info, compress); + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->extra_read += 12; + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + // V4/V5 header + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - info.extra_read - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + // accept some number of extra bytes after the header, but if the offset points either to before + // the header ends or implies a large amount of extra data, reject the file as malformed + int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original); + int header_limit = 1024; // max we actually read is below 256 bytes currently. + int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size. + if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) { + return stbi__errpuc("bad header", "Corrupt BMP"); + } + // we established that bytes_read_so_far is positive and sensible. + // the first half of this test rejects offsets that are either too small positives, or + // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn + // ensures the number computed in the second half of the test can't overflow. + if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } else { + stbi__skip(s, info.offset - bytes_read_so_far); + } + } + + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - info.extra_read - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i]; p1[i] = p2[i]; p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + if (!result) return stbi__errpuc("outofmem", "Out of memory"); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!g) return stbi__err("outofmem", "Out of memory"); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) +{ + STBI_FREE(g->out); + STBI_FREE(g->history); + STBI_FREE(g->background); + + if (out) STBI_FREE(out); + if (delays && *delays) STBI_FREE(*delays); + return stbi__errpuc("outofmem", "Out of memory"); +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + int out_size = 0; + int delays_size = 0; + + STBI_NOTUSED(out_size); + STBI_NOTUSED(delays_size); + + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (!tmp) + return stbi__load_gif_main_outofmem(&g, out, delays); + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + + if (delays) { + int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + if (!new_delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; + delays_size = layers * sizeof(int); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (!out) + return stbi__load_gif_main_outofmem(&g, out, delays); + out_size = layers * stride; + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + if (!*delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + delays_size = layers * sizeof(int); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + if (p == NULL) { + stbi__rewind( s ); + return 0; + } + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + STBI_NOTUSED(stbi__get32be(s)); + STBI_NOTUSED(stbi__get32be(s)); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); + if (ri->bits_per_channel == 0) + return 0; + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { + STBI_FREE(out); + return stbi__errpuc("bad PNM", "PNM file truncated"); + } + + if (req_comp && req_comp != s->img_n) { + if (ri->bits_per_channel == 16) { + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y); + } else { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + } + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + if((value > 214748364) || (value == 214748364 && *c > '7')) + return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + if(*x == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + if (*y == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + if (maxv > 65535) + return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; + else + return 8; +} + +static int stbi__pnm_is16(stbi__context *s) +{ + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) + return 1; + return 0; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_is16(s)) return 1; + #endif + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +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. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +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 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. +------------------------------------------------------------------------------ +*/ diff --git a/external/stb_include.h b/external/stb_include.h new file mode 100644 index 0000000..c5db201 --- /dev/null +++ b/external/stb_include.h @@ -0,0 +1,295 @@ +// stb_include.h - v0.02 - parse and process #include directives - public domain +// +// To build this, in one source file that includes this file do +// #define STB_INCLUDE_IMPLEMENTATION +// +// This program parses a string and replaces lines of the form +// #include "foo" +// with the contents of a file named "foo". It also embeds the +// appropriate #line directives. Note that all include files must +// reside in the location specified in the path passed to the API; +// it does not check multiple directories. +// +// If the string contains a line of the form +// #inject +// then it will be replaced with the contents of the string 'inject' passed to the API. +// +// Options: +// +// Define STB_INCLUDE_LINE_GLSL to get GLSL-style #line directives +// which use numbers instead of filenames. +// +// Define STB_INCLUDE_LINE_NONE to disable output of #line directives. +// +// Standard libraries: +// +// stdio.h FILE, fopen, fclose, fseek, ftell +// stdlib.h malloc, realloc, free +// string.h strcpy, strncmp, memcpy +// +// Credits: +// +// Written by Sean Barrett. +// +// Fixes: +// Michal Klos + +#ifndef STB_INCLUDE_STB_INCLUDE_H +#define STB_INCLUDE_STB_INCLUDE_H + +// Do include-processing on the string 'str'. To free the return value, pass it to free() +char *stb_include_string(char *str, char *inject, char *path_to_includes, char *filename_for_line_directive, char error[256]); + +// Concatenate the strings 'strs' and do include-processing on the result. To free the return value, pass it to free() +char *stb_include_strings(char **strs, int count, char *inject, char *path_to_includes, char *filename_for_line_directive, char error[256]); + +// Load the file 'filename' and do include-processing on the string therein. note that +// 'filename' is opened directly; 'path_to_includes' is not used. To free the return value, pass it to free() +char *stb_include_file(char *filename, char *inject, char *path_to_includes, char error[256]); + +#endif + + +#ifdef STB_INCLUDE_IMPLEMENTATION + +#include +#include +#include + +static char *stb_include_load_file(char *filename, size_t *plen) +{ + char *text; + size_t len; + FILE *f = fopen(filename, "rb"); + if (f == 0) return 0; + fseek(f, 0, SEEK_END); + len = (size_t) ftell(f); + if (plen) *plen = len; + text = (char *) malloc(len+1); + if (text == 0) return 0; + fseek(f, 0, SEEK_SET); + fread(text, 1, len, f); + fclose(f); + text[len] = 0; + return text; +} + +typedef struct +{ + int offset; + int end; + char *filename; + int next_line_after; +} include_info; + +static include_info *stb_include_append_include(include_info *array, int len, int offset, int end, char *filename, int next_line) +{ + include_info *z = (include_info *) realloc(array, sizeof(*z) * (len+1)); + z[len].offset = offset; + z[len].end = end; + z[len].filename = filename; + z[len].next_line_after = next_line; + return z; +} + +static void stb_include_free_includes(include_info *array, int len) +{ + int i; + for (i=0; i < len; ++i) + free(array[i].filename); + free(array); +} + +static int stb_include_isspace(int ch) +{ + return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'); +} + +// find location of all #include and #inject +static int stb_include_find_includes(char *text, include_info **plist) +{ + int line_count = 1; + int inc_count = 0; + char *s = text, *start; + include_info *list = NULL; + while (*s) { + // parse is always at start of line when we reach here + start = s; + while (*s == ' ' || *s == '\t') + ++s; + if (*s == '#') { + ++s; + while (*s == ' ' || *s == '\t') + ++s; + if (0==strncmp(s, "include", 7) && stb_include_isspace(s[7])) { + s += 7; + while (*s == ' ' || *s == '\t') + ++s; + if (*s == '"') { + char *t = ++s; + while (*t != '"' && *t != '\n' && *t != '\r' && *t != 0) + ++t; + if (*t == '"') { + char *filename = (char *) malloc(t-s+1); + memcpy(filename, s, t-s); + filename[t-s] = 0; + s=t; + while (*s != '\r' && *s != '\n' && *s != 0) + ++s; + // s points to the newline, so s-start is everything except the newline + list = stb_include_append_include(list, inc_count++, start-text, s-text, filename, line_count+1); + } + } + } else if (0==strncmp(s, "inject", 6) && (stb_include_isspace(s[6]) || s[6]==0)) { + while (*s != '\r' && *s != '\n' && *s != 0) + ++s; + list = stb_include_append_include(list, inc_count++, start-text, s-text, NULL, line_count+1); + } + } + while (*s != '\r' && *s != '\n' && *s != 0) + ++s; + if (*s == '\r' || *s == '\n') { + s = s + (s[0] + s[1] == '\r' + '\n' ? 2 : 1); + } + ++line_count; + } + *plist = list; + return inc_count; +} + +// avoid dependency on sprintf() +static void stb_include_itoa(char str[9], int n) +{ + int i; + for (i=0; i < 8; ++i) + str[i] = ' '; + str[i] = 0; + + for (i=1; i < 8; ++i) { + str[7-i] = '0' + (n % 10); + n /= 10; + if (n == 0) + break; + } +} + +static char *stb_include_append(char *str, size_t *curlen, char *addstr, size_t addlen) +{ + str = (char *) realloc(str, *curlen + addlen); + memcpy(str + *curlen, addstr, addlen); + *curlen += addlen; + return str; +} + +char *stb_include_string(char *str, char *inject, char *path_to_includes, char *filename, char error[256]) +{ + char temp[4096]; + include_info *inc_list; + int i, num = stb_include_find_includes(str, &inc_list); + size_t source_len = strlen(str); + char *text=0; + size_t textlen=0, last=0; + for (i=0; i < num; ++i) { + text = stb_include_append(text, &textlen, str+last, inc_list[i].offset - last); + // write out line directive for the include + #ifndef STB_INCLUDE_LINE_NONE + #ifdef STB_INCLUDE_LINE_GLSL + if (textlen != 0) // GLSL #version must appear first, so don't put a #line at the top + #endif + { + strcpy(temp, "#line "); + stb_include_itoa(temp+6, 1); + strcat(temp, " "); + #ifdef STB_INCLUDE_LINE_GLSL + stb_include_itoa(temp+15, i+1); + #else + strcat(temp, "\""); + if (inc_list[i].filename == 0) + strcmp(temp, "INJECT"); + else + strcat(temp, inc_list[i].filename); + strcat(temp, "\""); + #endif + strcat(temp, "\n"); + text = stb_include_append(text, &textlen, temp, strlen(temp)); + } + #endif + if (inc_list[i].filename == 0) { + if (inject != 0) + text = stb_include_append(text, &textlen, inject, strlen(inject)); + } else { + char *inc; + strcpy(temp, path_to_includes); + strcat(temp, "/"); + strcat(temp, inc_list[i].filename); + inc = stb_include_file(temp, inject, path_to_includes, error); + if (inc == NULL) { + stb_include_free_includes(inc_list, num); + return NULL; + } + text = stb_include_append(text, &textlen, inc, strlen(inc)); + free(inc); + } + // write out line directive + #ifndef STB_INCLUDE_LINE_NONE + strcpy(temp, "\n#line "); + stb_include_itoa(temp+6, inc_list[i].next_line_after); + strcat(temp, " "); + #ifdef STB_INCLUDE_LINE_GLSL + stb_include_itoa(temp+15, 0); + #else + strcat(temp, filename != 0 ? filename : "source-file"); + #endif + text = stb_include_append(text, &textlen, temp, strlen(temp)); + // no newlines, because we kept the #include newlines, which will get appended next + #endif + last = inc_list[i].end; + } + text = stb_include_append(text, &textlen, str+last, source_len - last + 1); // append '\0' + stb_include_free_includes(inc_list, num); + return text; +} + +char *stb_include_strings(char **strs, int count, char *inject, char *path_to_includes, char *filename, char error[256]) +{ + char *text; + char *result; + int i; + size_t length=0; + for (i=0; i < count; ++i) + length += strlen(strs[i]); + text = (char *) malloc(length+1); + length = 0; + for (i=0; i < count; ++i) { + strcpy(text + length, strs[i]); + length += strlen(strs[i]); + } + result = stb_include_string(text, inject, path_to_includes, filename, error); + free(text); + return result; +} + +char *stb_include_file(char *filename, char *inject, char *path_to_includes, char error[256]) +{ + size_t len; + char *result; + char *text = stb_include_load_file(filename, &len); + if (text == NULL) { + strcpy(error, "Error: couldn't load '"); + strcat(error, filename); + strcat(error, "'"); + return 0; + } + result = stb_include_string(text, inject, path_to_includes, filename, error); + free(text); + return result; +} + +#if 0 // @TODO, GL_ARB_shader_language_include-style system that doesn't touch filesystem +char *stb_include_preloaded(char *str, char *inject, char *includes[][2], char error[256]) +{ + +} +#endif + +#endif // STB_INCLUDE_IMPLEMENTATION diff --git a/external/stb_truetype.cpp b/external/stb_truetype.cpp new file mode 100644 index 0000000..dc22d88 --- /dev/null +++ b/external/stb_truetype.cpp @@ -0,0 +1,2 @@ +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_truetype.h" diff --git a/external/stb_truetype.h b/external/stb_truetype.h new file mode 100644 index 0000000..bbf2284 --- /dev/null +++ b/external/stb_truetype.h @@ -0,0 +1,5077 @@ +// stb_truetype.h - v1.26 - public domain +// authored from 2009-2021 by Sean Barrett / RAD Game Tools +// +// ======================================================================= +// +// NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES +// +// This library does no range checking of the offsets found in the file, +// meaning an attacker can use it to read arbitrary memory. +// +// ======================================================================= +// +// This library processes TrueType files: +// parse files +// extract glyph metrics +// extract glyph shapes +// render glyphs to one-channel bitmaps with antialiasing (box filter) +// render glyphs to one-channel SDF bitmaps (signed-distance field/function) +// +// Todo: +// non-MS cmaps +// crashproof on bad data +// hinting? (no longer patented) +// cleartype-style AA? +// optimize: use simple memory allocator for intermediates +// optimize: build edge-list directly from curves +// optimize: rasterize directly from curves? +// +// ADDITIONAL CONTRIBUTORS +// +// Mikko Mononen: compound shape support, more cmap formats +// Tor Andersson: kerning, subpixel rendering +// Dougall Johnson: OpenType / Type 2 font handling +// Daniel Ribeiro Maciel: basic GPOS-based kerning +// +// Misc other: +// Ryan Gordon +// Simon Glass +// github:IntellectualKitty +// Imanol Celaya +// Daniel Ribeiro Maciel +// +// Bug/warning reports/fixes: +// "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe +// Cass Everitt Martins Mozeiko github:aloucks +// stoiko (Haemimont Games) Cap Petschulat github:oyvindjam +// Brian Hook Omar Cornut github:vassvik +// Walter van Niftrik Ryan Griege +// David Gow Peter LaValle +// David Given Sergey Popov +// Ivan-Assen Ivanov Giumo X. Clanjor +// Anthony Pesch Higor Euripedes +// Johan Duparc Thomas Fields +// Hou Qiming Derek Vinyard +// Rob Loach Cort Stratton +// Kenney Phillis Jr. Brian Costabile +// Ken Voskuil (kaesve) +// +// VERSION HISTORY +// +// 1.26 (2021-08-28) fix broken rasterizer +// 1.25 (2021-07-11) many fixes +// 1.24 (2020-02-05) fix warning +// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) +// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined +// 1.21 (2019-02-25) fix warning +// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() +// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod +// 1.18 (2018-01-29) add missing function +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// variant PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// +// Full history can be found at the end of this file. +// +// LICENSE +// +// See end of file for license information. +// +// USAGE +// +// Include this file in whatever places need to refer to it. In ONE C/C++ +// file, write: +// #define STB_TRUETYPE_IMPLEMENTATION +// before the #include of this file. This expands out the actual +// implementation into that C/C++ file. +// +// To make the implementation private to the file that generates the implementation, +// #define STBTT_STATIC +// +// Simple 3D API (don't ship this, but it's fine for tools and quick start) +// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +// stbtt_GetBakedQuad() -- compute quad to draw for a given char +// +// Improved 3D API (more shippable): +// #include "stb_rect_pack.h" -- optional, but you really want it +// stbtt_PackBegin() +// stbtt_PackSetOversampling() -- for improved quality on small fonts +// stbtt_PackFontRanges() -- pack and renders +// stbtt_PackEnd() +// stbtt_GetPackedQuad() +// +// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +// stbtt_InitFont() +// stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections +// stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections +// +// Render a unicode codepoint to a bitmap +// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +// +// Character advance/positioning +// stbtt_GetCodepointHMetrics() +// stbtt_GetFontVMetrics() +// stbtt_GetFontVMetricsOS2() +// stbtt_GetCodepointKernAdvance() +// +// Starting with version 1.06, the rasterizer was replaced with a new, +// faster and generally-more-precise rasterizer. The new rasterizer more +// accurately measures pixel coverage for anti-aliasing, except in the case +// where multiple shapes overlap, in which case it overestimates the AA pixel +// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If +// this turns out to be a problem, you can re-enable the old rasterizer with +// #define STBTT_RASTERIZER_VERSION 1 +// which will incur about a 15% speed hit. +// +// ADDITIONAL DOCUMENTATION +// +// Immediately after this block comment are a series of sample programs. +// +// After the sample programs is the "header file" section. This section +// includes documentation for each API function. +// +// Some important concepts to understand to use this library: +// +// Codepoint +// Characters are defined by unicode codepoints, e.g. 65 is +// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is +// the hiragana for "ma". +// +// Glyph +// A visual character shape (every codepoint is rendered as +// some glyph) +// +// Glyph index +// A font-specific integer ID representing a glyph +// +// Baseline +// Glyph shapes are defined relative to a baseline, which is the +// bottom of uppercase characters. Characters extend both above +// and below the baseline. +// +// Current Point +// As you draw text to the screen, you keep track of a "current point" +// which is the origin of each character. The current point's vertical +// position is the baseline. Even "baked fonts" use this model. +// +// Vertical Font Metrics +// The vertical qualities of the font, used to vertically position +// and space the characters. See docs for stbtt_GetFontVMetrics. +// +// Font Size in Pixels or Points +// The preferred interface for specifying font sizes in stb_truetype +// is to specify how tall the font's vertical extent should be in pixels. +// If that sounds good enough, skip the next paragraph. +// +// Most font APIs instead use "points", which are a common typographic +// measurement for describing font size, defined as 72 points per inch. +// stb_truetype provides a point API for compatibility. However, true +// "per inch" conventions don't make much sense on computer displays +// since different monitors have different number of pixels per +// inch. For example, Windows traditionally uses a convention that +// there are 96 pixels per inch, thus making 'inch' measurements have +// nothing to do with inches, and thus effectively defining a point to +// be 1.333 pixels. Additionally, the TrueType font data provides +// an explicit scale factor to scale a given font's glyphs to points, +// but the author has observed that this scale factor is often wrong +// for non-commercial fonts, thus making fonts scaled in points +// according to the TrueType spec incoherently sized in practice. +// +// DETAILED USAGE: +// +// Scale: +// Select how high you want the font to be, in points or pixels. +// Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute +// a scale factor SF that will be used by all other functions. +// +// Baseline: +// You need to select a y-coordinate that is the baseline of where +// your text will appear. Call GetFontBoundingBox to get the baseline-relative +// bounding box for all characters. SF*-y0 will be the distance in pixels +// that the worst-case character could extend above the baseline, so if +// you want the top edge of characters to appear at the top of the +// screen where y=0, then you would set the baseline to SF*-y0. +// +// Current point: +// Set the current point where the first character will appear. The +// first character could extend left of the current point; this is font +// dependent. You can either choose a current point that is the leftmost +// point and hope, or add some padding, or check the bounding box or +// left-side-bearing of the first character to be displayed and set +// the current point based on that. +// +// Displaying a character: +// Compute the bounding box of the character. It will contain signed values +// relative to . I.e. if it returns x0,y0,x1,y1, +// then the character should be displayed in the rectangle from +// to = 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +// +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program (this compiles): get a single bitmap, print as ASCII art +// +#if 0 +#include +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +// +// Output: +// +// .ii. +// @@@@@@. +// V@Mio@@o +// :i. V@V +// :oM@@M +// :@@@MM@M +// @@o o@M +// :@@. M@M +// @@@o@@@@ +// :M@@V:@@. +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program: print "Hello World!" banner, with bugs +// +#if 0 +char buffer[24<<20]; +unsigned char screen[20][79]; + +int main(int arg, char **argv) +{ + stbtt_fontinfo font; + int i,j,ascent,baseline,ch=0; + float scale, xpos=2; // leave a little padding in case the character extends left + char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 15); + stbtt_GetFontVMetrics(&font, &ascent,0,0); + baseline = (int) (ascent*scale); + + while (text[ch]) { + int advance,lsb,x0,y0,x1,y1; + float x_shift = xpos - (float) floor(xpos); + stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); + stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); + stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); + // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong + // because this API is really for baking character bitmaps into textures. if you want to render + // a sequence of characters, you really need to render each bitmap to a temp buffer, then + // "alpha blend" that into the working buffer + xpos += (advance * scale); + if (text[ch+1]) + xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); + ++ch; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 78; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// INTEGRATION WITH YOUR CODEBASE +//// +//// The following sections allow you to supply alternate definitions +//// of C library functions used by stb_truetype, e.g. if you don't +//// link with the C runtime library. + +#ifdef STB_TRUETYPE_IMPLEMENTATION + // #define your own (u)stbtt_int8/16/32 before including to override this + #ifndef stbtt_uint8 + typedef unsigned char stbtt_uint8; + typedef signed char stbtt_int8; + typedef unsigned short stbtt_uint16; + typedef signed short stbtt_int16; + typedef unsigned int stbtt_uint32; + typedef signed int stbtt_int32; + #endif + + typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; + typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + + // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h + #ifndef STBTT_ifloor + #include + #define STBTT_ifloor(x) ((int) floor(x)) + #define STBTT_iceil(x) ((int) ceil(x)) + #endif + + #ifndef STBTT_sqrt + #include + #define STBTT_sqrt(x) sqrt(x) + #define STBTT_pow(x,y) pow(x,y) + #endif + + #ifndef STBTT_fmod + #include + #define STBTT_fmod(x,y) fmod(x,y) + #endif + + #ifndef STBTT_cos + #include + #define STBTT_cos(x) cos(x) + #define STBTT_acos(x) acos(x) + #endif + + #ifndef STBTT_fabs + #include + #define STBTT_fabs(x) fabs(x) + #endif + + // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h + #ifndef STBTT_malloc + #include + #define STBTT_malloc(x,u) ((void)(u),malloc(x)) + #define STBTT_free(x,u) ((void)(u),free(x)) + #endif + + #ifndef STBTT_assert + #include + #define STBTT_assert(x) assert(x) + #endif + + #ifndef STBTT_strlen + #include + #define STBTT_strlen(x) strlen(x) + #endif + + #ifndef STBTT_memcpy + #include + #define STBTT_memcpy memcpy + #define STBTT_memset memset + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef STBTT_STATIC +#define STBTT_DEF static +#else +#define STBTT_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// private structure +typedef struct +{ + unsigned char *data; + int cursor; + int size; +} stbtt__buf; + +////////////////////////////////////////////////////////////////////////////// +// +// TEXTURE BAKING API +// +// If you use this API, you only have to call two functions ever. +// + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; +} stbtt_bakedchar; + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata); // you allocate this, it's num_chars long +// if return is positive, the first unused row of the bitmap +// if return is negative, returns the negative of the number of characters that fit +// if return is 0, no characters fit and no rows were used +// This uses a very crappy packing. + +typedef struct +{ + float x0,y0,s0,t0; // top-left + float x1,y1,s1,t1; // bottom-right +} stbtt_aligned_quad; + +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier +// Call GetBakedQuad with char_index = 'character - first_char', and it +// creates the quad you need to draw and advances the current position. +// +// The coordinate system used assumes y increases downwards. +// +// Characters will extend both above and below the current position; +// see discussion of "BASELINE" above. +// +// It's inefficient; you might want to c&p it and optimize it. + +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap); +// Query the font vertical metrics without having to create a font first. + + +////////////////////////////////////////////////////////////////////////////// +// +// NEW TEXTURE BAKING API +// +// This provides options for packing multiple fonts into one atlas, not +// perfectly but better than nothing. + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; + float xoff2,yoff2; +} stbtt_packedchar; + +typedef struct stbtt_pack_context stbtt_pack_context; +typedef struct stbtt_fontinfo stbtt_fontinfo; +#ifndef STB_RECT_PACK_VERSION +typedef struct stbrp_rect stbrp_rect; +#endif + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); +// Initializes a packing context stored in the passed-in stbtt_pack_context. +// Future calls using this context will pack characters into the bitmap passed +// in here: a 1-channel bitmap that is width * height. stride_in_bytes is +// the distance from one row to the next (or 0 to mean they are packed tightly +// together). "padding" is the amount of padding to leave between each +// character (normally you want '1' for bitmaps you'll use as textures with +// bilinear filtering). +// +// Returns 0 on failure, 1 on success. + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); +// Cleans up the packing context and frees all memory. + +#define STBTT_POINT_SIZE(x) (-(x)) + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); +// Creates character bitmaps from the font_index'th font found in fontdata (use +// font_index=0 if you don't know what that is). It creates num_chars_in_range +// bitmaps for characters with unicode values starting at first_unicode_char_in_range +// and increasing. Data for how to render them is stored in chardata_for_range; +// pass these to stbtt_GetPackedQuad to get back renderable quads. +// +// font_size is the full height of the character from ascender to descender, +// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed +// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() +// and pass that result as 'font_size': +// ..., 20 , ... // font max minus min y is 20 pixels tall +// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall + +typedef struct +{ + float font_size; + int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint + int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints + int num_chars; + stbtt_packedchar *chardata_for_range; // output + unsigned char h_oversample, v_oversample; // don't set these, they're used internally +} stbtt_pack_range; + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); +// Creates character bitmaps from multiple ranges of characters stored in +// ranges. This will usually create a better-packed bitmap than multiple +// calls to stbtt_PackFontRange. Note that you can call this multiple +// times within a single PackBegin/PackEnd. + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); +// Oversampling a font increases the quality by allowing higher-quality subpixel +// positioning, and is especially valuable at smaller text sizes. +// +// This function sets the amount of oversampling for all following calls to +// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given +// pack context. The default (no oversampling) is achieved by h_oversample=1 +// and v_oversample=1. The total number of pixels required is +// h_oversample*v_oversample larger than the default; for example, 2x2 +// oversampling requires 4x the storage of 1x1. For best results, render +// oversampled textures with bilinear filtering. Look at the readme in +// stb/tests/oversample for information about oversampled fonts +// +// To use with PackFontRangesGather etc., you must set it before calls +// call to PackFontRangesGatherRects. + +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip); +// If skip != 0, this tells stb_truetype to skip any codepoints for which +// there is no corresponding glyph. If skip=0, which is the default, then +// codepoints without a glyph recived the font's "missing character" glyph, +// typically an empty box by convention. + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int align_to_integer); + +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +// Calling these functions in sequence is roughly equivalent to calling +// stbtt_PackFontRanges(). If you more control over the packing of multiple +// fonts, or if you want to pack custom data into a font texture, take a look +// at the source to of stbtt_PackFontRanges() and create a custom version +// using these functions, e.g. call GatherRects multiple times, +// building up a single array of rects, then call PackRects once, +// then call RenderIntoRects repeatedly. This may result in a +// better packing than calling PackFontRanges multiple times +// (or it may not). + +// this is an opaque structure that you shouldn't mess with which holds +// all the context needed from PackBegin to PackEnd. +struct stbtt_pack_context { + void *user_allocator_context; + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + int skip_missing; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// FONT LOADING +// +// + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data); +// This function will determine the number of fonts in a font file. TrueType +// collection (.ttc) files may contain multiple fonts, while TrueType font +// (.ttf) files only contain one font. The number of fonts can be used for +// indexing with the previous function where the index is between zero and one +// less than the total fonts. If an error occurs, -1 is returned. + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +// Each .ttf/.ttc file may have more than one font. Each font has a sequential +// index number starting from 0. Call this function to get the font offset for +// a given index; it returns -1 if the index is out of range. A regular .ttf +// file will only define one font and it always be at offset 0, so it will +// return '0' for index 0, and -1 for all other indices. + +// The following structure is defined publicly so you can declare one on +// the stack or as a global or etc, but you should treat it as opaque. +struct stbtt_fontinfo +{ + void * userdata; + unsigned char * data; // pointer to .ttf file + int fontstart; // offset of start of font + + int numGlyphs; // number of glyphs, needed for range checking + + int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf + int index_map; // a cmap mapping for our chosen character encoding + int indexToLocFormat; // format needed to map from glyph index to glyph + + stbtt__buf cff; // cff font data + stbtt__buf charstrings; // the charstring index + stbtt__buf gsubrs; // global charstring subroutines index + stbtt__buf subrs; // private charstring subroutines index + stbtt__buf fontdicts; // array of font dicts + stbtt__buf fdselect; // map from glyph to fontdict +}; + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +// Given an offset into the file that defines a font, this function builds +// the necessary cached info for the rest of the system. You must allocate +// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't +// need to do anything special to free it, because the contents are pure +// value data with no additional data structures. Returns 0 on failure. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER TO GLYPH-INDEX CONVERSIOn + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +// If you're going to perform multiple operations on the same character +// and you want a speed-up, call this function with the character you're +// going to process, then use glyph-based functions instead of the +// codepoint-based functions. +// Returns 0 if the character codepoint is not defined in the font. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER PROPERTIES +// + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose "height" is 'pixels' tall. +// Height is measured as the distance from the highest ascender to the lowest +// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics +// and computing: +// scale = pixels / (ascent - descent) +// so if you prefer to measure height by the ascent only, use a similar calculation. + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose EM size is mapped to +// 'pixels' tall. This is probably what traditional APIs compute, but +// I'm not positive. + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +// ascent is the coordinate above the baseline the font extends; descent +// is the coordinate below the baseline the font extends (i.e. it is typically negative) +// lineGap is the spacing between one row's descent and the next row's ascent... +// so you should advance the vertical position by "*ascent - *descent + *lineGap" +// these are expressed in unscaled coordinates, so you must multiply by +// the scale factor for a given size + +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap); +// analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2 +// table (specific to MS/Windows TTF files). +// +// Returns 1 on success (table present), 0 on failure. + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); +// the bounding box around all possible characters + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +// leftSideBearing is the offset from the current horizontal position to the left edge of the character +// advanceWidth is the offset from the current horizontal position to the next horizontal position +// these are expressed in unscaled coordinates + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +// an additional amount to add to the 'advance' value between ch1 and ch2 + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +// Gets the bounding box of the visible part of the glyph, in unscaled coordinates + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +// as above, but takes one or more glyph indices for greater efficiency + +typedef struct stbtt_kerningentry +{ + int glyph1; // use stbtt_FindGlyphIndex + int glyph2; + int advance; +} stbtt_kerningentry; + +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info); +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length); +// Retrieves a complete list of all of the kerning pairs provided by the font +// stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write. +// The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1) + +////////////////////////////////////////////////////////////////////////////// +// +// GLYPH SHAPES (you probably don't need these, but they have to go before +// the bitmaps for C declaration-order reasons) +// + +#ifndef STBTT_vmove // you can predefine these to use different values (but why?) + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve, + STBTT_vcubic + }; +#endif + +#ifndef stbtt_vertex // you can predefine this to use different values + // (we share this with other code at RAD) + #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file + typedef struct + { + stbtt_vertex_type x,y,cx,cy,cx1,cy1; + unsigned char type,padding; + } stbtt_vertex; +#endif + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); +// returns non-zero if nothing is drawn for this glyph + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +// returns # of vertices and fills *vertices with the pointer to them +// these are expressed in "unscaled" coordinates +// +// The shape is a series of contours. Each one starts with +// a STBTT_moveto, then consists of a series of mixed +// STBTT_lineto and STBTT_curveto segments. A lineto +// draws a line from previous endpoint to its x,y; a curveto +// draws a quadratic bezier from previous endpoint to +// its x,y, using cx,cy as the bezier control point. + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +// frees the data allocated above + +STBTT_DEF unsigned char *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl); +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); +// fills svg with the character's SVG data. +// returns data size or 0 if SVG not found. + +////////////////////////////////////////////////////////////////////////////// +// +// BITMAP RENDERING +// + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +// frees the bitmap allocated below + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// allocates a large-enough single-channel 8bpp bitmap and renders the +// specified character/glyph at the specified scale into it, with +// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). +// *width & *height are filled out with the width & height of the bitmap, +// which is stored left-to-right, top-to-bottom. +// +// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap +// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap +// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the +// width and height and positioning info for it first. + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); +// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint); +// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering +// is performed (see stbtt_PackSetOversampling) + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +// get the bbox of the bitmap centered around the glyph origin; so the +// bitmap width is ix1-ix0, height is iy1-iy0, and location to place +// the bitmap top left is (leftSideBearing*scale,iy0). +// (Note that the bitmap uses y-increases-down, but the shape uses +// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); +// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel +// shift for the character + +// the following functions are equivalent to the above functions, but operate +// on glyph indices instead of Unicode codepoints (for efficiency) +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph); +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); + + +// @TODO: don't expose this structure +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} stbtt__bitmap; + +// rasterize a shape with quadratic beziers into a bitmap +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into + float flatness_in_pixels, // allowable error of curve in pixels + stbtt_vertex *vertices, // array of vertices defining shape + int num_verts, // number of vertices in above array + float scale_x, float scale_y, // scale applied to input vertices + float shift_x, float shift_y, // translation applied to input vertices + int x_off, int y_off, // another translation applied to input + int invert, // if non-zero, vertically flip shape + void *userdata); // context for to STBTT_MALLOC + +////////////////////////////////////////////////////////////////////////////// +// +// Signed Distance Function (or Field) rendering + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata); +// frees the SDF bitmap allocated below + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +// These functions compute a discretized SDF field for a single character, suitable for storing +// in a single-channel texture, sampling with bilinear filtering, and testing against +// larger than some threshold to produce scalable fonts. +// info -- the font +// scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap +// glyph/codepoint -- the character to generate the SDF for +// padding -- extra "pixels" around the character which are filled with the distance to the character (not 0), +// which allows effects like bit outlines +// onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character) +// pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale) +// if positive, > onedge_value is inside; if negative, < onedge_value is inside +// width,height -- output height & width of the SDF bitmap (including padding) +// xoff,yoff -- output origin of the character +// return value -- a 2D array of bytes 0..255, width*height in size +// +// pixel_dist_scale & onedge_value are a scale & bias that allows you to make +// optimal use of the limited 0..255 for your application, trading off precision +// and special effects. SDF values outside the range 0..255 are clamped to 0..255. +// +// Example: +// scale = stbtt_ScaleForPixelHeight(22) +// padding = 5 +// onedge_value = 180 +// pixel_dist_scale = 180/5.0 = 36.0 +// +// This will create an SDF bitmap in which the character is about 22 pixels +// high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled +// shape, sample the SDF at each pixel and fill the pixel if the SDF value +// is greater than or equal to 180/255. (You'll actually want to antialias, +// which is beyond the scope of this example.) Additionally, you can compute +// offset outlines (e.g. to stroke the character border inside & outside, +// or only outside). For example, to fill outside the character up to 3 SDF +// pixels, you would compare against (180-36.0*3)/255 = 72/255. The above +// choice of variables maps a range from 5 pixels outside the shape to +// 2 pixels inside the shape to 0..255; this is intended primarily for apply +// outside effects only (the interior range is needed to allow proper +// antialiasing of the font at *smaller* sizes) +// +// The function computes the SDF analytically at each SDF pixel, not by e.g. +// building a higher-res bitmap and approximating it. In theory the quality +// should be as high as possible for an SDF of this size & representation, but +// unclear if this is true in practice (perhaps building a higher-res bitmap +// and computing from that can allow drop-out prevention). +// +// The algorithm has not been optimized at all, so expect it to be slow +// if computing lots of characters or very large sizes. + + + +////////////////////////////////////////////////////////////////////////////// +// +// Finding the right font... +// +// You should really just solve this offline, keep your own tables +// of what font is what, and don't try to get it out of the .ttf file. +// That's because getting it out of the .ttf file is really hard, because +// the names in the file can appear in many possible encodings, in many +// possible languages, and e.g. if you need a case-insensitive comparison, +// the details of that depend on the encoding & language in a complex way +// (actually underspecified in truetype, but also gigantic). +// +// But you can use the provided functions in two possible ways: +// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on +// unicode-encoded names to try to find the font you want; +// you can run this before calling stbtt_InitFont() +// +// stbtt_GetFontNameString() lets you get any of the various strings +// from the file yourself and do your own comparisons on them. +// You have to have called stbtt_InitFont() first. + + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +// returns the offset (not index) of the font that matches, or -1 if none +// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". +// if you use any other flag, use a font name like "Arial"; this checks +// the 'macStyle' header field; i don't know if fonts set this consistently +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +// returns 1/0 whether the first string interpreted as utf8 is identical to +// the second string interpreted as big-endian utf16... useful for strings from next func + +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +// returns the string (which may be big-endian double byte, e.g. for unicode) +// and puts the length in bytes in *length. +// +// some of the values for the IDs are below; for more see the truetype spec: +// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html +// http://www.microsoft.com/typography/otspec/name.htm + +enum { // platformID + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_UNICODE + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 +}; + +enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... + // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D +}; + +enum { // languageID for STBTT_PLATFORM_ID_MAC + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 +}; + +#ifdef __cplusplus +} +#endif + +#endif // __STB_INCLUDE_STB_TRUETYPE_H__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// IMPLEMENTATION +//// +//// + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +#ifndef STBTT_MAX_OVERSAMPLE +#define STBTT_MAX_OVERSAMPLE 8 +#endif + +#if STBTT_MAX_OVERSAMPLE > 255 +#error "STBTT_MAX_OVERSAMPLE cannot be > 255" +#endif + +typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; + +#ifndef STBTT_RASTERIZER_VERSION +#define STBTT_RASTERIZER_VERSION 2 +#endif + +#ifdef _MSC_VER +#define STBTT__NOTUSED(v) (void)(v) +#else +#define STBTT__NOTUSED(v) (void)sizeof(v) +#endif + +////////////////////////////////////////////////////////////////////////// +// +// stbtt__buf helpers to parse data from file +// + +static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor++]; +} + +static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor]; +} + +static void stbtt__buf_seek(stbtt__buf *b, int o) +{ + STBTT_assert(!(o > b->size || o < 0)); + b->cursor = (o > b->size || o < 0) ? b->size : o; +} + +static void stbtt__buf_skip(stbtt__buf *b, int o) +{ + stbtt__buf_seek(b, b->cursor + o); +} + +static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n) +{ + stbtt_uint32 v = 0; + int i; + STBTT_assert(n >= 1 && n <= 4); + for (i = 0; i < n; i++) + v = (v << 8) | stbtt__buf_get8(b); + return v; +} + +static stbtt__buf stbtt__new_buf(const void *p, size_t size) +{ + stbtt__buf r; + STBTT_assert(size < 0x40000000); + r.data = (stbtt_uint8*) p; + r.size = (int) size; + r.cursor = 0; + return r; +} + +#define stbtt__buf_get16(b) stbtt__buf_get((b), 2) +#define stbtt__buf_get32(b) stbtt__buf_get((b), 4) + +static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s) +{ + stbtt__buf r = stbtt__new_buf(NULL, 0); + if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r; + r.data = b->data + o; + r.size = s; + return r; +} + +static stbtt__buf stbtt__cff_get_index(stbtt__buf *b) +{ + int count, start, offsize; + start = b->cursor; + count = stbtt__buf_get16(b); + if (count) { + offsize = stbtt__buf_get8(b); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(b, offsize * count); + stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1); + } + return stbtt__buf_range(b, start, b->cursor - start); +} + +static stbtt_uint32 stbtt__cff_int(stbtt__buf *b) +{ + int b0 = stbtt__buf_get8(b); + if (b0 >= 32 && b0 <= 246) return b0 - 139; + else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108; + else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108; + else if (b0 == 28) return stbtt__buf_get16(b); + else if (b0 == 29) return stbtt__buf_get32(b); + STBTT_assert(0); + return 0; +} + +static void stbtt__cff_skip_operand(stbtt__buf *b) { + int v, b0 = stbtt__buf_peek8(b); + STBTT_assert(b0 >= 28); + if (b0 == 30) { + stbtt__buf_skip(b, 1); + while (b->cursor < b->size) { + v = stbtt__buf_get8(b); + if ((v & 0xF) == 0xF || (v >> 4) == 0xF) + break; + } + } else { + stbtt__cff_int(b); + } +} + +static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key) +{ + stbtt__buf_seek(b, 0); + while (b->cursor < b->size) { + int start = b->cursor, end, op; + while (stbtt__buf_peek8(b) >= 28) + stbtt__cff_skip_operand(b); + end = b->cursor; + op = stbtt__buf_get8(b); + if (op == 12) op = stbtt__buf_get8(b) | 0x100; + if (op == key) return stbtt__buf_range(b, start, end-start); + } + return stbtt__buf_range(b, 0, 0); +} + +static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out) +{ + int i; + stbtt__buf operands = stbtt__dict_get(b, key); + for (i = 0; i < outcount && operands.cursor < operands.size; i++) + out[i] = stbtt__cff_int(&operands); +} + +static int stbtt__cff_index_count(stbtt__buf *b) +{ + stbtt__buf_seek(b, 0); + return stbtt__buf_get16(b); +} + +static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) +{ + int count, offsize, start, end; + stbtt__buf_seek(&b, 0); + count = stbtt__buf_get16(&b); + offsize = stbtt__buf_get8(&b); + STBTT_assert(i >= 0 && i < count); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(&b, i*offsize); + start = stbtt__buf_get(&b, offsize); + end = stbtt__buf_get(&b, offsize); + return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start); +} + +////////////////////////////////////////////////////////////////////////// +// +// accessors to parse data from file +// + +// on platforms that don't allow misaligned reads, if we want to allow +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } +static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(stbtt_uint8 *font) +{ + // check the version number + if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 + if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! + if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF + if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + if (stbtt_tag(font, "true")) return 1; // Apple specification for TrueType fonts + return 0; +} + +// @OPTIMIZE: binary search +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index) +{ + // if it's just a font, there's only one valid index + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*4); + } + } + return -1; +} + +static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection) +{ + // if it's just a font, there's only one valid font + if (stbtt__isfont(font_collection)) + return 1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + return ttLONG(font_collection+8); + } + } + return 0; +} + +static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) +{ + stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 }; + stbtt__buf pdict; + stbtt__dict_get_ints(&fontdict, 18, 2, private_loc); + if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0); + pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]); + stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); + if (!subrsoff) return stbtt__new_buf(NULL, 0); + stbtt__buf_seek(&cff, private_loc[1]+subrsoff); + return stbtt__cff_get_index(&cff); +} + +// since most people won't use this, find this table the first time it's needed +static int stbtt__get_svg(stbtt_fontinfo *info) +{ + stbtt_uint32 t; + if (info->svg < 0) { + t = stbtt__find_table(info->data, info->fontstart, "SVG "); + if (t) { + stbtt_uint32 offset = ttULONG(info->data + t + 2); + info->svg = t + offset; + } else { + info->svg = 0; + } + } + return info->svg; +} + +static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) +{ + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + info->cff = stbtt__new_buf(NULL, 0); + + cmap = stbtt__find_table(data, fontstart, "cmap"); // required + info->loca = stbtt__find_table(data, fontstart, "loca"); // required + info->head = stbtt__find_table(data, fontstart, "head"); // required + info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required + info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required + info->kern = stbtt__find_table(data, fontstart, "kern"); // not required + info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required + + if (!cmap || !info->head || !info->hhea || !info->hmtx) + return 0; + if (info->glyf) { + // required for truetype + if (!info->loca) return 0; + } else { + // initialization for CFF / Type2 fonts (OTF) + stbtt__buf b, topdict, topdictidx; + stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0; + stbtt_uint32 cff; + + cff = stbtt__find_table(data, fontstart, "CFF "); + if (!cff) return 0; + + info->fontdicts = stbtt__new_buf(NULL, 0); + info->fdselect = stbtt__new_buf(NULL, 0); + + // @TODO this should use size from table (not 512MB) + info->cff = stbtt__new_buf(data+cff, 512*1024*1024); + b = info->cff; + + // read the header + stbtt__buf_skip(&b, 2); + stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize + + // @TODO the name INDEX could list multiple fonts, + // but we just use the first one. + stbtt__cff_get_index(&b); // name INDEX + topdictidx = stbtt__cff_get_index(&b); + topdict = stbtt__cff_index_get(topdictidx, 0); + stbtt__cff_get_index(&b); // string INDEX + info->gsubrs = stbtt__cff_get_index(&b); + + stbtt__dict_get_ints(&topdict, 17, 1, &charstrings); + stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); + stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); + stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); + info->subrs = stbtt__get_subrs(b, topdict); + + // we only support Type 2 charstrings + if (cstype != 2) return 0; + if (charstrings == 0) return 0; + + if (fdarrayoff) { + // looks like a CID font + if (!fdselectoff) return 0; + stbtt__buf_seek(&b, fdarrayoff); + info->fontdicts = stbtt__cff_get_index(&b); + info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff); + } + + stbtt__buf_seek(&b, charstrings); + info->charstrings = stbtt__cff_get_index(&b); + } + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + info->svg = -1; + + // find a cmap encoding table we understand *now* to avoid searching + // later. (todo: could make this installable) + // the same regardless of glyph. + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + // find an encoding we understand: + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + // MS/Unicode + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + case STBTT_PLATFORM_ID_UNICODE: + // Mac/iOS has these + // all the encodingIDs are unicode, so we don't bother to check it + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { // apple byte encoding + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean + return 0; + } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + + // do a binary search of the segments + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + // they lie from endCount .. endCount + segCount + // but searchRange is the nearest power of two, so... + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + // now decrement to bias correctly to find smallest + search -= 2; + while (entrySelector) { + stbtt_uint16 end; + searchRange >>= 1; + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + { + stbtt_uint16 offset, start, last; + stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); + + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + last = ttUSHORT(data + endCount + 2*item); + if (unicode_codepoint < start || unicode_codepoint > last) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } + } else if (format == 12 || format == 13) { + stbtt_uint32 ngroups = ttULONG(data+index_map+12); + stbtt_int32 low,high; + low = 0; high = (stbtt_int32)ngroups; + // Binary search the right group. + while (low < high) { + stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return start_glyph + unicode_codepoint-start_char; + else // format == 13 + return start_glyph; + } + } + return 0; // not found + } + // @TODO + STBTT_assert(0); + return 0; +} + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) +{ + v->type = type; + v->x = (stbtt_int16) x; + v->y = (stbtt_int16) y; + v->cx = (stbtt_int16) cx; + v->cy = (stbtt_int16) cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + STBTT_assert(!info->cff.size); + + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range + if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; // if length is 0, return -1 +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); + +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + if (info->cff.size) { + stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); + } else { + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + } + return 1; +} + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt_int16 numberOfContours; + int g; + if (info->cff.size) + return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0; + g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 1; + numberOfContours = ttSHORT(info->data + g); + return numberOfContours == 0; +} + +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) +{ + if (start_off) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + return num_vertices; +} + +static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + 2*numberOfContours; // a loose bound on how many vertices we might need + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + // in first pass, we load uninterpreted data into the allocated array + // above, shifted to the end of the array so we won't overwrite it when + // we create our final data starting from the front + + off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated + + // first load flags + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + // now load x coordinates + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; // ??? + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (stbtt_int16) x; + } + + // now load y coordinates + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; // ??? + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (stbtt_int16) y; + } + + // now convert them to our format + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + // now start the new one + start_off = !(flags & 1); + if (start_off) { + // if we start off with an off-curve point, then when we need to find a point on the curve + // where we can start, and we need to save some state for when we wraparound. + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + // next point is also a curve point, so interpolate an on-point curve + sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; + sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; + } else { + // otherwise just use the next point as our start point + sx = (stbtt_int32) vertices[off+i+1].x; + sy = (stbtt_int32) vertices[off+i+1].y; + ++i; // we're using point i+1 as the starting point, so skip it + } + } else { + sx = x; + sy = y; + } + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) { // if it's a curve + if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours < 0) { + // Compound shapes. + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { // XY values + if (flags & 1) { // shorts + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + // @TODO handle matching point + STBTT_assert(0); + } + if (flags & (1<<3)) { // WE_HAVE_A_SCALE + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + // Find transformation scales. + m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + // Get indexed glyph. + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + // Transform vertices. + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + // Append vertices. + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0 && vertices) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + // More components ? + more = flags & (1<<5); + } + } else { + // numberOfCounters == 0, do nothing + } + + *pvertices = vertices; + return num_vertices; +} + +typedef struct +{ + int bounds; + int started; + float first_x, first_y; + float x, y; + stbtt_int32 min_x, max_x, min_y, max_y; + + stbtt_vertex *pvertices; + int num_vertices; +} stbtt__csctx; + +#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0} + +static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y) +{ + if (x > c->max_x || !c->started) c->max_x = x; + if (y > c->max_y || !c->started) c->max_y = y; + if (x < c->min_x || !c->started) c->min_x = x; + if (y < c->min_y || !c->started) c->min_y = y; + c->started = 1; +} + +static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1) +{ + if (c->bounds) { + stbtt__track_vertex(c, x, y); + if (type == STBTT_vcubic) { + stbtt__track_vertex(c, cx, cy); + stbtt__track_vertex(c, cx1, cy1); + } + } else { + stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); + c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1; + c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1; + } + c->num_vertices++; +} + +static void stbtt__csctx_close_shape(stbtt__csctx *ctx) +{ + if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy) +{ + stbtt__csctx_close_shape(ctx); + ctx->first_x = ctx->x = ctx->x + dx; + ctx->first_y = ctx->y = ctx->y + dy; + stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy) +{ + ctx->x += dx; + ctx->y += dy; + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) +{ + float cx1 = ctx->x + dx1; + float cy1 = ctx->y + dy1; + float cx2 = cx1 + dx2; + float cy2 = cy1 + dy2; + ctx->x = cx2 + dx3; + ctx->y = cy2 + dy3; + stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); +} + +static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) +{ + int count = stbtt__cff_index_count(&idx); + int bias = 107; + if (count >= 33900) + bias = 32768; + else if (count >= 1240) + bias = 1131; + n += bias; + if (n < 0 || n >= count) + return stbtt__new_buf(NULL, 0); + return stbtt__cff_index_get(idx, n); +} + +static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt__buf fdselect = info->fdselect; + int nranges, start, end, v, fmt, fdselector = -1, i; + + stbtt__buf_seek(&fdselect, 0); + fmt = stbtt__buf_get8(&fdselect); + if (fmt == 0) { + // untested + stbtt__buf_skip(&fdselect, glyph_index); + fdselector = stbtt__buf_get8(&fdselect); + } else if (fmt == 3) { + nranges = stbtt__buf_get16(&fdselect); + start = stbtt__buf_get16(&fdselect); + for (i = 0; i < nranges; i++) { + v = stbtt__buf_get8(&fdselect); + end = stbtt__buf_get16(&fdselect); + if (glyph_index >= start && glyph_index < end) { + fdselector = v; + break; + } + start = end; + } + } + if (fdselector == -1) stbtt__new_buf(NULL, 0); + return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector)); +} + +static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c) +{ + int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0; + int has_subrs = 0, clear_stack; + float s[48]; + stbtt__buf subr_stack[10], subrs = info->subrs, b; + float f; + +#define STBTT__CSERR(s) (0) + + // this currently ignores the initial width value, which isn't needed if we have hmtx + b = stbtt__cff_index_get(info->charstrings, glyph_index); + while (b.cursor < b.size) { + i = 0; + clear_stack = 1; + b0 = stbtt__buf_get8(&b); + switch (b0) { + // @TODO implement hinting + case 0x13: // hintmask + case 0x14: // cntrmask + if (in_header) + maskbits += (sp / 2); // implicit "vstem" + in_header = 0; + stbtt__buf_skip(&b, (maskbits + 7) / 8); + break; + + case 0x01: // hstem + case 0x03: // vstem + case 0x12: // hstemhm + case 0x17: // vstemhm + maskbits += (sp / 2); + break; + + case 0x15: // rmoveto + in_header = 0; + if (sp < 2) return STBTT__CSERR("rmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]); + break; + case 0x04: // vmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("vmoveto stack"); + stbtt__csctx_rmove_to(c, 0, s[sp-1]); + break; + case 0x16: // hmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("hmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-1], 0); + break; + + case 0x05: // rlineto + if (sp < 2) return STBTT__CSERR("rlineto stack"); + for (; i + 1 < sp; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical + // starting from a different place. + + case 0x07: // vlineto + if (sp < 1) return STBTT__CSERR("vlineto stack"); + goto vlineto; + case 0x06: // hlineto + if (sp < 1) return STBTT__CSERR("hlineto stack"); + for (;;) { + if (i >= sp) break; + stbtt__csctx_rline_to(c, s[i], 0); + i++; + vlineto: + if (i >= sp) break; + stbtt__csctx_rline_to(c, 0, s[i]); + i++; + } + break; + + case 0x1F: // hvcurveto + if (sp < 4) return STBTT__CSERR("hvcurveto stack"); + goto hvcurveto; + case 0x1E: // vhcurveto + if (sp < 4) return STBTT__CSERR("vhcurveto stack"); + for (;;) { + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f); + i += 4; + hvcurveto: + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]); + i += 4; + } + break; + + case 0x08: // rrcurveto + if (sp < 6) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x18: // rcurveline + if (sp < 8) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp - 2; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack"); + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + case 0x19: // rlinecurve + if (sp < 8) return STBTT__CSERR("rlinecurve stack"); + for (; i + 1 < sp - 6; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack"); + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x1A: // vvcurveto + case 0x1B: // hhcurveto + if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack"); + f = 0.0; + if (sp & 1) { f = s[i]; i++; } + for (; i + 3 < sp; i += 4) { + if (b0 == 0x1B) + stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0); + else + stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]); + f = 0.0; + } + break; + + case 0x0A: // callsubr + if (!has_subrs) { + if (info->fdselect.size) + subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); + has_subrs = 1; + } + // FALLTHROUGH + case 0x1D: // callgsubr + if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); + v = (int) s[--sp]; + if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit"); + subr_stack[subr_stack_height++] = b; + b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v); + if (b.size == 0) return STBTT__CSERR("subr not found"); + b.cursor = 0; + clear_stack = 0; + break; + + case 0x0B: // return + if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr"); + b = subr_stack[--subr_stack_height]; + clear_stack = 0; + break; + + case 0x0E: // endchar + stbtt__csctx_close_shape(c); + return 1; + + case 0x0C: { // two-byte escape + float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6; + float dx, dy; + int b1 = stbtt__buf_get8(&b); + switch (b1) { + // @TODO These "flex" implementations ignore the flex-depth and resolution, + // and always draw beziers. + case 0x22: // hflex + if (sp < 7) return STBTT__CSERR("hflex stack"); + dx1 = s[0]; + dx2 = s[1]; + dy2 = s[2]; + dx3 = s[3]; + dx4 = s[4]; + dx5 = s[5]; + dx6 = s[6]; + stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); + break; + + case 0x23: // flex + if (sp < 13) return STBTT__CSERR("flex stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = s[10]; + dy6 = s[11]; + //fd is s[12] + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + case 0x24: // hflex1 + if (sp < 9) return STBTT__CSERR("hflex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dx4 = s[5]; + dx5 = s[6]; + dy5 = s[7]; + dx6 = s[8]; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5)); + break; + + case 0x25: // flex1 + if (sp < 11) return STBTT__CSERR("flex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = dy6 = s[10]; + dx = dx1+dx2+dx3+dx4+dx5; + dy = dy1+dy2+dy3+dy4+dy5; + if (STBTT_fabs(dx) > STBTT_fabs(dy)) + dy6 = -dy; + else + dx6 = -dx; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + default: + return STBTT__CSERR("unimplemented"); + } + } break; + + default: + if (b0 != 255 && b0 != 28 && b0 < 32) + return STBTT__CSERR("reserved operator"); + + // push immediate + if (b0 == 255) { + f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000; + } else { + stbtt__buf_skip(&b, -1); + f = (float)(stbtt_int16)stbtt__cff_int(&b); + } + if (sp >= 48) return STBTT__CSERR("push stack overflow"); + s[sp++] = f; + clear_stack = 0; + break; + } + if (clear_stack) sp = 0; + } + return STBTT__CSERR("no endchar"); + +#undef STBTT__CSERR +} + +static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + // runs the charstring twice, once to count and once to output (to avoid realloc) + stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1); + stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0); + if (stbtt__run_charstring(info, glyph_index, &count_ctx)) { + *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata); + output_ctx.pvertices = *pvertices; + if (stbtt__run_charstring(info, glyph_index, &output_ctx)) { + STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices); + return output_ctx.num_vertices; + } + } + *pvertices = NULL; + return 0; +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + stbtt__csctx c = STBTT__CSCTX_INIT(1); + int r = stbtt__run_charstring(info, glyph_index, &c); + if (x0) *x0 = r ? c.min_x : 0; + if (y0) *y0 = r ? c.min_y : 0; + if (x1) *x1 = r ? c.max_x : 0; + if (y1) *y1 = r ? c.max_y : 0; + return r ? c.num_vertices : 0; +} + +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + if (!info->cff.size) + return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices); + else + return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices); +} + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info) +{ + stbtt_uint8 *data = info->data + info->kern; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + return ttUSHORT(data+10); +} + +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length) +{ + stbtt_uint8 *data = info->data + info->kern; + int k, length; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + length = ttUSHORT(data+10); + if (table_length < length) + length = table_length; + + for (k = 0; k < length; k++) + { + table[k].glyph1 = ttUSHORT(data+18+(k*6)); + table[k].glyph2 = ttUSHORT(data+20+(k*6)); + table[k].advance = ttSHORT(data+22+(k*6)); + } + + return length; +} + +static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint8 *data = info->data + info->kern; + stbtt_uint32 needle, straw; + int l, r, m; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + l = 0; + r = ttUSHORT(data+10) - 1; + needle = glyph1 << 16 | glyph2; + while (l <= r) { + m = (l + r) >> 1; + straw = ttULONG(data+18+(m*6)); // note: unaligned read + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return ttSHORT(data+22+(m*6)); + } + return 0; +} + +static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) +{ + stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); + switch (coverageFormat) { + case 1: { + stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); + + // Binary search. + stbtt_int32 l=0, r=glyphCount-1, m; + int straw, needle=glyph; + while (l <= r) { + stbtt_uint8 *glyphArray = coverageTable + 4; + stbtt_uint16 glyphID; + m = (l + r) >> 1; + glyphID = ttUSHORT(glyphArray + 2 * m); + straw = glyphID; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + return m; + } + } + break; + } + + case 2: { + stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); + stbtt_uint8 *rangeArray = coverageTable + 4; + + // Binary search. + stbtt_int32 l=0, r=rangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *rangeRecord; + m = (l + r) >> 1; + rangeRecord = rangeArray + 6 * m; + strawStart = ttUSHORT(rangeRecord); + strawEnd = ttUSHORT(rangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else { + stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); + return startCoverageIndex + glyph - strawStart; + } + } + break; + } + + default: return -1; // unsupported + } + + return -1; +} + +static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) +{ + stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); + switch (classDefFormat) + { + case 1: { + stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); + stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); + stbtt_uint8 *classDef1ValueArray = classDefTable + 6; + + if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) + return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); + break; + } + + case 2: { + stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); + stbtt_uint8 *classRangeRecords = classDefTable + 4; + + // Binary search. + stbtt_int32 l=0, r=classRangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *classRangeRecord; + m = (l + r) >> 1; + classRangeRecord = classRangeRecords + 6 * m; + strawStart = ttUSHORT(classRangeRecord); + strawEnd = ttUSHORT(classRangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else + return (stbtt_int32)ttUSHORT(classRangeRecord + 4); + } + break; + } + + default: + return -1; // Unsupported definition type, return an error. + } + + // "All glyphs not assigned to a class fall into class 0". (OpenType spec) + return 0; +} + +// Define to STBTT_assert(x) if you want to break on unimplemented formats. +#define STBTT_GPOS_TODO_assert(x) + +static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint16 lookupListOffset; + stbtt_uint8 *lookupList; + stbtt_uint16 lookupCount; + stbtt_uint8 *data; + stbtt_int32 i, sti; + + if (!info->gpos) return 0; + + data = info->data + info->gpos; + + if (ttUSHORT(data+0) != 1) return 0; // Major version 1 + if (ttUSHORT(data+2) != 0) return 0; // Minor version 0 + + lookupListOffset = ttUSHORT(data+8); + lookupList = data + lookupListOffset; + lookupCount = ttUSHORT(lookupList); + + for (i=0; i= pairSetCount) return 0; + + needle=glyph2; + r=pairValueCount-1; + l=0; + + // Binary search. + while (l <= r) { + stbtt_uint16 secondGlyph; + stbtt_uint8 *pairValue; + m = (l + r) >> 1; + pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; + secondGlyph = ttUSHORT(pairValue); + straw = secondGlyph; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + stbtt_int16 xAdvance = ttSHORT(pairValue + 2); + return xAdvance; + } + } + } else + return 0; + break; + } + + case 2: { + stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); + stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); + if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats? + stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); + stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); + int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); + int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); + + stbtt_uint16 class1Count = ttUSHORT(table + 12); + stbtt_uint16 class2Count = ttUSHORT(table + 14); + stbtt_uint8 *class1Records, *class2Records; + stbtt_int16 xAdvance; + + if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed + if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed + + class1Records = table + 16; + class2Records = class1Records + 2 * (glyph1class * class2Count); + xAdvance = ttSHORT(class2Records + 2 * glyph2class); + return xAdvance; + } else + return 0; + break; + } + + default: + return 0; // Unsupported position format + } + } + } + + return 0; +} + +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2) +{ + int xAdvance = 0; + + if (info->gpos) + xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); + else if (info->kern) + xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); + + return xAdvance; +} + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs + return 0; + return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); +} + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap) +{ + int tab = stbtt__find_table(info->data, info->fontstart, "OS/2"); + if (!tab) + return 0; + if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68); + if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70); + if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72); + return 1; +} + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +{ + *x0 = ttSHORT(info->data + info->head + 36); + *y0 = ttSHORT(info->data + info->head + 38); + *x1 = ttSHORT(info->data + info->head + 40); + *y1 = ttSHORT(info->data + info->head + 42); +} + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +{ + int unitsPerEm = ttUSHORT(info->data + info->head + 18); + return pixels / unitsPerEm; +} + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl) +{ + int i; + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info); + + int numEntries = ttUSHORT(svg_doc_list); + stbtt_uint8 *svg_docs = svg_doc_list + 2; + + for(i=0; i= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2))) + return svg_doc; + } + return 0; +} + +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg) +{ + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc; + + if (info->svg == 0) + return 0; + + svg_doc = stbtt_FindSVGDoc(info, gl); + if (svg_doc != NULL) { + *svg = (char *) data + info->svg + ttULONG(svg_doc + 4); + return ttULONG(svg_doc + 8); + } else { + return 0; + } +} + +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg) +{ + return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg); +} + +////////////////////////////////////////////////////////////////////////////// +// +// antialiasing software rasterizer +// + +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + // e.g. space character + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + // move to integral bboxes (treating pixels as little squares, what pixels get touched)? + if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); + if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); + if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); + if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); + } +} + +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Rasterizer + +typedef struct stbtt__hheap_chunk +{ + struct stbtt__hheap_chunk *next; +} stbtt__hheap_chunk; + +typedef struct stbtt__hheap +{ + struct stbtt__hheap_chunk *head; + void *first_free; + int num_remaining_in_head_chunk; +} stbtt__hheap; + +static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) +{ + if (hh->first_free) { + void *p = hh->first_free; + hh->first_free = * (void **) p; + return p; + } else { + if (hh->num_remaining_in_head_chunk == 0) { + int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); + stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); + if (c == NULL) + return NULL; + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = count; + } + --hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk; + } +} + +static void stbtt__hheap_free(stbtt__hheap *hh, void *p) +{ + *(void **) p = hh->first_free; + hh->first_free = p; +} + +static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) +{ + stbtt__hheap_chunk *c = hh->head; + while (c) { + stbtt__hheap_chunk *n = c->next; + STBTT_free(c, userdata); + c = n; + } +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + + +typedef struct stbtt__active_edge +{ + struct stbtt__active_edge *next; + #if STBTT_RASTERIZER_VERSION==1 + int x,dx; + float ey; + int direction; + #elif STBTT_RASTERIZER_VERSION==2 + float fx,fdx,fdy; + float direction; + float sy; + float ey; + #else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" + #endif +} stbtt__active_edge; + +#if STBTT_RASTERIZER_VERSION == 1 +#define STBTT_FIXSHIFT 10 +#define STBTT_FIX (1 << STBTT_FIXSHIFT) +#define STBTT_FIXMASK (STBTT_FIX-1) + +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + if (!z) return z; + + // round dx down to avoid overshooting + if (dxdy < 0) + z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); + else + z->dx = STBTT_ifloor(STBTT_FIX * dxdy); + + z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount + z->x -= off_x * STBTT_FIX; + + z->ey = e->y1; + z->next = 0; + z->direction = e->invert ? 1 : -1; + return z; +} +#elif STBTT_RASTERIZER_VERSION == 2 +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + //STBTT_assert(e->y0 <= start_point); + if (!z) return z; + z->fdx = dxdy; + z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; + z->fx = e->x0 + dxdy * (start_point - e->y0); + z->fx -= off_x; + z->direction = e->invert ? 1.0f : -1.0f; + z->sy = e->y0; + z->ey = e->y1; + z->next = 0; + return z; +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#if STBTT_RASTERIZER_VERSION == 1 +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + // non-zero winding fill + int x0=0, w=0; + + while (e) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->direction; + } else { + int x1 = e->x; w += e->direction; + // if we went to zero, we need to draw + if (w == 0) { + int i = x0 >> STBTT_FIXSHIFT; + int j = x1 >> STBTT_FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); // weight per vertical scanline + int s; // vertical subsample index + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + // find center of pixel for this scanline + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); + if (z != NULL) { + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + } + ++e; + } + + // now process all active edges in XOR fashion + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +#elif STBTT_RASTERIZER_VERSION == 2 + +// the edge passed in here does not cross the vertical line at x or the vertical line at x+1 +// (i.e. it has already been clipped to those) +static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) +{ + if (y0 == y1) return; + STBTT_assert(y0 < y1); + STBTT_assert(e->sy <= e->ey); + if (y0 > e->ey) return; + if (y1 < e->sy) return; + if (y0 < e->sy) { + x0 += (x1-x0) * (e->sy - y0) / (y1-y0); + y0 = e->sy; + } + if (y1 > e->ey) { + x1 += (x1-x0) * (e->ey - y1) / (y1-y0); + y1 = e->ey; + } + + if (x0 == x) + STBTT_assert(x1 <= x+1); + else if (x0 == x+1) + STBTT_assert(x1 >= x); + else if (x0 <= x) + STBTT_assert(x1 <= x); + else if (x0 >= x+1) + STBTT_assert(x1 >= x+1); + else + STBTT_assert(x1 >= x && x1 <= x+1); + + if (x0 <= x && x1 <= x) + scanline[x] += e->direction * (y1-y0); + else if (x0 >= x+1 && x1 >= x+1) + ; + else { + STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); + scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position + } +} + +static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width) +{ + STBTT_assert(top_width >= 0); + STBTT_assert(bottom_width >= 0); + return (top_width + bottom_width) / 2.0f * height; +} + +static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1) +{ + return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0); +} + +static float stbtt__sized_triangle_area(float height, float width) +{ + return height * width / 2; +} + +static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) +{ + float y_bottom = y_top+1; + + while (e) { + // brute force every pixel + + // compute intersection points with top & bottom + STBTT_assert(e->ey >= y_top); + + if (e->fdx == 0) { + float x0 = e->fx; + if (x0 < len) { + if (x0 >= 0) { + stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); + stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); + } else { + stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); + } + } + } else { + float x0 = e->fx; + float dx = e->fdx; + float xb = x0 + dx; + float x_top, x_bottom; + float sy0,sy1; + float dy = e->fdy; + STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); + + // compute endpoints of line segment clipped to this scanline (if the + // line segment starts on this scanline. x0 is the intersection of the + // line with y_top, but that may be off the line segment. + if (e->sy > y_top) { + x_top = x0 + dx * (e->sy - y_top); + sy0 = e->sy; + } else { + x_top = x0; + sy0 = y_top; + } + if (e->ey < y_bottom) { + x_bottom = x0 + dx * (e->ey - y_top); + sy1 = e->ey; + } else { + x_bottom = xb; + sy1 = y_bottom; + } + + if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { + // from here on, we don't have to range check x values + + if ((int) x_top == (int) x_bottom) { + float height; + // simple case, only spans one pixel + int x = (int) x_top; + height = (sy1 - sy0) * e->direction; + STBTT_assert(x >= 0 && x < len); + scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f); + scanline_fill[x] += height; // everything right of this pixel is filled + } else { + int x,x1,x2; + float y_crossing, y_final, step, sign, area; + // covers 2+ pixels + if (x_top > x_bottom) { + // flip scanline vertically; signed area is the same + float t; + sy0 = y_bottom - (sy0 - y_top); + sy1 = y_bottom - (sy1 - y_top); + t = sy0, sy0 = sy1, sy1 = t; + t = x_bottom, x_bottom = x_top, x_top = t; + dx = -dx; + dy = -dy; + t = x0, x0 = xb, xb = t; + } + STBTT_assert(dy >= 0); + STBTT_assert(dx >= 0); + + x1 = (int) x_top; + x2 = (int) x_bottom; + // compute intersection with y axis at x1+1 + y_crossing = y_top + dy * (x1+1 - x0); + + // compute intersection with y axis at x2 + y_final = y_top + dy * (x2 - x0); + + // x1 x_top x2 x_bottom + // y_top +------|-----+------------+------------+--------|---+------------+ + // | | | | | | + // | | | | | | + // sy0 | Txxxxx|............|............|............|............| + // y_crossing | *xxxxx.......|............|............|............| + // | | xxxxx..|............|............|............| + // | | /- xx*xxxx........|............|............| + // | | dy < | xxxxxx..|............|............| + // y_final | | \- | xx*xxx.........|............| + // sy1 | | | | xxxxxB...|............| + // | | | | | | + // | | | | | | + // y_bottom +------------+------------+------------+------------+------------+ + // + // goal is to measure the area covered by '.' in each pixel + + // if x2 is right at the right edge of x1, y_crossing can blow up, github #1057 + // @TODO: maybe test against sy1 rather than y_bottom? + if (y_crossing > y_bottom) + y_crossing = y_bottom; + + sign = e->direction; + + // area of the rectangle covered from sy0..y_crossing + area = sign * (y_crossing-sy0); + + // area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing) + scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top); + + // check if final y_crossing is blown up; no test case for this + if (y_final > y_bottom) { + y_final = y_bottom; + dy = (y_final - y_crossing ) / (x2 - (x1+1)); // if denom=0, y_final = y_crossing, so y_final <= y_bottom + } + + // in second pixel, area covered by line segment found in first pixel + // is always a rectangle 1 wide * the height of that line segment; this + // is exactly what the variable 'area' stores. it also gets a contribution + // from the line segment within it. the THIRD pixel will get the first + // pixel's rectangle contribution, the second pixel's rectangle contribution, + // and its own contribution. the 'own contribution' is the same in every pixel except + // the leftmost and rightmost, a trapezoid that slides down in each pixel. + // the second pixel's contribution to the third pixel will be the + // rectangle 1 wide times the height change in the second pixel, which is dy. + + step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x, + // which multiplied by 1-pixel-width is how much pixel area changes for each step in x + // so the area advances by 'step' every time + + for (x = x1+1; x < x2; ++x) { + scanline[x] += area + step/2; // area of trapezoid is 1*step/2 + area += step; + } + STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down + STBTT_assert(sy1 > y_final-0.01f); + + // area covered in the last pixel is the rectangle from all the pixels to the left, + // plus the trapezoid filled by the line segment in this pixel all the way to the right edge + scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f); + + // the rest of the line is filled based on the total height of the line segment in this pixel + scanline_fill[x2] += sign * (sy1-sy0); + } + } else { + // if edge goes outside of box we're drawing, we require + // clipping logic. since this does not match the intended use + // of this library, we use a different, very slow brute + // force implementation + // note though that this does happen some of the time because + // x_top and x_bottom can be extrapolated at the top & bottom of + // the shape and actually lie outside the bounding box + int x; + for (x=0; x < len; ++x) { + // cases: + // + // there can be up to two intersections with the pixel. any intersection + // with left or right edges can be handled by splitting into two (or three) + // regions. intersections with top & bottom do not necessitate case-wise logic. + // + // the old way of doing this found the intersections with the left & right edges, + // then used some simple logic to produce up to three segments in sorted order + // from top-to-bottom. however, this had a problem: if an x edge was epsilon + // across the x border, then the corresponding y position might not be distinct + // from the other y segment, and it might ignored as an empty segment. to avoid + // that, we need to explicitly produce segments based on x positions. + + // rename variables to clearly-defined pairs + float y0 = y_top; + float x1 = (float) (x); + float x2 = (float) (x+1); + float x3 = xb; + float y3 = y_bottom; + + // x = e->x + e->dx * (y-y_top) + // (y-y_top) = (x - e->x) / e->dx + // y = (x - e->x) / e->dx + y_top + float y1 = (x - x0) / dx + y_top; + float y2 = (x+1 - x0) / dx + y_top; + + if (x0 < x1 && x3 > x2) { // three segments descending down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x1 && x0 > x2) { // three segments descending down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else { // one segment + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); + } + } + } + } + e = e->next; + } +} + +// directly AA rasterize edges w/o supersampling +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0, i; + float scanline_data[129], *scanline, *scanline2; + + STBTT__NOTUSED(vsubsample); + + if (result->w > 64) + scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); + else + scanline = scanline_data; + + scanline2 = scanline + result->w; + + y = off_y; + e[n].y0 = (float) (off_y + result->h) + 1; + + while (j < result->h) { + // find center of pixel for this scanline + float scan_y_top = y + 0.0f; + float scan_y_bottom = y + 1.0f; + stbtt__active_edge **step = &active; + + STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); + STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); + + // update all active edges; + // remove all active edges that terminate before the top of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y_top) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + step = &((*step)->next); // advance through list + } + } + + // insert all edges that start before the bottom of this scanline + while (e->y0 <= scan_y_bottom) { + if (e->y0 != e->y1) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); + if (z != NULL) { + if (j == 0 && off_y != 0) { + if (z->ey < scan_y_top) { + // this can happen due to subpixel positioning and some kind of fp rounding error i think + z->ey = scan_y_top; + } + } + STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds + // insert at front + z->next = active; + active = z; + } + } + ++e; + } + + // now process all active edges + if (active) + stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); + + { + float sum = 0; + for (i=0; i < result->w; ++i) { + float k; + int m; + sum += scanline2[i]; + k = scanline[i] + sum; + k = (float) STBTT_fabs(k)*255 + 0.5f; + m = (int) k; + if (m > 255) m = 255; + result->pixels[j*result->stride + i] = (unsigned char) m; + } + } + // advance all the edges + step = &active; + while (*step) { + stbtt__active_edge *z = *step; + z->fx += z->fdx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + + ++y; + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) + +static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) +{ + int i,j; + for (i=1; i < n; ++i) { + stbtt__edge t = p[i], *a = &t; + j = i; + while (j > 0) { + stbtt__edge *b = &p[j-1]; + int c = STBTT__COMPARE(a,b); + if (!c) break; + p[j] = p[j-1]; + --j; + } + if (i != j) + p[j] = t; + } +} + +static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) +{ + /* threshold for transitioning to insertion sort */ + while (n > 12) { + stbtt__edge t; + int c01,c12,c,m,i,j; + + /* compute median of three */ + m = n >> 1; + c01 = STBTT__COMPARE(&p[0],&p[m]); + c12 = STBTT__COMPARE(&p[m],&p[n-1]); + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ + if (c01 != c12) { + /* otherwise, we'll need to swap something else to middle */ + int z; + c = STBTT__COMPARE(&p[0],&p[n-1]); + /* 0>mid && midn => n; 0 0 */ + /* 0n: 0>n => 0; 0 n */ + z = (c == c12) ? 0 : n-1; + t = p[z]; + p[z] = p[m]; + p[m] = t; + } + /* now p[m] is the median-of-three */ + /* swap it to the beginning so it won't move around */ + t = p[0]; + p[0] = p[m]; + p[m] = t; + + /* partition loop */ + i=1; + j=n-1; + for(;;) { + /* handling of equality is crucial here */ + /* for sentinels & efficiency with duplicates */ + for (;;++i) { + if (!STBTT__COMPARE(&p[i], &p[0])) break; + } + for (;;--j) { + if (!STBTT__COMPARE(&p[0], &p[j])) break; + } + /* make sure we haven't crossed */ + if (i >= j) break; + t = p[i]; + p[i] = p[j]; + p[j] = t; + + ++i; + --j; + } + /* recurse on smaller side, iterate on larger */ + if (j < (n-i)) { + stbtt__sort_edges_quicksort(p,j); + p = p+i; + n = n-i; + } else { + stbtt__sort_edges_quicksort(p+i, n-i); + n = j; + } + } +} + +static void stbtt__sort_edges(stbtt__edge *p, int n) +{ + stbtt__sort_edges_quicksort(p, n); + stbtt__sort_edges_ins_sort(p, n); +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; +#if STBTT_RASTERIZER_VERSION == 1 + int vsubsample = result->h < 8 ? 15 : 5; +#elif STBTT_RASTERIZER_VERSION == 2 + int vsubsample = 1; +#else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + // vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + // now we have to blow out the windings into explicit edge lists + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + // skip the edge if horizontal + if (p[j].y == p[k].y) + continue; + // add edge from j to k to the list + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; + ++n; + } + } + + // now sort the edges by their highest point (should snap to integer, and then by x) + //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + stbtt__sort_edges(e, n); + + // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; // during first pass, it's unallocated + points[n].x = x; + points[n].y = y; +} + +// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + // midpoint + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + // versus directly drawn line + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) // 65536 segments on one curve better be enough! + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) +{ + // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough + float dx0 = x1-x0; + float dy0 = y1-y0; + float dx1 = x2-x1; + float dy1 = y2-y1; + float dx2 = x3-x2; + float dy2 = y3-y2; + float dx = x3-x0; + float dy = y3-y0; + float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2)); + float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy); + float flatness_squared = longlen*longlen-shortlen*shortlen; + + if (n > 16) // 65536 segments on one curve better be enough! + return; + + if (flatness_squared > objspace_flatness_squared) { + float x01 = (x0+x1)/2; + float y01 = (y0+y1)/2; + float x12 = (x1+x2)/2; + float y12 = (y1+y2)/2; + float x23 = (x2+x3)/2; + float y23 = (y2+y3)/2; + + float xa = (x01+x12)/2; + float ya = (y01+y12)/2; + float xb = (x12+x23)/2; + float yb = (y12+y23)/2; + + float mx = (xa+xb)/2; + float my = (ya+yb)/2; + + stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x3,y3); + *num_points = *num_points+1; + } +} + +// returns number of contours +static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + // count how many "moves" there are to get the contour count + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + // make two passes through the points so we don't need to realloc + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + // start the next contour + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + case STBTT_vcubic: + stbtt__tesselate_cubic(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].cx1, vertices[i].cy1, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count = 0; + int *winding_lengths = NULL; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) { + STBTT_free(vertices, info->userdata); + return NULL; + } + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); + + // now we get the size + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; // in case we error + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); +} + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-CRAPPY packing to keep source code small + +static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo f; + f.userdata = NULL; + if (!stbtt_InitFont(&f, data, offset)) + return -1; + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&f, first_char + i); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // advance to next row + if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 1; + if (y+gh+1 > bottom_y) + bottom_y = y+gh+1; + } + return bottom_y; +} + +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// rectangle packing replacement routines if you don't have stb_rect_pack.h +// + +#ifndef STB_RECT_PACK_VERSION + +typedef int stbrp_coord; + +//////////////////////////////////////////////////////////////////////////////////// +// // +// // +// COMPILER WARNING ?!?!? // +// // +// // +// if you get a compile warning due to these symbols being defined more than // +// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // +// // +//////////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + int width,height; + int x,y,bottom_y; +} stbrp_context; + +typedef struct +{ + unsigned char x; +} stbrp_node; + +struct stbrp_rect +{ + stbrp_coord x,y; + int id,w,h,was_packed; +}; + +static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) +{ + con->width = pw; + con->height = ph; + con->x = 0; + con->y = 0; + con->bottom_y = 0; + STBTT__NOTUSED(nodes); + STBTT__NOTUSED(num_nodes); +} + +static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) +{ + int i; + for (i=0; i < num_rects; ++i) { + if (con->x + rects[i].w > con->width) { + con->x = 0; + con->y = con->bottom_y; + } + if (con->y + rects[i].h > con->height) + break; + rects[i].x = con->x; + rects[i].y = con->y; + rects[i].was_packed = 1; + con->x += rects[i].w; + if (con->y + rects[i].h > con->bottom_y) + con->bottom_y = con->y + rects[i].h; + } + for ( ; i < num_rects; ++i) + rects[i].was_packed = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If +// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) +{ + stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); + int num_nodes = pw - padding; + stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); + + if (context == NULL || nodes == NULL) { + if (context != NULL) STBTT_free(context, alloc_context); + if (nodes != NULL) STBTT_free(nodes , alloc_context); + return 0; + } + + spc->user_allocator_context = alloc_context; + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + spc->skip_missing = 0; + + stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + + if (pixels) + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + + return 1; +} + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) +{ + STBTT_free(spc->nodes , spc->user_allocator_context); + STBTT_free(spc->pack_info, spc->user_allocator_context); +} + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) +{ + STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); + STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); + if (h_oversample <= STBTT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= STBTT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip) +{ + spc->skip_missing = skip; +} + +#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) + +static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < h; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < w; ++i) { + STBTT_assert(pixels[i] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i] = (unsigned char) (total / kernel_width); + } + + pixels += stride_in_bytes; + } +} + +static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < w; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < h; ++i) { + STBTT_assert(pixels[i*stride_in_bytes] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + + pixels += 1; + } +} + +static float stbtt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + // The prefilter is a box filter of width "oversample", + // which shifts phase by (oversample - 1)/2 pixels in + // oversampled space. We want to shift in the opposite + // direction to counter this. + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k; + int missing_glyph_added = 0; + + k=0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + ranges[i].h_oversample = (unsigned char) spc->h_oversample; + ranges[i].v_oversample = (unsigned char) spc->v_oversample; + for (j=0; j < ranges[i].num_chars; ++j) { + int x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) { + rects[k].w = rects[k].h = 0; + } else { + stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + if (glyph == 0) + missing_glyph_added = 1; + } + ++k; + } + } + + return k; +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, + output, + out_w - (prefilter_x - 1), + out_h - (prefilter_y - 1), + out_stride, + scale_x, + scale_y, + shift_x, + shift_y, + glyph); + + if (prefilter_x > 1) + stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x); + + if (prefilter_y > 1) + stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y); + + *sub_x = stbtt__oversample_shift(prefilter_x); + *sub_y = stbtt__oversample_shift(prefilter_y); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k, missing_glyph = -1, return_value = 1; + + // save current values + int old_h_over = spc->h_oversample; + int old_v_over = spc->v_oversample; + + k = 0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + float recip_h,recip_v,sub_x,sub_y; + spc->h_oversample = ranges[i].h_oversample; + spc->v_oversample = ranges[i].v_oversample; + recip_h = 1.0f / spc->h_oversample; + recip_v = 1.0f / spc->v_oversample; + sub_x = stbtt__oversample_shift(spc->h_oversample); + sub_y = stbtt__oversample_shift(spc->v_oversample); + for (j=0; j < ranges[i].num_chars; ++j) { + stbrp_rect *r = &rects[k]; + if (r->was_packed && r->w != 0 && r->h != 0) { + stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + stbrp_coord pad = (stbrp_coord) spc->padding; + + // pad on left and top + r->x += pad; + r->y += pad; + r->w -= pad; + r->h -= pad; + stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); + stbtt_GetGlyphBitmapBox(info, glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + &x0,&y0,&x1,&y1); + stbtt_MakeGlyphBitmapSubpixel(info, + spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w - spc->h_oversample+1, + r->h - spc->v_oversample+1, + spc->stride_in_bytes, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + glyph); + + if (spc->h_oversample > 1) + stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->h_oversample); + + if (spc->v_oversample > 1) + stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->v_oversample); + + bc->x0 = (stbtt_int16) r->x; + bc->y0 = (stbtt_int16) r->y; + bc->x1 = (stbtt_int16) (r->x + r->w); + bc->y1 = (stbtt_int16) (r->y + r->h); + bc->xadvance = scale * advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = (x0 + r->w) * recip_h + sub_x; + bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + + if (glyph == 0) + missing_glyph = j; + } else if (spc->skip_missing) { + return_value = 0; + } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) { + ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph]; + } else { + return_value = 0; // if any fail, report failure + } + + ++k; + } + } + + // restore original values + spc->h_oversample = old_h_over; + spc->v_oversample = old_v_over; + + return return_value; +} + +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) +{ + stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); +} + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +{ + stbtt_fontinfo info; + int i,j,n, return_value = 1; + //stbrp_context *context = (stbrp_context *) spc->pack_info; + stbrp_rect *rects; + + // flag all characters as NOT packed + for (i=0; i < num_ranges; ++i) + for (j=0; j < ranges[i].num_chars; ++j) + ranges[i].chardata_for_range[j].x0 = + ranges[i].chardata_for_range[j].y0 = + ranges[i].chardata_for_range[j].x1 = + ranges[i].chardata_for_range[j].y1 = 0; + + n = 0; + for (i=0; i < num_ranges; ++i) + n += ranges[i].num_chars; + + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); + if (rects == NULL) + return 0; + + info.userdata = spc->user_allocator_context; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); + + n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); + + stbtt_PackFontRangesPackRects(spc, rects, n); + + return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); + + STBTT_free(rects, spc->user_allocator_context); + return return_value; +} + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, + int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) +{ + stbtt_pack_range range; + range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; + range.array_of_unicode_codepoints = NULL; + range.num_chars = num_chars_in_range; + range.chardata_for_range = chardata_for_range; + range.font_size = font_size; + return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); +} + +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap) +{ + int i_ascent, i_descent, i_lineGap; + float scale; + stbtt_fontinfo info; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index)); + scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size); + stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap); + *ascent = (float) i_ascent * scale; + *descent = (float) i_descent * scale; + *lineGap = (float) i_lineGap * scale; +} + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) +{ + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_packedchar *b = chardata + char_index; + + if (align_to_integer) { + float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); + float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// sdf computation +// + +#define STBTT_min(a,b) ((a) < (b) ? (a) : (b)) +#define STBTT_max(a,b) ((a) < (b) ? (b) : (a)) + +static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2]) +{ + float q0perp = q0[1]*ray[0] - q0[0]*ray[1]; + float q1perp = q1[1]*ray[0] - q1[0]*ray[1]; + float q2perp = q2[1]*ray[0] - q2[0]*ray[1]; + float roperp = orig[1]*ray[0] - orig[0]*ray[1]; + + float a = q0perp - 2*q1perp + q2perp; + float b = q1perp - q0perp; + float c = q0perp - roperp; + + float s0 = 0., s1 = 0.; + int num_s = 0; + + if (a != 0.0) { + float discr = b*b - a*c; + if (discr > 0.0) { + float rcpna = -1 / a; + float d = (float) STBTT_sqrt(discr); + s0 = (b+d) * rcpna; + s1 = (b-d) * rcpna; + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) { + if (num_s == 0) s0 = s1; + ++num_s; + } + } + } else { + // 2*b*s + c = 0 + // s = -c / (2*b) + s0 = c / (-2 * b); + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + } + + if (num_s == 0) + return 0; + else { + float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]); + float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2; + + float q0d = q0[0]*rayn_x + q0[1]*rayn_y; + float q1d = q1[0]*rayn_x + q1[1]*rayn_y; + float q2d = q2[0]*rayn_x + q2[1]*rayn_y; + float rod = orig[0]*rayn_x + orig[1]*rayn_y; + + float q10d = q1d - q0d; + float q20d = q2d - q0d; + float q0rd = q0d - rod; + + hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d; + hits[0][1] = a*s0+b; + + if (num_s > 1) { + hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d; + hits[1][1] = a*s1+b; + return 2; + } else { + return 1; + } + } +} + +static int equal(float *a, float *b) +{ + return (a[0] == b[0] && a[1] == b[1]); +} + +static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts) +{ + int i; + float orig[2], ray[2] = { 1, 0 }; + float y_frac; + int winding = 0; + + // make sure y never passes through a vertex of the shape + y_frac = (float) STBTT_fmod(y, 1.0f); + if (y_frac < 0.01f) + y += 0.01f; + else if (y_frac > 0.99f) + y -= 0.01f; + + orig[0] = x; + orig[1] = y; + + // test a ray from (-infinity,y) to (x,y) + for (i=0; i < nverts; ++i) { + if (verts[i].type == STBTT_vline) { + int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y; + int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } + if (verts[i].type == STBTT_vcurve) { + int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ; + int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy; + int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ; + int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2)); + int by = STBTT_max(y0,STBTT_max(y1,y2)); + if (y > ay && y < by && x > ax) { + float q0[2],q1[2],q2[2]; + float hits[2][2]; + q0[0] = (float)x0; + q0[1] = (float)y0; + q1[0] = (float)x1; + q1[1] = (float)y1; + q2[0] = (float)x2; + q2[1] = (float)y2; + if (equal(q0,q1) || equal(q1,q2)) { + x0 = (int)verts[i-1].x; + y0 = (int)verts[i-1].y; + x1 = (int)verts[i ].x; + y1 = (int)verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } else { + int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits); + if (num_hits >= 1) + if (hits[0][0] < 0) + winding += (hits[0][1] < 0 ? -1 : 1); + if (num_hits >= 2) + if (hits[1][0] < 0) + winding += (hits[1][1] < 0 ? -1 : 1); + } + } + } + } + return winding; +} + +static float stbtt__cuberoot( float x ) +{ + if (x<0) + return -(float) STBTT_pow(-x,1.0f/3.0f); + else + return (float) STBTT_pow( x,1.0f/3.0f); +} + +// x^3 + a*x^2 + b*x + c = 0 +static int stbtt__solve_cubic(float a, float b, float c, float* r) +{ + float s = -a / 3; + float p = b - a*a / 3; + float q = a * (2*a*a - 9*b) / 27 + c; + float p3 = p*p*p; + float d = q*q + 4*p3 / 27; + if (d >= 0) { + float z = (float) STBTT_sqrt(d); + float u = (-q + z) / 2; + float v = (-q - z) / 2; + u = stbtt__cuberoot(u); + v = stbtt__cuberoot(v); + r[0] = s + u + v; + return 1; + } else { + float u = (float) STBTT_sqrt(-p/3); + float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative + float m = (float) STBTT_cos(v); + float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; + r[0] = s + u * 2 * m; + r[1] = s - u * (m + n); + r[2] = s - u * (m - n); + + //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? + //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); + //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); + return 3; + } +} + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + float scale_x = scale, scale_y = scale; + int ix0,iy0,ix1,iy1; + int w,h; + unsigned char *data; + + if (scale == 0) return NULL; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); + + // if empty, return NULL + if (ix0 == ix1 || iy0 == iy1) + return NULL; + + ix0 -= padding; + iy0 -= padding; + ix1 += padding; + iy1 += padding; + + w = (ix1 - ix0); + h = (iy1 - iy0); + + if (width ) *width = w; + if (height) *height = h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + // invert for y-downwards bitmaps + scale_y = -scale_y; + + { + int x,y,i,j; + float *precompute; + stbtt_vertex *verts; + int num_verts = stbtt_GetGlyphShape(info, glyph, &verts); + data = (unsigned char *) STBTT_malloc(w * h, info->userdata); + precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata); + + for (i=0,j=num_verts-1; i < num_verts; j=i++) { + if (verts[i].type == STBTT_vline) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; + float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); + precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; + float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; + float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float len2 = bx*bx + by*by; + if (len2 != 0.0f) + precompute[i] = 1.0f / (bx*bx + by*by); + else + precompute[i] = 0.0f; + } else + precompute[i] = 0.0f; + } + + for (y=iy0; y < iy1; ++y) { + for (x=ix0; x < ix1; ++x) { + float val; + float min_dist = 999999.0f; + float sx = (float) x + 0.5f; + float sy = (float) y + 0.5f; + float x_gspace = (sx / scale_x); + float y_gspace = (sy / scale_y); + + int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path + + for (i=0; i < num_verts; ++i) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + + if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) { + float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; + + float dist,dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); + if (dist2 < min_dist*min_dist) + min_dist = (float) STBTT_sqrt(dist2); + + // coarse culling against bbox + //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && + // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) + dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; + STBTT_assert(i != 0); + if (dist < min_dist) { + // check position along line + // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0) + // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy) + float dx = x1-x0, dy = y1-y0; + float px = x0-sx, py = y0-sy; + // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy + // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve + float t = -(px*dx + py*dy) / (dx*dx + dy*dy); + if (t >= 0.0f && t <= 1.0f) + min_dist = dist; + } + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y; + float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y; + float box_x0 = STBTT_min(STBTT_min(x0,x1),x2); + float box_y0 = STBTT_min(STBTT_min(y0,y1),y2); + float box_x1 = STBTT_max(STBTT_max(x0,x1),x2); + float box_y1 = STBTT_max(STBTT_max(y0,y1),y2); + // coarse culling against bbox to avoid computing cubic unnecessarily + if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) { + int num=0; + float ax = x1-x0, ay = y1-y0; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float mx = x0 - sx, my = y0 - sy; + float res[3] = {0.f,0.f,0.f}; + float px,py,t,it,dist2; + float a_inv = precompute[i]; + if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula + float a = 3*(ax*bx + ay*by); + float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); + float c = mx*ax+my*ay; + if (a == 0.0) { // if a is 0, it's linear + if (b != 0.0) { + res[num++] = -c/b; + } + } else { + float discriminant = b*b - 4*a*c; + if (discriminant < 0) + num = 0; + else { + float root = (float) STBTT_sqrt(discriminant); + res[0] = (-b - root)/(2*a); + res[1] = (-b + root)/(2*a); + num = 2; // don't bother distinguishing 1-solution case, as code below will still work + } + } + } else { + float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point + float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv; + float d = (mx*ax+my*ay) * a_inv; + num = stbtt__solve_cubic(b, c, d, res); + } + dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); + if (dist2 < min_dist*min_dist) + min_dist = (float) STBTT_sqrt(dist2); + + if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { + t = res[0], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) { + t = res[1], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) { + t = res[2], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + } + } + } + if (winding == 0) + min_dist = -min_dist; // if outside the shape, value is negative + val = onedge_value + pixel_dist_scale * min_dist; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + data[(y-iy0)*w+(x-ix0)] = (unsigned char) val; + } + } + STBTT_free(precompute, info->userdata); + STBTT_free(verts, info->userdata); + } + return data; +} + +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// font name matching -- recommended not to use this +// + +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + // convert utf16 to utf8 and compare the results while converting + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; // plus another 2 below + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); +} + +// returns results in whatever encoding you request... but note that 2-byte encodings +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + // find the encoding + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + // is this a Unicode encoding? + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8); + stbtt_int32 off = ttUSHORT(fc+loc+10); + + // check if there's a prefix match + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + // check for target_id+1 immediately following, with same encoding & language + if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { + slen = ttUSHORT(fc+loc+12+8); + off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + // if nothing immediately following + if (matchlen == nlen) + return 1; + } + } + } + + // @TODO handle other encodings + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + // check italics/bold/underline flags in macStyle... + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + // if we checked the macStyle flags, then just check the family and ignore the subfamily + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, + float pixel_height, unsigned char *pixels, int pw, int ph, + int first_char, int num_chars, stbtt_bakedchar *chardata) +{ + return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata); +} + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) +{ + return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); +} + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) +{ + return stbtt_GetNumberOfFonts_internal((unsigned char *) data); +} + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset) +{ + return stbtt_InitFont_internal(info, (unsigned char *) data, offset); +} + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags) +{ + return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags); +} + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2); +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#endif // STB_TRUETYPE_IMPLEMENTATION + + +// FULL VERSION HISTORY +// +// 1.25 (2021-07-11) many fixes +// 1.24 (2020-02-05) fix warning +// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) +// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined +// 1.21 (2019-02-25) fix warning +// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() +// 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod +// 1.18 (2018-01-29) add missing function +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) allow user-defined fabs() replacement +// fix memory leak if fontsize=0.0 +// fix warning from duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// allow PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) +// also more precise AA rasterizer, except if shapes overlap +// remove need for STBTT_sort +// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC +// 1.04 (2015-04-15) typo in example +// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes +// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ +// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match +// non-oversampled; STBTT_POINT_SIZE for packed case only +// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling +// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) +// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID +// 0.8b (2014-07-07) fix a warning +// 0.8 (2014-05-25) fix a few more warnings +// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back +// 0.6c (2012-07-24) improve documentation +// 0.6b (2012-07-20) fix a few more warnings +// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, +// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty +// 0.5 (2011-12-09) bugfixes: +// subpixel glyph renderer computed wrong bounding box +// first vertex of shape can be off-curve (FreeSans) +// 0.4b (2011-12-03) fixed an error in the font baking example +// 0.4 (2011-12-01) kerning, subpixel rendering (tor) +// bugfixes for: +// codepoint-to-glyph conversion using table fmt=12 +// codepoint-to-glyph conversion using table fmt=4 +// stbtt_GetBakedQuad with non-square texture (Zer) +// updated Hello World! sample to use kerning and subpixel +// fixed some warnings +// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +// userdata, malloc-from-userdata, non-zero fill (stb) +// 0.2 (2009-03-11) Fix unsigned/signed char warnings +// 0.1 (2009-03-09) First public release +// + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +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. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +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 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. +------------------------------------------------------------------------------ +*/ diff --git a/external/stb_vorbis.cpp b/external/stb_vorbis.cpp new file mode 100644 index 0000000..d670504 --- /dev/null +++ b/external/stb_vorbis.cpp @@ -0,0 +1,2 @@ +#define STB_VORBIS_IMPLEMENTATION +#include "stb_vorbis.h" diff --git a/external/stb_vorbis.h b/external/stb_vorbis.h new file mode 100644 index 0000000..1bc5b29 --- /dev/null +++ b/external/stb_vorbis.h @@ -0,0 +1,5470 @@ +// Ogg Vorbis audio decoder - v1.14 - public domain +// http://nothings.org/stb_vorbis/ +// +// Original version written by Sean Barrett in 2007. +// +// Originally sponsored by RAD Game Tools. Seeking implementation +// sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker, +// Elias Software, Aras Pranckevicius, and Sean Barrett. +// +// LICENSE +// +// See end of file for license information. +// +// Limitations: +// +// - floor 0 not supported (used in old ogg vorbis files pre-2004) +// - lossless sample-truncation at beginning ignored +// - cannot concatenate multiple vorbis streams +// - sample positions are 32-bit, limiting seekable 192Khz +// files to around 6 hours (Ogg supports 64-bit) +// +// Feature contributors: +// Dougall Johnson (sample-exact seeking) +// +// Bugfix/warning contributors: +// Terje Mathisen Niklas Frykholm Andy Hill +// Casey Muratori John Bolton Gargaj +// Laurent Gomila Marc LeBlanc Ronny Chevalier +// Bernhard Wodo Evan Balster alxprd@github +// Tom Beaumont Ingo Leitgeb Nicolas Guillemot +// Phillip Bennefall Rohit Thiago Goulart +// manxorist@github saga musix github:infatum +// Timur Gagiev BareRose +// +// Partial history: +// 1.14 - 2018-02-11 - delete bogus dealloca usage +// 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) +// 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files +// 1.11 - 2017-07-23 - fix MinGW compilation +// 1.10 - 2017-03-03 - more robust seeking; fix negative stbv_ilog(); clear error in open_memory +// 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version +// 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame +// 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const +// 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) +// some crash fixes when out of memory or with corrupt files +// fix some inappropriately signed shifts +// 1.05 - 2015-04-19 - don't define __forceinline if it's redundant +// 1.04 - 2014-08-27 - fix missing const-correct case in API +// 1.03 - 2014-08-07 - warning fixes +// 1.02 - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows +// 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct) +// 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel; +// (API change) report sample rate for decode-full-file funcs +// +// See end of file for full version history. + + +////////////////////////////////////////////////////////////////////////////// +// +// HEADER BEGINS HERE +// + +#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H +#define STB_VORBIS_INCLUDE_STB_VORBIS_H + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) +#define STB_VORBIS_NO_STDIO +#endif + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_VORBIS_STATIC +#define STBVDEF static +#else +#define STBVDEF extern +#endif + +/////////// THREAD SAFETY + +// Individual stb_vorbis* handles are not thread-safe; you cannot decode from +// them from multiple threads at the same time. However, you can have multiple +// stb_vorbis* handles and decode from them independently in multiple thrads. + + +/////////// MEMORY ALLOCATION + +// normally stb_vorbis uses malloc() to allocate memory at startup, +// and alloca() to allocate temporary memory during a frame on the +// stack. (Memory consumption will depend on the amount of setup +// data in the file and how you set the compile flags for speed +// vs. size. In my test files the maximal-size usage is ~150KB.) +// +// You can modify the wrapper functions in the source (stbv_setup_malloc, +// stbv_setup_temp_malloc, temp_malloc) to change this behavior, or you +// can use a simpler allocation model: you pass in a buffer from +// which stb_vorbis will allocate _all_ its memory (including the +// temp memory). "open" may fail with a VORBIS_outofmem if you +// do not pass in enough data; there is no way to determine how +// much you do need except to succeed (at which point you can +// query get_info to find the exact amount required. yes I know +// this is lame). +// +// If you pass in a non-NULL buffer of the type below, allocation +// will occur from it as described above. Otherwise just pass NULL +// to use malloc()/alloca() + +typedef struct +{ + char *alloc_buffer; + int alloc_buffer_length_in_bytes; +} stb_vorbis_alloc; + + +/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES + +typedef struct stb_vorbis stb_vorbis; + +typedef struct +{ + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int setup_temp_memory_required; + unsigned int temp_memory_required; + + int max_frame_size; +} stb_vorbis_info; + +// get general information about the file +STBVDEF stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); + +// get the last error detected (clears it, too) +STBVDEF int stb_vorbis_get_error(stb_vorbis *f); + +// close an ogg vorbis file and free all memory in use +STBVDEF void stb_vorbis_close(stb_vorbis *f); + +// this function returns the offset (in samples) from the beginning of the +// file that will be returned by the next decode, if it is known, or -1 +// otherwise. after a flush_pushdata() call, this may take a while before +// it becomes valid again. +// NOT WORKING YET after a seek with PULLDATA API +STBVDEF int stb_vorbis_get_sample_offset(stb_vorbis *f); + +// returns the current seek point within the file, or offset from the beginning +// of the memory buffer. In pushdata mode it returns 0. +STBVDEF unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); + +/////////// PUSHDATA API + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +// this API allows you to get blocks of data from any source and hand +// them to stb_vorbis. you have to buffer them; stb_vorbis will tell +// you how much it used, and you have to give it the rest next time; +// and stb_vorbis may not have enough data to work with and you will +// need to give it the same data again PLUS more. Note that the Vorbis +// specification does not bound the size of an individual frame. + +STBVDEF stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char * datablock, int datablock_length_in_bytes, + int *datablock_memory_consumed_in_bytes, + int *error, + const stb_vorbis_alloc *alloc_buffer); +// create a vorbis decoder by passing in the initial data block containing +// the ogg&vorbis headers (you don't need to do parse them, just provide +// the first N bytes of the file--you're told if it's not enough, see below) +// on success, returns an stb_vorbis *, does not set error, returns the amount of +// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; +// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed +// if returns NULL and *error is VORBIS_need_more_data, then the input block was +// incomplete and you need to pass in a larger block from the start of the file + +STBVDEF int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, + const unsigned char *datablock, int datablock_length_in_bytes, + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ); +// decode a frame of audio sample data if possible from the passed-in data block +// +// return value: number of bytes we used from datablock +// +// possible cases: +// 0 bytes used, 0 samples output (need more data) +// N bytes used, 0 samples output (resynching the stream, keep going) +// N bytes used, M samples output (one frame of data) +// note that after opening a file, you will ALWAYS get one N-bytes,0-sample +// frame, because Vorbis always "discards" the first frame. +// +// Note that on resynch, stb_vorbis will rarely consume all of the buffer, +// instead only datablock_length_in_bytes-3 or less. This is because it wants +// to avoid missing parts of a page header if they cross a datablock boundary, +// without writing state-machiney code to record a partial detection. +// +// The number of channels returned are stored in *channels (which can be +// NULL--it is always the same as the number of channels reported by +// get_info). *output will contain an array of float* buffers, one per +// channel. In other words, (*output)[0][0] contains the first sample from +// the first channel, and (*output)[1][0] contains the first sample from +// the second channel. + +STBVDEF void stb_vorbis_flush_pushdata(stb_vorbis *f); +// inform stb_vorbis that your next datablock will not be contiguous with +// previous ones (e.g. you've seeked in the data); future attempts to decode +// frames will cause stb_vorbis to resynchronize (as noted above), and +// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it +// will begin decoding the _next_ frame. +// +// if you want to seek using pushdata, you need to seek in your file, then +// call stb_vorbis_flush_pushdata(), then start calling decoding, then once +// decoding is returning you data, call stb_vorbis_get_sample_offset, and +// if you don't like the result, seek your file again and repeat. +#endif + + +////////// PULLING INPUT API + +#ifndef STB_VORBIS_NO_PULLDATA_API +// This API assumes stb_vorbis is allowed to pull data from a source-- +// either a block of memory containing the _entire_ vorbis stream, or a +// FILE * that you or it create, or possibly some other reading mechanism +// if you go modify the source to replace the FILE * case with some kind +// of callback to your code. (But if you don't support seeking, you may +// just want to go ahead and use pushdata.) + +#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +STBVDEF int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); +#endif +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +STBVDEF int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); +#endif +// decode an entire file and output the data interleaved into a malloc()ed +// buffer stored in *output. The return value is the number of samples +// decoded, or -1 if the file could not be opened or was not an ogg vorbis file. +// When you're done with it, just free() the pointer returned in *output. + +STBVDEF stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an ogg vorbis stream in memory (note +// this must be the entire stream!). on failure, returns NULL and sets *error + +#ifndef STB_VORBIS_NO_STDIO +STBVDEF stb_vorbis * stb_vorbis_open_filename(const char *filename, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from a filename via fopen(). on failure, +// returns NULL and sets *error (possibly to VORBIS_file_open_failure). + +STBVDEF stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell). on failure, returns NULL and sets *error. +// note that stb_vorbis must "own" this stream; if you seek it in between +// calls to stb_vorbis, it will become confused. Morever, if you attempt to +// perform stb_vorbis_seek_*() operations on this file, it will assume it +// owns the _entire_ rest of the file after the start point. Use the next +// function, stb_vorbis_open_file_section(), to limit it. + +STBVDEF stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell); the stream will be of length 'len' bytes. +// on failure, returns NULL and sets *error. note that stb_vorbis must "own" +// this stream; if you seek it in between calls to stb_vorbis, it will become +// confused. +#endif + +STBVDEF int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); +STBVDEF int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); +// these functions seek in the Vorbis file to (approximately) 'sample_number'. +// after calling seek_frame(), the next call to get_frame_*() will include +// the specified sample. after calling stb_vorbis_seek(), the next call to +// stb_vorbis_get_samples_* will start with the specified sample. If you +// do not need to seek to EXACTLY the target sample when using get_samples_*, +// you can also use seek_frame(). + +STBVDEF int stb_vorbis_seek_start(stb_vorbis *f); +// this function is equivalent to stb_vorbis_seek(f,0) + +STBVDEF unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); +STBVDEF float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); +// these functions return the total length of the vorbis stream + +STBVDEF int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); +// decode the next frame and return the number of samples. the number of +// channels returned are stored in *channels (which can be NULL--it is always +// the same as the number of channels reported by get_info). *output will +// contain an array of float* buffers, one per channel. These outputs will +// be overwritten on the next call to stb_vorbis_get_frame_*. +// +// You generally should not intermix calls to stb_vorbis_get_frame_*() +// and stb_vorbis_get_samples_*(), since the latter calls the former. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +STBVDEF int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); +STBVDEF int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); +#endif +// decode the next frame and return the number of *samples* per channel. +// Note that for interleaved data, you pass in the number of shorts (the +// size of your array), but the return value is the number of samples per +// channel, not the total number of samples. +// +// The data is coerced to the number of channels you request according to the +// channel coercion rules (see below). You must pass in the size of your +// buffer(s) so that stb_vorbis will not overwrite the end of the buffer. +// The maximum buffer size needed can be gotten from get_info(); however, +// the Vorbis I specification implies an absolute maximum of 4096 samples +// per channel. + +// Channel coercion rules: +// Let M be the number of channels requested, and N the number of channels present, +// and Cn be the nth channel; let stereo L be the sum of all L and center channels, +// and stereo R be the sum of all R and center channels (channel assignment from the +// vorbis spec). +// M N output +// 1 k sum(Ck) for all k +// 2 * stereo L, stereo R +// k l k > l, the first l channels, then 0s +// k l k <= l, the first k channels +// Note that this is not _good_ surround etc. mixing at all! It's just so +// you get something useful. + +STBVDEF int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); +STBVDEF int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. +// Returns the number of samples stored per channel; it may be less than requested +// at the end of the file. If there are no more samples in the file, returns 0. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +STBVDEF int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); +STBVDEF int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); +#endif +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. Applies the coercion rules above +// to produce 'channels' channels. Returns the number of samples stored per channel; +// it may be less than requested at the end of the file. If there are no more +// samples in the file, returns 0. + +#endif + +//////// ERROR CODES + +enum STBVorbisError +{ + VORBIS__no_error, + + VORBIS_need_more_data=1, // not a real error + + VORBIS_invalid_api_mixing, // can't mix API modes + VORBIS_outofmem, // not enough memory + VORBIS_feature_not_supported, // uses floor 0 + VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small + VORBIS_file_open_failure, // fopen() failed + VORBIS_seek_without_length, // can't seek in unknown-length file + + VORBIS_unexpected_eof=10, // file is truncated? + VORBIS_seek_invalid, // seek past EOF + + // decoding errors (corrupt/invalid stream) -- you probably + // don't care about the exact details of these + + // vorbis errors: + VORBIS_invalid_setup=20, + VORBIS_invalid_stream, + + // ogg errors: + VORBIS_missing_capture_pattern=30, + VORBIS_invalid_stream_structure_version, + VORBIS_continued_packet_flag_invalid, + VORBIS_incorrect_stream_serial_number, + VORBIS_invalid_first_page, + VORBIS_bad_packet_type, + VORBIS_cant_find_last_page, + VORBIS_seek_failed +}; + + +#ifdef __cplusplus +} +#endif + +#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H +// +// HEADER ENDS HERE +// +////////////////////////////////////////////////////////////////////////////// + +#ifdef STB_VORBIS_IMPLEMENTATION + +// global configuration settings (e.g. set these in the project/makefile), +// or just set them in this file at the top (although ideally the first few +// should be visible when the header file is compiled too, although it's not +// crucial) + +// STB_VORBIS_NO_PUSHDATA_API +// does not compile the code for the various stb_vorbis_*_pushdata() +// functions +// #define STB_VORBIS_NO_PUSHDATA_API + +// STB_VORBIS_NO_PULLDATA_API +// does not compile the code for the non-pushdata APIs +// #define STB_VORBIS_NO_PULLDATA_API + +// STB_VORBIS_NO_STDIO +// does not compile the code for the APIs that use FILE *s internally +// or externally (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_STDIO + +// STB_VORBIS_NO_INTEGER_CONVERSION +// does not compile the code for converting audio sample data from +// float to integer (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_INTEGER_CONVERSION + +// STB_VORBIS_NO_FAST_SCALED_FLOAT +// does not use a fast float-to-int trick to accelerate float-to-int on +// most platforms which requires endianness be defined correctly. +// #define STB_VORBIS_NO_FAST_SCALED_FLOAT + + +// STB_VORBIS_MAX_CHANNELS [number] +// globally define this to the maximum number of channels you need. +// The spec does not put a restriction on channels except that +// the count is stored in a byte, so 255 is the hard limit. +// Reducing this saves about 16 bytes per value, so using 16 saves +// (255-16)*16 or around 4KB. Plus anything other memory usage +// I forgot to account for. Can probably go as low as 8 (7.1 audio), +// 6 (5.1 audio), or 2 (stereo only). +#ifndef STB_VORBIS_MAX_CHANNELS +#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? +#endif + +// STB_VORBIS_PUSHDATA_CRC_COUNT [number] +// after a flush_pushdata(), stb_vorbis begins scanning for the +// next valid page, without backtracking. when it finds something +// that looks like a page, it streams through it and verifies its +// CRC32. Should that validation fail, it keeps scanning. But it's +// possible that _while_ streaming through to check the CRC32 of +// one candidate page, it sees another candidate page. This #define +// determines how many "overlapping" candidate pages it can search +// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas +// garbage pages could be as big as 64KB, but probably average ~16KB. +// So don't hose ourselves by scanning an apparent 64KB page and +// missing a ton of real ones in the interim; so minimum of 2 +#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT +#define STB_VORBIS_PUSHDATA_CRC_COUNT 4 +#endif + +// STB_VORBIS_FAST_HUFFMAN_LENGTH [number] +// sets the log size of the huffman-acceleration table. Maximum +// supported value is 24. with larger numbers, more decodings are O(1), +// but the table size is larger so worse cache missing, so you'll have +// to probe (and try multiple ogg vorbis files) to find the sweet spot. +#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH +#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10 +#endif + +// STB_VORBIS_FAST_BINARY_LENGTH [number] +// sets the log size of the binary-search acceleration table. this +// is used in similar fashion to the fast-huffman size to set initial +// parameters for the binary search + +// STB_VORBIS_FAST_HUFFMAN_INT +// The fast huffman tables are much more efficient if they can be +// stored as 16-bit results instead of 32-bit results. This restricts +// the codebooks to having only 65535 possible outcomes, though. +// (At least, accelerated by the huffman table.) +#ifndef STB_VORBIS_FAST_HUFFMAN_INT +#define STB_VORBIS_FAST_HUFFMAN_SHORT +#endif + +// STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH +// If the 'fast huffman' search doesn't succeed, then stb_vorbis falls +// back on binary searching for the correct one. This requires storing +// extra tables with the huffman codes in sorted order. Defining this +// symbol trades off space for speed by forcing a linear search in the +// non-fast case, except for "sparse" codebooks. +// #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + +// STB_VORBIS_DIVIDES_IN_RESIDUE +// stb_vorbis precomputes the result of the scalar residue decoding +// that would otherwise require a divide per chunk. you can trade off +// space for time by defining this symbol. +// #define STB_VORBIS_DIVIDES_IN_RESIDUE + +// STB_VORBIS_DIVIDES_IN_CODEBOOK +// vorbis VQ codebooks can be encoded two ways: with every case explicitly +// stored, or with all elements being chosen from a small range of values, +// and all values possible in all elements. By default, stb_vorbis expands +// this latter kind out to look like the former kind for ease of decoding, +// because otherwise an integer divide-per-vector-element is required to +// unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can +// trade off storage for speed. +//#define STB_VORBIS_DIVIDES_IN_CODEBOOK + +#ifdef STB_VORBIS_CODEBOOK_SHORTS +#error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats" +#endif + +// STB_VORBIS_DIVIDE_TABLE +// this replaces small integer divides in the floor decode loop with +// table lookups. made less than 1% difference, so disabled by default. + +// STB_VORBIS_NO_INLINE_DECODE +// disables the inlining of the scalar codebook fast-huffman decode. +// might save a little codespace; useful for debugging +// #define STB_VORBIS_NO_INLINE_DECODE + +// STB_VORBIS_NO_DEFER_FLOOR +// Normally we only decode the floor without synthesizing the actual +// full curve. We can instead synthesize the curve immediately. This +// requires more memory and is very likely slower, so I don't think +// you'd ever want to do it except for debugging. +// #define STB_VORBIS_NO_DEFER_FLOOR + + + + +////////////////////////////////////////////////////////////////////////////// + +#ifdef STB_VORBIS_NO_PULLDATA_API + #define STB_VORBIS_NO_INTEGER_CONVERSION + #define STB_VORBIS_NO_STDIO +#endif + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) + #define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + + // only need endianness for fast-float-to-int, which we don't + // use for pushdata + + #ifndef STB_VORBIS_BIG_ENDIAN + #define STB_VORBIS_ENDIAN 0 + #else + #define STB_VORBIS_ENDIAN 1 + #endif + +#endif +#endif + + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifndef STB_VORBIS_NO_CRT + #include + #include + #include + #include + + // find definition of alloca if it's not in stdlib.h: + #if defined(_MSC_VER) || defined(__MINGW32__) + #include + #endif + #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) + #include + #endif +#else // STB_VORBIS_NO_CRT + #define NULL 0 + #define malloc(s) 0 + #define free(s) ((void) 0) + #define realloc(s) 0 +#endif // STB_VORBIS_NO_CRT + +#include + +#ifdef __MINGW32__ + // eff you mingw: + // "fixed": + // http://sourceforge.net/p/mingw-w64/mailman/message/32882927/ + // "no that broke the build, reverted, who cares about C": + // http://sourceforge.net/p/mingw-w64/mailman/message/32890381/ + #ifdef __forceinline + #undef __forceinline + #endif + #define __forceinline + #ifndef alloca + #define alloca(s) __builtin_alloca(s) + #endif +#elif !defined(_MSC_VER) + #if __GNUC__ + #define __forceinline inline + #else + #define __forceinline + #endif +#endif + +#if STB_VORBIS_MAX_CHANNELS > 256 +#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range" +#endif + +#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24 +#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range" +#endif + + +#if 0 +#include +#define STBV_CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1]) +#else +#define STBV_CHECK(f) ((void) 0) +#endif + +#define STBV_MAX_BLOCKSIZE_LOG 13 // from specification +#define STBV_MAX_BLOCKSIZE (1 << STBV_MAX_BLOCKSIZE_LOG) + + +typedef unsigned char stbv_uint8; +typedef signed char stbv_int8; +typedef unsigned short stbv_uint16; +typedef signed short stbv_int16; +typedef unsigned int stbv_uint32; +typedef signed int stbv_int32; + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +typedef float stbv_codetype; + +// @NOTE +// +// Some arrays below are tagged "//varies", which means it's actually +// a variable-sized piece of data, but rather than malloc I assume it's +// small enough it's better to just allocate it all together with the +// main thing +// +// Most of the variables are specified with the smallest size I could pack +// them into. It might give better performance to make them all full-sized +// integers. It should be safe to freely rearrange the structures or change +// the sizes larger--nothing relies on silently truncating etc., nor the +// order of variables. + +#define STBV_FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) +#define STBV_FAST_HUFFMAN_TABLE_MASK (STBV_FAST_HUFFMAN_TABLE_SIZE - 1) + +typedef struct +{ + int dimensions, entries; + stbv_uint8 *codeword_lengths; + float minimum_value; + float delta_value; + stbv_uint8 value_bits; + stbv_uint8 lookup_type; + stbv_uint8 sequence_p; + stbv_uint8 sparse; + stbv_uint32 lookup_values; + stbv_codetype *multiplicands; + stbv_uint32 *codewords; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + stbv_int16 fast_huffman[STBV_FAST_HUFFMAN_TABLE_SIZE]; + #else + stbv_int32 fast_huffman[STBV_FAST_HUFFMAN_TABLE_SIZE]; + #endif + stbv_uint32 *sorted_codewords; + int *sorted_values; + int sorted_entries; +} StbvCodebook; + +typedef struct +{ + stbv_uint8 order; + stbv_uint16 rate; + stbv_uint16 bark_map_size; + stbv_uint8 amplitude_bits; + stbv_uint8 amplitude_offset; + stbv_uint8 number_of_books; + stbv_uint8 book_list[16]; // varies +} StbvFloor0; + +typedef struct +{ + stbv_uint8 partitions; + stbv_uint8 partition_class_list[32]; // varies + stbv_uint8 class_dimensions[16]; // varies + stbv_uint8 class_subclasses[16]; // varies + stbv_uint8 class_masterbooks[16]; // varies + stbv_int16 subclass_books[16][8]; // varies + stbv_uint16 Xlist[31*8+2]; // varies + stbv_uint8 sorted_order[31*8+2]; + stbv_uint8 stbv_neighbors[31*8+2][2]; + stbv_uint8 floor1_multiplier; + stbv_uint8 rangebits; + int values; +} StbvFloor1; + +typedef union +{ + StbvFloor0 floor0; + StbvFloor1 floor1; +} StbvFloor; + +typedef struct +{ + stbv_uint32 begin, end; + stbv_uint32 part_size; + stbv_uint8 classifications; + stbv_uint8 classbook; + stbv_uint8 **classdata; + stbv_int16 (*residue_books)[8]; +} StbvResidue; + +typedef struct +{ + stbv_uint8 magnitude; + stbv_uint8 angle; + stbv_uint8 mux; +} StbvMappingChannel; + +typedef struct +{ + stbv_uint16 coupling_steps; + StbvMappingChannel *chan; + stbv_uint8 submaps; + stbv_uint8 submap_floor[15]; // varies + stbv_uint8 submap_residue[15]; // varies +} StbvMapping; + +typedef struct +{ + stbv_uint8 blockflag; + stbv_uint8 mapping; + stbv_uint16 windowtype; + stbv_uint16 transformtype; +} StbvMode; + +typedef struct +{ + stbv_uint32 goal_crc; // expected crc if match + int bytes_left; // bytes left in packet + stbv_uint32 crc_so_far; // running crc + int bytes_done; // bytes processed in _current_ chunk + stbv_uint32 sample_loc; // granule pos encoded in page +} StbvCRCscan; + +typedef struct +{ + stbv_uint32 page_start, page_end; + stbv_uint32 last_decoded_sample; +} StbvProbedPage; + +struct stb_vorbis +{ + // user-accessible info + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int temp_memory_required; + unsigned int setup_temp_memory_required; + + // input config +#ifndef STB_VORBIS_NO_STDIO + FILE *f; + stbv_uint32 f_start; + int close_on_free; +#endif + + stbv_uint8 *stream; + stbv_uint8 *stream_start; + stbv_uint8 *stream_end; + + stbv_uint32 stream_len; + + stbv_uint8 push_mode; + + stbv_uint32 first_audio_page_offset; + + StbvProbedPage p_first, p_last; + + // memory management + stb_vorbis_alloc alloc; + int setup_offset; + int temp_offset; + + // run-time results + int eof; + enum STBVorbisError error; + + // user-useful data + + // header info + int blocksize[2]; + int blocksize_0, blocksize_1; + int codebook_count; + StbvCodebook *codebooks; + int floor_count; + stbv_uint16 floor_types[64]; // varies + StbvFloor *floor_config; + int residue_count; + stbv_uint16 residue_types[64]; // varies + StbvResidue *residue_config; + int mapping_count; + StbvMapping *mapping; + int mode_count; + StbvMode mode_config[64]; // varies + + stbv_uint32 total_samples; + + // decode buffer + float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; + float *outputs [STB_VORBIS_MAX_CHANNELS]; + + float *previous_window[STB_VORBIS_MAX_CHANNELS]; + int previous_length; + + #ifndef STB_VORBIS_NO_DEFER_FLOOR + stbv_int16 *finalY[STB_VORBIS_MAX_CHANNELS]; + #else + float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; + #endif + + stbv_uint32 current_loc; // sample location of next frame to decode + int current_loc_valid; + + // per-blocksize precomputed data + + // twiddle factors + float *A[2],*B[2],*C[2]; + float *window[2]; + stbv_uint16 *stbv_bit_reverse[2]; + + // current page/packet/segment streaming info + stbv_uint32 serial; // stream serial number for verification + int last_page; + int segment_count; + stbv_uint8 segments[255]; + stbv_uint8 page_flag; + stbv_uint8 bytes_in_seg; + stbv_uint8 first_decode; + int next_seg; + int last_seg; // flag that we're on the last segment + int last_seg_which; // what was the segment number of the last seg? + stbv_uint32 acc; + int valid_bits; + int packet_bytes; + int end_seg_with_known_loc; + stbv_uint32 known_loc_for_packet; + int discard_samples_deferred; + stbv_uint32 samples_output; + + // push mode scanning + int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching +#ifndef STB_VORBIS_NO_PUSHDATA_API + StbvCRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; +#endif + + // sample-access + int channel_buffer_start; + int channel_buffer_end; +}; + +#if defined(STB_VORBIS_NO_PUSHDATA_API) + #define STBV_IS_PUSH_MODE(f) FALSE +#elif defined(STB_VORBIS_NO_PULLDATA_API) + #define STBV_IS_PUSH_MODE(f) TRUE +#else + #define STBV_IS_PUSH_MODE(f) ((f)->push_mode) +#endif + +typedef struct stb_vorbis stbv_vorb; + +static int stbv_error(stbv_vorb *f, enum STBVorbisError e) +{ + f->error = e; + if (!f->eof && e != VORBIS_need_more_data) { + f->error=e; // breakpoint for debugging + } + return 0; +} + + +// these functions are used for allocating temporary memory +// while decoding. if you can afford the stack space, use +// alloca(); otherwise, provide a temp buffer and it will +// allocate out of those. + +#define stbv_array_size_required(count,size) (count*(sizeof(void *)+(size))) + +#define stbv_temp_alloc(f,size) (f->alloc.alloc_buffer ? stbv_setup_temp_malloc(f,size) : alloca(size)) +#define stbv_temp_free(f,p) 0 +#define stbv_temp_alloc_save(f) ((f)->temp_offset) +#define stbv_temp_alloc_restore(f,p) ((f)->temp_offset = (p)) + +#define stbv_temp_block_array(f,count,size) stbv_make_block_array(stbv_temp_alloc(f,stbv_array_size_required(count,size)), count, size) + +// given a sufficiently large block of memory, make an array of pointers to subblocks of it +static void *stbv_make_block_array(void *mem, int count, int size) +{ + int i; + void ** p = (void **) mem; + char *q = (char *) (p + count); + for (i=0; i < count; ++i) { + p[i] = q; + q += size; + } + return p; +} + +static void *stbv_setup_malloc(stbv_vorb *f, int sz) +{ + sz = (sz+3) & ~3; + f->setup_memory_required += sz; + if (f->alloc.alloc_buffer) { + void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; + if (f->setup_offset + sz > f->temp_offset) return NULL; + f->setup_offset += sz; + return p; + } + return sz ? malloc(sz) : NULL; +} + +static void stbv_setup_free(stbv_vorb *f, void *p) +{ + if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack + free(p); +} + +static void *stbv_setup_temp_malloc(stbv_vorb *f, int sz) +{ + sz = (sz+3) & ~3; + if (f->alloc.alloc_buffer) { + if (f->temp_offset - sz < f->setup_offset) return NULL; + f->temp_offset -= sz; + return (char *) f->alloc.alloc_buffer + f->temp_offset; + } + return malloc(sz); +} + +static void stbv_setup_temp_free(stbv_vorb *f, void *p, int sz) +{ + if (f->alloc.alloc_buffer) { + f->temp_offset += (sz+3)&~3; + return; + } + free(p); +} + +#define STBV_CRC32_POLY 0x04c11db7 // from spec + +static stbv_uint32 stbv_crc_table[256]; +static void stbv_crc32_init(void) +{ + int i,j; + stbv_uint32 s; + for(i=0; i < 256; i++) { + for (s=(stbv_uint32) i << 24, j=0; j < 8; ++j) + s = (s << 1) ^ (s >= (1U<<31) ? STBV_CRC32_POLY : 0); + stbv_crc_table[i] = s; + } +} + +static __forceinline stbv_uint32 stbv_crc32_update(stbv_uint32 crc, stbv_uint8 byte) +{ + return (crc << 8) ^ stbv_crc_table[byte ^ (crc >> 24)]; +} + + +// used in setup, and for huffman that doesn't go fast path +static unsigned int stbv_bit_reverse(unsigned int n) +{ + n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); + n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); + n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); + n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); + return (n >> 16) | (n << 16); +} + +static float stbv_square(float x) +{ + return x*x; +} + +// this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 +// as required by the specification. fast(?) implementation from stb.h +// @OPTIMIZE: called multiple times per-packet with "constants"; move to setup +static int stbv_ilog(stbv_int32 n) +{ + static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; + + if (n < 0) return 0; // signed n returns 0 + + // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) + if (n < (1 << 14)) + if (n < (1 << 4)) return 0 + log2_4[n ]; + else if (n < (1 << 9)) return 5 + log2_4[n >> 5]; + else return 10 + log2_4[n >> 10]; + else if (n < (1 << 24)) + if (n < (1 << 19)) return 15 + log2_4[n >> 15]; + else return 20 + log2_4[n >> 20]; + else if (n < (1 << 29)) return 25 + log2_4[n >> 25]; + else return 30 + log2_4[n >> 30]; +} + +#ifndef M_PI + #define M_PI 3.14159265358979323846264f // from CRC +#endif + +// code length assigned to a value with no huffman encoding +#define NO_CODE 255 + +/////////////////////// LEAF SETUP FUNCTIONS ////////////////////////// +// +// these functions are only called at setup, and only a few times +// per file + +static float stbv_float32_unpack(stbv_uint32 x) +{ + // from the specification + stbv_uint32 mantissa = x & 0x1fffff; + stbv_uint32 sign = x & 0x80000000; + stbv_uint32 exp = (x & 0x7fe00000) >> 21; + double res = sign ? -(double)mantissa : (double)mantissa; + return (float) ldexp((float)res, exp-788); +} + + +// zlib & jpeg huffman tables assume that the output symbols +// can either be arbitrarily arranged, or have monotonically +// increasing frequencies--they rely on the lengths being sorted; +// this makes for a very simple generation algorithm. +// vorbis allows a huffman table with non-sorted lengths. This +// requires a more sophisticated construction, since symbols in +// order do not map to huffman codes "in order". +static void stbv_add_entry(StbvCodebook *c, stbv_uint32 huff_code, int symbol, int count, int len, stbv_uint32 *values) +{ + if (!c->sparse) { + c->codewords [symbol] = huff_code; + } else { + c->codewords [count] = huff_code; + c->codeword_lengths[count] = len; + values [count] = symbol; + } +} + +static int stbv_compute_codewords(StbvCodebook *c, stbv_uint8 *len, int n, stbv_uint32 *values) +{ + int i,k,m=0; + stbv_uint32 available[32]; + + memset(available, 0, sizeof(available)); + // find the first entry + for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; + if (k == n) { assert(c->sorted_entries == 0); return TRUE; } + // add to the list + stbv_add_entry(c, 0, k, m++, len[k], values); + // add all available leaves + for (i=1; i <= len[k]; ++i) + available[i] = 1U << (32-i); + // note that the above code treats the first case specially, + // but it's really the same as the following code, so they + // could probably be combined (except the initial code is 0, + // and I use 0 in available[] to mean 'empty') + for (i=k+1; i < n; ++i) { + stbv_uint32 res; + int z = len[i], y; + if (z == NO_CODE) continue; + // find lowest available leaf (should always be earliest, + // which is what the specification calls for) + // note that this property, and the fact we can never have + // more than one free leaf at a given level, isn't totally + // trivial to prove, but it seems true and the assert never + // fires, so! + while (z > 0 && !available[z]) --z; + if (z == 0) { return FALSE; } + res = available[z]; + assert(z >= 0 && z < 32); + available[z] = 0; + stbv_add_entry(c, stbv_bit_reverse(res), i, m++, len[i], values); + // propogate availability up the tree + if (z != len[i]) { + assert(len[i] >= 0 && len[i] < 32); + for (y=len[i]; y > z; --y) { + assert(available[y] == 0); + available[y] = res + (1 << (32-y)); + } + } + } + return TRUE; +} + +// accelerated huffman table allows fast O(1) match of all symbols +// of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH +static void stbv_compute_accelerated_huffman(StbvCodebook *c) +{ + int i, len; + for (i=0; i < STBV_FAST_HUFFMAN_TABLE_SIZE; ++i) + c->fast_huffman[i] = -1; + + len = c->sparse ? c->sorted_entries : c->entries; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + if (len > 32767) len = 32767; // largest possible value we can encode! + #endif + for (i=0; i < len; ++i) { + if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { + stbv_uint32 z = c->sparse ? stbv_bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; + // set table entries for all bit combinations in the higher bits + while (z < STBV_FAST_HUFFMAN_TABLE_SIZE) { + c->fast_huffman[z] = i; + z += 1 << c->codeword_lengths[i]; + } + } + } +} + +#ifdef _MSC_VER +#define STBV_CDECL __cdecl +#else +#define STBV_CDECL +#endif + +static int STBV_CDECL stbv_uint32_compare(const void *p, const void *q) +{ + stbv_uint32 x = * (stbv_uint32 *) p; + stbv_uint32 y = * (stbv_uint32 *) q; + return x < y ? -1 : x > y; +} + +static int stbv_include_in_sort(StbvCodebook *c, stbv_uint8 len) +{ + if (c->sparse) { assert(len != NO_CODE); return TRUE; } + if (len == NO_CODE) return FALSE; + if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE; + return FALSE; +} + +// if the fast table above doesn't work, we want to binary +// search them... need to reverse the bits +static void stbv_compute_sorted_huffman(StbvCodebook *c, stbv_uint8 *lengths, stbv_uint32 *values) +{ + int i, len; + // build a list of all the entries + // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. + // this is kind of a frivolous optimization--I don't see any performance improvement, + // but it's like 4 extra lines of code, so. + if (!c->sparse) { + int k = 0; + for (i=0; i < c->entries; ++i) + if (stbv_include_in_sort(c, lengths[i])) + c->sorted_codewords[k++] = stbv_bit_reverse(c->codewords[i]); + assert(k == c->sorted_entries); + } else { + for (i=0; i < c->sorted_entries; ++i) + c->sorted_codewords[i] = stbv_bit_reverse(c->codewords[i]); + } + + qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), stbv_uint32_compare); + c->sorted_codewords[c->sorted_entries] = 0xffffffff; + + len = c->sparse ? c->sorted_entries : c->entries; + // now we need to indicate how they correspond; we could either + // #1: sort a different data structure that says who they correspond to + // #2: for each sorted entry, search the original list to find who corresponds + // #3: for each original entry, find the sorted entry + // #1 requires extra storage, #2 is slow, #3 can use binary search! + for (i=0; i < len; ++i) { + int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; + if (stbv_include_in_sort(c,huff_len)) { + stbv_uint32 code = stbv_bit_reverse(c->codewords[i]); + int x=0, n=c->sorted_entries; + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + assert(c->sorted_codewords[x] == code); + if (c->sparse) { + c->sorted_values[x] = values[i]; + c->codeword_lengths[x] = huff_len; + } else { + c->sorted_values[x] = i; + } + } + } +} + +// only run while parsing the header (3 times) +static int stbv_vorbis_validate(stbv_uint8 *data) +{ + static stbv_uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; + return memcmp(data, vorbis, 6) == 0; +} + +// called from setup only, once per code book +// (formula implied by specification) +static int stbv_lookup1_values(int entries, int dim) +{ + int r = (int) floor(exp((float) log((float) entries) / dim)); + if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; + ++r; // floor() to avoid _ftol() when non-CRT + assert(pow((float) r+1, dim) > entries); + assert((int) floor(pow((float) r, dim)) <= entries); // (int),floor() as above + return r; +} + +// called twice per file +static void stbv_compute_twiddle_factors(int n, float *A, float *B, float *C) +{ + int n4 = n >> 2, n8 = n >> 3; + int k,k2; + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f; + B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f; + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } +} + +static void stbv_compute_window(int n, float *window) +{ + int n2 = n >> 1, i; + for (i=0; i < n2; ++i) + window[i] = (float) sin(0.5 * M_PI * stbv_square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); +} + +static void stbv_compute_bitreverse(int n, stbv_uint16 *rev) +{ + int ld = stbv_ilog(n) - 1; // stbv_ilog is off-by-one from normal definitions + int i, n8 = n >> 3; + for (i=0; i < n8; ++i) + rev[i] = (stbv_bit_reverse(i) >> (32-ld+3)) << 2; +} + +static int stbv_init_blocksize(stbv_vorb *f, int b, int n) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; + f->A[b] = (float *) stbv_setup_malloc(f, sizeof(float) * n2); + f->B[b] = (float *) stbv_setup_malloc(f, sizeof(float) * n2); + f->C[b] = (float *) stbv_setup_malloc(f, sizeof(float) * n4); + if (!f->A[b] || !f->B[b] || !f->C[b]) return stbv_error(f, VORBIS_outofmem); + stbv_compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); + f->window[b] = (float *) stbv_setup_malloc(f, sizeof(float) * n2); + if (!f->window[b]) return stbv_error(f, VORBIS_outofmem); + stbv_compute_window(n, f->window[b]); + f->stbv_bit_reverse[b] = (stbv_uint16 *) stbv_setup_malloc(f, sizeof(stbv_uint16) * n8); + if (!f->stbv_bit_reverse[b]) return stbv_error(f, VORBIS_outofmem); + stbv_compute_bitreverse(n, f->stbv_bit_reverse[b]); + return TRUE; +} + +static void stbv_neighbors(stbv_uint16 *x, int n, int *plow, int *phigh) +{ + int low = -1; + int high = 65536; + int i; + for (i=0; i < n; ++i) { + if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; } + if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; } + } +} + +// this has been repurposed so y is now the original index instead of y +typedef struct +{ + stbv_uint16 x,id; +} stbv_floor_ordering; + +static int STBV_CDECL stbv_point_compare(const void *p, const void *q) +{ + stbv_floor_ordering *a = (stbv_floor_ordering *) p; + stbv_floor_ordering *b = (stbv_floor_ordering *) q; + return a->x < b->x ? -1 : a->x > b->x; +} + +// +/////////////////////// END LEAF SETUP FUNCTIONS ////////////////////////// + + +#if defined(STB_VORBIS_NO_STDIO) + #define STBV_USE_MEMORY(z) TRUE +#else + #define STBV_USE_MEMORY(z) ((z)->stream) +#endif + +static stbv_uint8 stbv_get8(stbv_vorb *z) +{ + if (STBV_USE_MEMORY(z)) { + if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } + return *z->stream++; + } + + #ifndef STB_VORBIS_NO_STDIO + { + int c = fgetc(z->f); + if (c == EOF) { z->eof = TRUE; return 0; } + return c; + } + #endif +} + +static stbv_uint32 stbv_get32(stbv_vorb *f) +{ + stbv_uint32 x; + x = stbv_get8(f); + x += stbv_get8(f) << 8; + x += stbv_get8(f) << 16; + x += (stbv_uint32) stbv_get8(f) << 24; + return x; +} + +static int stbv_getn(stbv_vorb *z, stbv_uint8 *data, int n) +{ + if (STBV_USE_MEMORY(z)) { + if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } + memcpy(data, z->stream, n); + z->stream += n; + return 1; + } + + #ifndef STB_VORBIS_NO_STDIO + if (fread(data, n, 1, z->f) == 1) + return 1; + else { + z->eof = 1; + return 0; + } + #endif +} + +static void stbv_skip(stbv_vorb *z, int n) +{ + if (STBV_USE_MEMORY(z)) { + z->stream += n; + if (z->stream >= z->stream_end) z->eof = 1; + return; + } + #ifndef STB_VORBIS_NO_STDIO + { + long x = ftell(z->f); + fseek(z->f, x+n, SEEK_SET); + } + #endif +} + +static int stbv_set_file_offset(stb_vorbis *f, unsigned int loc) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + f->eof = 0; + if (STBV_USE_MEMORY(f)) { + if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { + f->stream = f->stream_end; + f->eof = 1; + return 0; + } else { + f->stream = f->stream_start + loc; + return 1; + } + } + #ifndef STB_VORBIS_NO_STDIO + if (loc + f->f_start < loc || loc >= 0x80000000) { + loc = 0x7fffffff; + f->eof = 1; + } else { + loc += f->f_start; + } + if (!fseek(f->f, loc, SEEK_SET)) + return 1; + f->eof = 1; + fseek(f->f, f->f_start, SEEK_END); + return 0; + #endif +} + + +static stbv_uint8 stbv_ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; + +static int stbv_capture_pattern(stbv_vorb *f) +{ + if (0x4f != stbv_get8(f)) return FALSE; + if (0x67 != stbv_get8(f)) return FALSE; + if (0x67 != stbv_get8(f)) return FALSE; + if (0x53 != stbv_get8(f)) return FALSE; + return TRUE; +} + +#define STBV_PAGEFLAG_continued_packet 1 +#define STBV_PAGEFLAG_first_page 2 +#define STBV_PAGEFLAG_last_page 4 + +static int stbv_start_page_no_capturepattern(stbv_vorb *f) +{ + stbv_uint32 loc0,loc1,n; + // stream structure version + if (0 != stbv_get8(f)) return stbv_error(f, VORBIS_invalid_stream_structure_version); + // header flag + f->page_flag = stbv_get8(f); + // absolute granule position + loc0 = stbv_get32(f); + loc1 = stbv_get32(f); + // @TODO: validate loc0,loc1 as valid positions? + // stream serial number -- vorbis doesn't interleave, so discard + stbv_get32(f); + //if (f->serial != stbv_get32(f)) return stbv_error(f, VORBIS_incorrect_stream_serial_number); + // page sequence number + n = stbv_get32(f); + f->last_page = n; + // CRC32 + stbv_get32(f); + // page_segments + f->segment_count = stbv_get8(f); + if (!stbv_getn(f, f->segments, f->segment_count)) + return stbv_error(f, VORBIS_unexpected_eof); + // assume we _don't_ know any the sample position of any segments + f->end_seg_with_known_loc = -2; + if (loc0 != ~0U || loc1 != ~0U) { + int i; + // determine which packet is the last one that will complete + for (i=f->segment_count-1; i >= 0; --i) + if (f->segments[i] < 255) + break; + // 'i' is now the index of the _last_ segment of a packet that ends + if (i >= 0) { + f->end_seg_with_known_loc = i; + f->known_loc_for_packet = loc0; + } + } + if (f->first_decode) { + int i,len; + StbvProbedPage p; + len = 0; + for (i=0; i < f->segment_count; ++i) + len += f->segments[i]; + len += 27 + f->segment_count; + p.page_start = f->first_audio_page_offset; + p.page_end = p.page_start + len; + p.last_decoded_sample = loc0; + f->p_first = p; + } + f->next_seg = 0; + return TRUE; +} + +static int stbv_start_page(stbv_vorb *f) +{ + if (!stbv_capture_pattern(f)) return stbv_error(f, VORBIS_missing_capture_pattern); + return stbv_start_page_no_capturepattern(f); +} + +static int stbv_start_packet(stbv_vorb *f) +{ + while (f->next_seg == -1) { + if (!stbv_start_page(f)) return FALSE; + if (f->page_flag & STBV_PAGEFLAG_continued_packet) + return stbv_error(f, VORBIS_continued_packet_flag_invalid); + } + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + // f->next_seg is now valid + return TRUE; +} + +static int stbv_maybe_start_packet(stbv_vorb *f) +{ + if (f->next_seg == -1) { + int x = stbv_get8(f); + if (f->eof) return FALSE; // EOF at page boundary is not an error! + if (0x4f != x ) return stbv_error(f, VORBIS_missing_capture_pattern); + if (0x67 != stbv_get8(f)) return stbv_error(f, VORBIS_missing_capture_pattern); + if (0x67 != stbv_get8(f)) return stbv_error(f, VORBIS_missing_capture_pattern); + if (0x53 != stbv_get8(f)) return stbv_error(f, VORBIS_missing_capture_pattern); + if (!stbv_start_page_no_capturepattern(f)) return FALSE; + if (f->page_flag & STBV_PAGEFLAG_continued_packet) { + // set up enough state that we can read this packet if we want, + // e.g. during recovery + f->last_seg = FALSE; + f->bytes_in_seg = 0; + return stbv_error(f, VORBIS_continued_packet_flag_invalid); + } + } + return stbv_start_packet(f); +} + +static int stbv_next_segment(stbv_vorb *f) +{ + int len; + if (f->last_seg) return 0; + if (f->next_seg == -1) { + f->last_seg_which = f->segment_count-1; // in case stbv_start_page fails + if (!stbv_start_page(f)) { f->last_seg = 1; return 0; } + if (!(f->page_flag & STBV_PAGEFLAG_continued_packet)) return stbv_error(f, VORBIS_continued_packet_flag_invalid); + } + len = f->segments[f->next_seg++]; + if (len < 255) { + f->last_seg = TRUE; + f->last_seg_which = f->next_seg-1; + } + if (f->next_seg >= f->segment_count) + f->next_seg = -1; + assert(f->bytes_in_seg == 0); + f->bytes_in_seg = len; + return len; +} + +#define STBV_EOP (-1) +#define STBV_INVALID_BITS (-1) + +static int stbv_get8_packet_raw(stbv_vorb *f) +{ + if (!f->bytes_in_seg) { // CLANG! + if (f->last_seg) return STBV_EOP; + else if (!stbv_next_segment(f)) return STBV_EOP; + } + assert(f->bytes_in_seg > 0); + --f->bytes_in_seg; + ++f->packet_bytes; + return stbv_get8(f); +} + +static int stbv_get8_packet(stbv_vorb *f) +{ + int x = stbv_get8_packet_raw(f); + f->valid_bits = 0; + return x; +} + +static void stbv_flush_packet(stbv_vorb *f) +{ + while (stbv_get8_packet_raw(f) != STBV_EOP); +} + +// @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important +// as the huffman decoder? +static stbv_uint32 stbv_get_bits(stbv_vorb *f, int n) +{ + stbv_uint32 z; + + if (f->valid_bits < 0) return 0; + if (f->valid_bits < n) { + if (n > 24) { + // the accumulator technique below would not work correctly in this case + z = stbv_get_bits(f, 24); + z += stbv_get_bits(f, n-24) << 24; + return z; + } + if (f->valid_bits == 0) f->acc = 0; + while (f->valid_bits < n) { + int z = stbv_get8_packet_raw(f); + if (z == STBV_EOP) { + f->valid_bits = STBV_INVALID_BITS; + return 0; + } + f->acc += z << f->valid_bits; + f->valid_bits += 8; + } + } + if (f->valid_bits < 0) return 0; + z = f->acc & ((1 << n)-1); + f->acc >>= n; + f->valid_bits -= n; + return z; +} + +// @OPTIMIZE: primary accumulator for huffman +// expand the buffer to as many bits as possible without reading off end of packet +// it might be nice to allow f->valid_bits and f->acc to be stored in registers, +// e.g. cache them locally and decode locally +static __forceinline void stbv_prep_huffman(stbv_vorb *f) +{ + if (f->valid_bits <= 24) { + if (f->valid_bits == 0) f->acc = 0; + do { + int z; + if (f->last_seg && !f->bytes_in_seg) return; + z = stbv_get8_packet_raw(f); + if (z == STBV_EOP) return; + f->acc += (unsigned) z << f->valid_bits; + f->valid_bits += 8; + } while (f->valid_bits <= 24); + } +} + +enum +{ + STBV_VORBIS_packet_id = 1, + STBV_VORBIS_packet_comment = 3, + STBV_VORBIS_packet_setup = 5 +}; + +static int stbv_codebook_decode_scalar_raw(stbv_vorb *f, StbvCodebook *c) +{ + int i; + stbv_prep_huffman(f); + + if (c->codewords == NULL && c->sorted_codewords == NULL) + return -1; + + // cases to use binary search: sorted_codewords && !c->codewords + // sorted_codewords && c->entries > 8 + if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { + // binary search + stbv_uint32 code = stbv_bit_reverse(f->acc); + int x=0, n=c->sorted_entries, len; + + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + // x is now the sorted index + if (!c->sparse) x = c->sorted_values[x]; + // x is now sorted index if sparse, or symbol otherwise + len = c->codeword_lengths[x]; + if (f->valid_bits >= len) { + f->acc >>= len; + f->valid_bits -= len; + return x; + } + + f->valid_bits = 0; + return -1; + } + + // if small, linear search + assert(!c->sparse); + for (i=0; i < c->entries; ++i) { + if (c->codeword_lengths[i] == NO_CODE) continue; + if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) { + if (f->valid_bits >= c->codeword_lengths[i]) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + return i; + } + f->valid_bits = 0; + return -1; + } + } + + stbv_error(f, VORBIS_invalid_stream); + f->valid_bits = 0; + return -1; +} + +#ifndef STB_VORBIS_NO_INLINE_DECODE + +#define STBV_DECODE_RAW(var, f,c) \ + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ + stbv_prep_huffman(f); \ + var = f->acc & STBV_FAST_HUFFMAN_TABLE_MASK; \ + var = c->fast_huffman[var]; \ + if (var >= 0) { \ + int n = c->codeword_lengths[var]; \ + f->acc >>= n; \ + f->valid_bits -= n; \ + if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ + } else { \ + var = stbv_codebook_decode_scalar_raw(f,c); \ + } + +#else + +static int stbv_codebook_decode_scalar(stbv_vorb *f, StbvCodebook *c) +{ + int i; + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) + stbv_prep_huffman(f); + // fast huffman table lookup + i = f->acc & STBV_FAST_HUFFMAN_TABLE_MASK; + i = c->fast_huffman[i]; + if (i >= 0) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } + return i; + } + return stbv_codebook_decode_scalar_raw(f,c); +} + +#define STBV_DECODE_RAW(var,f,c) var = stbv_codebook_decode_scalar(f,c); + +#endif + +#define STBV_DECODE(var,f,c) \ + STBV_DECODE_RAW(var,f,c) \ + if (c->sparse) var = c->sorted_values[var]; + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + #define DECODE_VQ(var,f,c) STBV_DECODE_RAW(var,f,c) +#else + #define DECODE_VQ(var,f,c) STBV_DECODE(var,f,c) +#endif + + + + + + +// STBV_CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case +// where we avoid one addition +#define STBV_CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) +#define STBV_CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) +#define STBV_CODEBOOK_ELEMENT_BASE(c) (0) + +static int stbv_codebook_decode_start(stbv_vorb *f, StbvCodebook *c) +{ + int z = -1; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) + stbv_error(f, VORBIS_invalid_stream); + else { + DECODE_VQ(z,f,c); + if (c->sparse) assert(z < c->sorted_entries); + if (z < 0) { // check for STBV_EOP + if (!f->bytes_in_seg) + if (f->last_seg) + return z; + stbv_error(f, VORBIS_invalid_stream); + } + } + return z; +} + +static int stbv_codebook_decode(stbv_vorb *f, StbvCodebook *c, float *output, int len) +{ + int i,z = stbv_codebook_decode_start(f,c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + float last = STBV_CODEBOOK_ELEMENT_BASE(c); + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = STBV_CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i] += val; + if (c->sequence_p) last = val + c->minimum_value; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + if (c->sequence_p) { + float last = STBV_CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + float val = STBV_CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i] += val; + last = val + c->minimum_value; + } + } else { + float last = STBV_CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + output[i] += STBV_CODEBOOK_ELEMENT_FAST(c,z+i) + last; + } + } + + return TRUE; +} + +static int stbv_codebook_decode_step(stbv_vorb *f, StbvCodebook *c, float *output, int len, int step) +{ + int i,z = stbv_codebook_decode_start(f,c); + float last = STBV_CODEBOOK_ELEMENT_BASE(c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = STBV_CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + for (i=0; i < len; ++i) { + float val = STBV_CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + } + + return TRUE; +} + +static int stbv_codebook_decode_deinterleave_repeat(stbv_vorb *f, StbvCodebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) +{ + int c_inter = *c_inter_p; + int p_inter = *p_inter_p; + int i,z, effective = c->dimensions; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) return stbv_error(f, VORBIS_invalid_stream); + + while (total_decode > 0) { + float last = STBV_CODEBOOK_ELEMENT_BASE(c); + DECODE_VQ(z,f,c); + #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + assert(!c->sparse || z < c->sorted_entries); + #endif + if (z < 0) { + if (!f->bytes_in_seg) + if (f->last_seg) return FALSE; + return stbv_error(f, VORBIS_invalid_stream); + } + + // if this will take us off the end of the buffers, stop short! + // we check by computing the length of the virtual interleaved + // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), + // and the length we'll be using (effective) + if (c_inter + p_inter*ch + effective > len * ch) { + effective = len*ch - (p_inter*ch - c_inter); + } + + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < effective; ++i) { + int off = (z / div) % c->lookup_values; + float val = STBV_CODEBOOK_ELEMENT_FAST(c,off) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + } else + #endif + { + z *= c->dimensions; + if (c->sequence_p) { + for (i=0; i < effective; ++i) { + float val = STBV_CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + last = val; + } + } else { + for (i=0; i < effective; ++i) { + float val = STBV_CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + } + } + } + + total_decode -= effective; + } + *c_inter_p = c_inter; + *p_inter_p = p_inter; + return TRUE; +} + +static int stbv_predict_point(int x, int x0, int x1, int y0, int y1) +{ + int dy = y1 - y0; + int adx = x1 - x0; + // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? + int err = abs(dy) * (x - x0); + int off = err / adx; + return dy < 0 ? y0 - off : y0 + off; +} + +// the following table is block-copied from the specification +static float stbv_inverse_db_table[256] = +{ + 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, + 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, + 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, + 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, + 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, + 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, + 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, + 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, + 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, + 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, + 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, + 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, + 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, + 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, + 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, + 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, + 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, + 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, + 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, + 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, + 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, + 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, + 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, + 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, + 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, + 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, + 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, + 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, + 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, + 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, + 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, + 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, + 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, + 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, + 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, + 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, + 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, + 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, + 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, + 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, + 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, + 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, + 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, + 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, + 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, + 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, + 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, + 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, + 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, + 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, + 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, + 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, + 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, + 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, + 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, + 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, + 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, + 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, + 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, + 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, + 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, + 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, + 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, + 0.82788260f, 0.88168307f, 0.9389798f, 1.0f +}; + + +// @OPTIMIZE: if you want to replace this bresenham line-drawing routine, +// note that you must produce bit-identical output to decode correctly; +// this specific sequence of operations is specified in the spec (it's +// drawing integer-quantized frequency-space lines that the encoder +// expects to be exactly the same) +// ... also, isn't the whole point of Bresenham's algorithm to NOT +// have to divide in the setup? sigh. +#ifndef STB_VORBIS_NO_DEFER_FLOOR +#define STBV_LINE_OP(a,b) a *= b +#else +#define STBV_LINE_OP(a,b) a = b +#endif + +#ifdef STB_VORBIS_DIVIDE_TABLE +#define STBV_DIVTAB_NUMER 32 +#define STBV_DIVTAB_DENOM 64 +stbv_int8 stbv_integer_divide_table[STBV_DIVTAB_NUMER][STBV_DIVTAB_DENOM]; // 2KB +#endif + +static __forceinline void stbv_draw_line(float *output, int x0, int y0, int x1, int y1, int n) +{ + int dy = y1 - y0; + int adx = x1 - x0; + int ady = abs(dy); + int base; + int x=x0,y=y0; + int err = 0; + int sy; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (adx < STBV_DIVTAB_DENOM && ady < STBV_DIVTAB_NUMER) { + if (dy < 0) { + base = -stbv_integer_divide_table[ady][adx]; + sy = base-1; + } else { + base = stbv_integer_divide_table[ady][adx]; + sy = base+1; + } + } else { + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; + } +#else + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; +#endif + ady -= abs(base) * adx; + if (x1 > n) x1 = n; + if (x < x1) { + STBV_LINE_OP(output[x], stbv_inverse_db_table[y]); + for (++x; x < x1; ++x) { + err += ady; + if (err >= adx) { + err -= adx; + y += sy; + } else + y += base; + STBV_LINE_OP(output[x], stbv_inverse_db_table[y]); + } + } +} + +static int stbv_residue_decode(stbv_vorb *f, StbvCodebook *book, float *target, int offset, int n, int rtype) +{ + int k; + if (rtype == 0) { + int step = n / book->dimensions; + for (k=0; k < step; ++k) + if (!stbv_codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) + return FALSE; + } else { + for (k=0; k < n; ) { + if (!stbv_codebook_decode(f, book, target+offset, n-k)) + return FALSE; + k += book->dimensions; + offset += book->dimensions; + } + } + return TRUE; +} + +// n is 1/2 of the blocksize -- +// specification: "Correct per-vector decode length is [n]/2" +static void stbv_decode_residue(stbv_vorb *f, float *residue_buffers[], int ch, int n, int rn, stbv_uint8 *do_not_decode) +{ + int i,j,pass; + StbvResidue *r = f->residue_config + rn; + int rtype = f->residue_types[rn]; + int c = r->classbook; + int classwords = f->codebooks[c].dimensions; + unsigned int actual_size = rtype == 2 ? n*2 : n; + unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size); + unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size); + int n_read = limit_r_end - limit_r_begin; + int part_read = n_read / r->part_size; + int temp_alloc_point = stbv_temp_alloc_save(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + stbv_uint8 ***part_classdata = (stbv_uint8 ***) stbv_temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); + #else + int **classifications = (int **) stbv_temp_block_array(f,f->channels, part_read * sizeof(**classifications)); + #endif + + STBV_CHECK(f); + + for (i=0; i < ch; ++i) + if (!do_not_decode[i]) + memset(residue_buffers[i], 0, sizeof(float) * n); + + if (rtype == 2 && ch != 1) { + for (j=0; j < ch; ++j) + if (!do_not_decode[j]) + break; + if (j == ch) + goto done; + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set = 0; + if (ch == 2) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = (z & 1), p_inter = z>>1; + if (pass == 0) { + StbvCodebook *c = f->codebooks+r->classbook; + int q; + STBV_DECODE(q,f,c); + if (q == STBV_EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + StbvCodebook *book = f->codebooks + b; + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (!stbv_codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #else + // saves 1% + if (!stbv_codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #endif + } else { + z += r->part_size; + c_inter = z & 1; + p_inter = z >> 1; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else if (ch == 1) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = 0, p_inter = z; + if (pass == 0) { + StbvCodebook *c = f->codebooks+r->classbook; + int q; + STBV_DECODE(q,f,c); + if (q == STBV_EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + StbvCodebook *book = f->codebooks + b; + if (!stbv_codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + } else { + z += r->part_size; + c_inter = 0; + p_inter = z; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = z % ch, p_inter = z/ch; + if (pass == 0) { + StbvCodebook *c = f->codebooks+r->classbook; + int q; + STBV_DECODE(q,f,c); + if (q == STBV_EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + StbvCodebook *book = f->codebooks + b; + if (!stbv_codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + } else { + z += r->part_size; + c_inter = z % ch; + p_inter = z / ch; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + } + goto done; + } + STBV_CHECK(f); + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set=0; + while (pcount < part_read) { + if (pass == 0) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + StbvCodebook *c = f->codebooks+r->classbook; + int temp; + STBV_DECODE(temp,f,c); + if (temp == STBV_EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[j][class_set] = r->classdata[temp]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[j][i+pcount] = temp % r->classifications; + temp /= r->classifications; + } + #endif + } + } + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[j][class_set][i]; + #else + int c = classifications[j][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + float *target = residue_buffers[j]; + int offset = r->begin + pcount * r->part_size; + int n = r->part_size; + StbvCodebook *book = f->codebooks + b; + if (!stbv_residue_decode(f, book, target, offset, n, rtype)) + goto done; + } + } + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + done: + STBV_CHECK(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + stbv_temp_free(f,part_classdata); + #else + stbv_temp_free(f,classifications); + #endif + stbv_temp_alloc_restore(f,temp_alloc_point); +} + + +#if 0 +// slow way for debugging +void inverse_mdct_slow(float *buffer, int n) +{ + int i,j; + int n2 = n >> 1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + // formula from paper: + //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + // formula from wikipedia + //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + // these are equivalent, except the formula from the paper inverts the multiplier! + // however, what actually works is NO MULTIPLIER!?! + //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + buffer[i] = acc; + } + free(x); +} +#elif 0 +// same as above, but just barely able to run in real time on modern machines +void inverse_mdct_slow(float *buffer, int n, stbv_vorb *f, int blocktype) +{ + float mcos[16384]; + int i,j; + int n2 = n >> 1, nmask = (n << 2) -1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < 4*n; ++i) + mcos[i] = (float) cos(M_PI / 2 * i / n); + + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask]; + buffer[i] = acc; + } + free(x); +} +#elif 0 +// transform to use a slow dct-iv; this is STILL basically trivial, +// but only requires half as many ops +void dct_iv_slow(float *buffer, int n) +{ + float mcos[16384]; + float x[2048]; + int i,j; + int n2 = n >> 1, nmask = (n << 3) - 1; + memcpy(x, buffer, sizeof(*x) * n); + for (i=0; i < 8*n; ++i) + mcos[i] = (float) cos(M_PI / 4 * i / n); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n; ++j) + acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; + buffer[i] = acc; + } +} + +void inverse_mdct_slow(float *buffer, int n, stbv_vorb *f, int blocktype) +{ + int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; + float temp[4096]; + + memcpy(temp, buffer, n2 * sizeof(float)); + dct_iv_slow(temp, n2); // returns -c'-d, a-b' + + for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b' + for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' + for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d +} +#endif + +#ifndef LIBVORBIS_MDCT +#define LIBVORBIS_MDCT 0 +#endif + +#if LIBVORBIS_MDCT +// directly call the vorbis MDCT using an interface documented +// by Jeff Roberts... useful for performance comparison +typedef struct +{ + int n; + int log2n; + + float *trig; + int *bitrev; + + float scale; +} mdct_lookup; + +extern void mdct_init(mdct_lookup *lookup, int n); +extern void mdct_clear(mdct_lookup *l); +extern void mdct_backward(mdct_lookup *init, float *in, float *out); + +mdct_lookup M1,M2; + +void stbv_inverse_mdct(float *buffer, int n, stbv_vorb *f, int blocktype) +{ + mdct_lookup *M; + if (M1.n == n) M = &M1; + else if (M2.n == n) M = &M2; + else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } + else { + if (M2.n) __asm int 3; + mdct_init(&M2, n); + M = &M2; + } + + mdct_backward(M, buffer, buffer); +} +#endif + + +// the following were split out into separate functions while optimizing; +// they could be pushed back up but eh. __forceinline showed no change; +// they're probably already being inlined. +static void stbv_imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) +{ + float *ee0 = e + i_off; + float *ee2 = ee0 + k_off; + int i; + + assert((n & 3) == 0); + for (i=(n>>2); i > 0; --i) { + float k00_20, k01_21; + k00_20 = ee0[ 0] - ee2[ 0]; + k01_21 = ee0[-1] - ee2[-1]; + ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-2] - ee2[-2]; + k01_21 = ee0[-3] - ee2[-3]; + ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-4] - ee2[-4]; + k01_21 = ee0[-5] - ee2[-5]; + ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-6] - ee2[-6]; + k01_21 = ee0[-7] - ee2[-7]; + ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + ee0 -= 8; + ee2 -= 8; + } +} + +static void stbv_imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) +{ + int i; + float k00_20, k01_21; + + float *e0 = e + d0; + float *e2 = e0 + k_off; + + for (i=lim >> 2; i > 0; --i) { + k00_20 = e0[-0] - e2[-0]; + k01_21 = e0[-1] - e2[-1]; + e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0]; + e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1]; + e2[-0] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-1] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-2] - e2[-2]; + k01_21 = e0[-3] - e2[-3]; + e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2]; + e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3]; + e2[-2] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-3] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-4] - e2[-4]; + k01_21 = e0[-5] - e2[-5]; + e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4]; + e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5]; + e2[-4] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-5] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-6] - e2[-6]; + k01_21 = e0[-7] - e2[-7]; + e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6]; + e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7]; + e2[-6] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-7] = (k01_21)*A[0] + (k00_20) * A[1]; + + e0 -= 8; + e2 -= 8; + + A += k1; + } +} + +static void stbv_imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) +{ + int i; + float A0 = A[0]; + float A1 = A[0+1]; + float A2 = A[0+a_off]; + float A3 = A[0+a_off+1]; + float A4 = A[0+a_off*2+0]; + float A5 = A[0+a_off*2+1]; + float A6 = A[0+a_off*3+0]; + float A7 = A[0+a_off*3+1]; + + float k00,k11; + + float *ee0 = e +i_off; + float *ee2 = ee0+k_off; + + for (i=n; i > 0; --i) { + k00 = ee0[ 0] - ee2[ 0]; + k11 = ee0[-1] - ee2[-1]; + ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = (k00) * A0 - (k11) * A1; + ee2[-1] = (k11) * A0 + (k00) * A1; + + k00 = ee0[-2] - ee2[-2]; + k11 = ee0[-3] - ee2[-3]; + ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = (k00) * A2 - (k11) * A3; + ee2[-3] = (k11) * A2 + (k00) * A3; + + k00 = ee0[-4] - ee2[-4]; + k11 = ee0[-5] - ee2[-5]; + ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = (k00) * A4 - (k11) * A5; + ee2[-5] = (k11) * A4 + (k00) * A5; + + k00 = ee0[-6] - ee2[-6]; + k11 = ee0[-7] - ee2[-7]; + ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = (k00) * A6 - (k11) * A7; + ee2[-7] = (k11) * A6 + (k00) * A7; + + ee0 -= k0; + ee2 -= k0; + } +} + +static __forceinline void stbv_iter_54(float *z) +{ + float k00,k11,k22,k33; + float y0,y1,y2,y3; + + k00 = z[ 0] - z[-4]; + y0 = z[ 0] + z[-4]; + y2 = z[-2] + z[-6]; + k22 = z[-2] - z[-6]; + + z[-0] = y0 + y2; // z0 + z4 + z2 + z6 + z[-2] = y0 - y2; // z0 + z4 - z2 - z6 + + // done with y0,y2 + + k33 = z[-3] - z[-7]; + + z[-4] = k00 + k33; // z0 - z4 + z3 - z7 + z[-6] = k00 - k33; // z0 - z4 - z3 + z7 + + // done with k33 + + k11 = z[-1] - z[-5]; + y1 = z[-1] + z[-5]; + y3 = z[-3] + z[-7]; + + z[-1] = y1 + y3; // z1 + z5 + z3 + z7 + z[-3] = y1 - y3; // z1 + z5 - z3 - z7 + z[-5] = k11 - k22; // z1 - z5 + z2 - z6 + z[-7] = k11 + k22; // z1 - z5 - z2 + z6 +} + +static void stbv_imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) +{ + int a_off = base_n >> 3; + float A2 = A[0+a_off]; + float *z = e + i_off; + float *base = z - 16 * n; + + while (z > base) { + float k00,k11; + + k00 = z[-0] - z[-8]; + k11 = z[-1] - z[-9]; + z[-0] = z[-0] + z[-8]; + z[-1] = z[-1] + z[-9]; + z[-8] = k00; + z[-9] = k11 ; + + k00 = z[ -2] - z[-10]; + k11 = z[ -3] - z[-11]; + z[ -2] = z[ -2] + z[-10]; + z[ -3] = z[ -3] + z[-11]; + z[-10] = (k00+k11) * A2; + z[-11] = (k11-k00) * A2; + + k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation + k11 = z[ -5] - z[-13]; + z[ -4] = z[ -4] + z[-12]; + z[ -5] = z[ -5] + z[-13]; + z[-12] = k11; + z[-13] = k00; + + k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation + k11 = z[ -7] - z[-15]; + z[ -6] = z[ -6] + z[-14]; + z[ -7] = z[ -7] + z[-15]; + z[-14] = (k00+k11) * A2; + z[-15] = (k00-k11) * A2; + + stbv_iter_54(z); + stbv_iter_54(z-8); + z -= 16; + } +} + +static void stbv_inverse_mdct(float *buffer, int n, stbv_vorb *f, int blocktype) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int ld; + // @OPTIMIZE: reduce register pressure by using fewer variables? + int save_point = stbv_temp_alloc_save(f); + float *buf2 = (float *) stbv_temp_alloc(f, n2 * sizeof(*buf2)); + float *u=NULL,*v=NULL; + // twiddle factors + float *A = f->A[blocktype]; + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. + + // kernel from paper + + + // merged: + // copy and reflect spectral data + // step 0 + + // note that it turns out that the items added together during + // this step are, in fact, being added to themselves (as reflected + // by step 0). inexplicable inefficiency! this became obvious + // once I combined the passes. + + // so there's a missing 'times 2' here (for adding X to itself). + // this propogates through linearly to the end, where the numbers + // are 1/2 too small, and need to be compensated for. + + { + float *d,*e, *AA, *e_stop; + d = &buf2[n2-2]; + AA = A; + e = &buffer[0]; + e_stop = &buffer[n2]; + while (e != e_stop) { + d[1] = (e[0] * AA[0] - e[2]*AA[1]); + d[0] = (e[0] * AA[1] + e[2]*AA[0]); + d -= 2; + AA += 2; + e += 4; + } + + e = &buffer[n2-3]; + while (d >= buf2) { + d[1] = (-e[2] * AA[0] - -e[0]*AA[1]); + d[0] = (-e[2] * AA[1] + -e[0]*AA[0]); + d -= 2; + AA += 2; + e -= 4; + } + } + + // now we use symbolic names for these, so that we can + // possibly swap their meaning as we change which operations + // are in place + + u = buffer; + v = buf2; + + // step 2 (paper output is w, now u) + // this could be in place, but the data ends up in the wrong + // place... _somebody_'s got to swap it, so this is nominated + { + float *AA = &A[n2-8]; + float *d0,*d1, *e0, *e1; + + e0 = &v[n4]; + e1 = &v[0]; + + d0 = &u[n4]; + d1 = &u[0]; + + while (AA >= A) { + float v40_20, v41_21; + + v41_21 = e0[1] - e1[1]; + v40_20 = e0[0] - e1[0]; + d0[1] = e0[1] + e1[1]; + d0[0] = e0[0] + e1[0]; + d1[1] = v41_21*AA[4] - v40_20*AA[5]; + d1[0] = v40_20*AA[4] + v41_21*AA[5]; + + v41_21 = e0[3] - e1[3]; + v40_20 = e0[2] - e1[2]; + d0[3] = e0[3] + e1[3]; + d0[2] = e0[2] + e1[2]; + d1[3] = v41_21*AA[0] - v40_20*AA[1]; + d1[2] = v40_20*AA[0] + v41_21*AA[1]; + + AA -= 8; + + d0 += 4; + d1 += 4; + e0 += 4; + e1 += 4; + } + } + + // step 3 + ld = stbv_ilog(n) - 1; // stbv_ilog is off-by-one from normal definitions + + // optimized step 3: + + // the original step3 loop can be nested r inside s or s inside r; + // it's written originally as s inside r, but this is dumb when r + // iterates many times, and s few. So I have two copies of it and + // switch between them halfway. + + // this is iteration 0 of step 3 + stbv_imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); + stbv_imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); + + // this is iteration 1 of step 3 + stbv_imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); + stbv_imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); + stbv_imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); + stbv_imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); + + l=2; + for (; l < (ld-3)>>1; ++l) { + int k0 = n >> (l+2), k0_2 = k0>>1; + int lim = 1 << (l+1); + int i; + for (i=0; i < lim; ++i) + stbv_imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); + } + + for (; l < ld-6; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1; + int rlim = n >> (l+6), r; + int lim = 1 << (l+1); + int i_off; + float *A0 = A; + i_off = n2-1; + for (r=rlim; r > 0; --r) { + stbv_imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); + A0 += k1*4; + i_off -= 8; + } + } + + // iterations with count: + // ld-6,-5,-4 all interleaved together + // the big win comes from getting rid of needless flops + // due to the constants on pass 5 & 4 being all 1 and 0; + // combining them to be simultaneous to improve cache made little difference + stbv_imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); + + // output is u + + // step 4, 5, and 6 + // cannot be in-place because of step 5 + { + stbv_uint16 *bitrev = f->stbv_bit_reverse[blocktype]; + // weirdly, I'd have thought reading sequentially and writing + // erratically would have been better than vice-versa, but in + // fact that's not what my testing showed. (That is, with + // j = bitreverse(i), do you read i and write j, or read j and write i.) + + float *d0 = &v[n4-4]; + float *d1 = &v[n2-4]; + while (d0 >= v) { + int k4; + + k4 = bitrev[0]; + d1[3] = u[k4+0]; + d1[2] = u[k4+1]; + d0[3] = u[k4+2]; + d0[2] = u[k4+3]; + + k4 = bitrev[1]; + d1[1] = u[k4+0]; + d1[0] = u[k4+1]; + d0[1] = u[k4+2]; + d0[0] = u[k4+3]; + + d0 -= 4; + d1 -= 4; + bitrev += 2; + } + } + // (paper output is u, now v) + + + // data must be in buf2 + assert(v == buf2); + + // step 7 (paper output is v, now v) + // this is now in place + { + float *C = f->C[blocktype]; + float *d, *e; + + d = v; + e = v + n2 - 4; + + while (d < e) { + float a02,a11,b0,b1,b2,b3; + + a02 = d[0] - e[2]; + a11 = d[1] + e[3]; + + b0 = C[1]*a02 + C[0]*a11; + b1 = C[1]*a11 - C[0]*a02; + + b2 = d[0] + e[ 2]; + b3 = d[1] - e[ 3]; + + d[0] = b2 + b0; + d[1] = b3 + b1; + e[2] = b2 - b0; + e[3] = b1 - b3; + + a02 = d[2] - e[0]; + a11 = d[3] + e[1]; + + b0 = C[3]*a02 + C[2]*a11; + b1 = C[3]*a11 - C[2]*a02; + + b2 = d[2] + e[ 0]; + b3 = d[3] - e[ 1]; + + d[2] = b2 + b0; + d[3] = b3 + b1; + e[0] = b2 - b0; + e[1] = b1 - b3; + + C += 4; + d += 4; + e -= 4; + } + } + + // data must be in buf2 + + + // step 8+decode (paper output is X, now buffer) + // this generates pairs of data a la 8 and pushes them directly through + // the decode kernel (pushing rather than pulling) to avoid having + // to make another pass later + + // this cannot POSSIBLY be in place, so we refer to the buffers directly + + { + float *d0,*d1,*d2,*d3; + + float *B = f->B[blocktype] + n2 - 8; + float *e = buf2 + n2 - 8; + d0 = &buffer[0]; + d1 = &buffer[n2-4]; + d2 = &buffer[n2]; + d3 = &buffer[n-4]; + while (e >= v) { + float p0,p1,p2,p3; + + p3 = e[6]*B[7] - e[7]*B[6]; + p2 = -e[6]*B[6] - e[7]*B[7]; + + d0[0] = p3; + d1[3] = - p3; + d2[0] = p2; + d3[3] = p2; + + p1 = e[4]*B[5] - e[5]*B[4]; + p0 = -e[4]*B[4] - e[5]*B[5]; + + d0[1] = p1; + d1[2] = - p1; + d2[1] = p0; + d3[2] = p0; + + p3 = e[2]*B[3] - e[3]*B[2]; + p2 = -e[2]*B[2] - e[3]*B[3]; + + d0[2] = p3; + d1[1] = - p3; + d2[2] = p2; + d3[1] = p2; + + p1 = e[0]*B[1] - e[1]*B[0]; + p0 = -e[0]*B[0] - e[1]*B[1]; + + d0[3] = p1; + d1[0] = - p1; + d2[3] = p0; + d3[0] = p0; + + B -= 8; + e -= 8; + d0 += 4; + d2 += 4; + d1 -= 4; + d3 -= 4; + } + } + + stbv_temp_free(f,buf2); + stbv_temp_alloc_restore(f,save_point); +} + +#if 0 +// this is the original version of the above code, if you want to optimize it from scratch +void inverse_mdct_naive(float *buffer, int n) +{ + float s; + float A[1 << 12], B[1 << 12], C[1 << 11]; + int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int n3_4 = n - n4, ld; + // how can they claim this only uses N words?! + // oh, because they're only used sparsely, whoops + float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13]; + // set up twiddle factors + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2); + B[k2+1] = (float) sin((k2+1)*M_PI/n/2); + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // Note there are bugs in that pseudocode, presumably due to them attempting + // to rename the arrays nicely rather than representing the way their actual + // implementation bounces buffers back and forth. As a result, even in the + // "some formulars corrected" version, a direct implementation fails. These + // are noted below as "paper bug". + + // copy and reflect spectral data + for (k=0; k < n2; ++k) u[k] = buffer[k]; + for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1]; + // kernel from paper + // step 1 + for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) { + v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1]; + v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2]; + } + // step 2 + for (k=k4=0; k < n8; k+=1, k4+=4) { + w[n2+3+k4] = v[n2+3+k4] + v[k4+3]; + w[n2+1+k4] = v[n2+1+k4] + v[k4+1]; + w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4]; + w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; + } + // step 3 + ld = stbv_ilog(n) - 1; // stbv_ilog is off-by-one from normal definitions + for (l=0; l < ld-3; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3); + int rlim = n >> (l+4), r4, r; + int s2lim = 1 << (l+2), s2; + for (r=r4=0; r < rlim; r4+=4,++r) { + for (s2=0; s2 < s2lim; s2+=2) { + u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4]; + u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4]; + u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1] + - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1]; + u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1] + + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1]; + } + } + if (l+1 < ld-3) { + // paper bug: ping-ponging of u&w here is omitted + memcpy(w, u, sizeof(u)); + } + } + + // step 4 + for (i=0; i < n8; ++i) { + int j = stbv_bit_reverse(i) >> (32-ld+3); + assert(j < n8); + if (i == j) { + // paper bug: original code probably swapped in place; if copying, + // need to directly copy in this case + int i8 = i << 3; + v[i8+1] = u[i8+1]; + v[i8+3] = u[i8+3]; + v[i8+5] = u[i8+5]; + v[i8+7] = u[i8+7]; + } else if (i < j) { + int i8 = i << 3, j8 = j << 3; + v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1]; + v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3]; + v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5]; + v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7]; + } + } + // step 5 + for (k=0; k < n2; ++k) { + w[k] = v[k*2+1]; + } + // step 6 + for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) { + u[n-1-k2] = w[k4]; + u[n-2-k2] = w[k4+1]; + u[n3_4 - 1 - k2] = w[k4+2]; + u[n3_4 - 2 - k2] = w[k4+3]; + } + // step 7 + for (k=k2=0; k < n8; ++k, k2 += 2) { + v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + } + // step 8 + for (k=k2=0; k < n4; ++k,k2 += 2) { + X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1]; + X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ]; + } + + // decode kernel to output + // determined the following value experimentally + // (by first figuring out what made inverse_mdct_slow work); then matching that here + // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?) + s = 0.5; // theoretically would be n4 + + // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code, + // so it needs to use the "old" B values to behave correctly, or else + // set s to 1.0 ]]] + for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4]; + for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1]; + for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4]; +} +#endif + +static float *stbv_get_window(stbv_vorb *f, int len) +{ + len <<= 1; + if (len == f->blocksize_0) return f->window[0]; + if (len == f->blocksize_1) return f->window[1]; + assert(0); + return NULL; +} + +#ifndef STB_VORBIS_NO_DEFER_FLOOR +typedef stbv_int16 STBV_YTYPE; +#else +typedef int STBV_YTYPE; +#endif +static int stbv_do_floor(stbv_vorb *f, StbvMapping *map, int i, int n, float *target, STBV_YTYPE *finalY, stbv_uint8 *step2_flag) +{ + int n2 = n >> 1; + int s = map->chan[i].mux, floor; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return stbv_error(f, VORBIS_invalid_stream); + } else { + StbvFloor1 *g = &f->floor_config[floor].floor1; + int j,q; + int lx = 0, ly = finalY[0] * g->floor1_multiplier; + for (q=1; q < g->values; ++q) { + j = g->sorted_order[q]; + #ifndef STB_VORBIS_NO_DEFER_FLOOR + if (finalY[j] >= 0) + #else + if (step2_flag[j]) + #endif + { + int hy = finalY[j] * g->floor1_multiplier; + int hx = g->Xlist[j]; + if (lx != hx) + stbv_draw_line(target, lx,ly, hx,hy, n2); + STBV_CHECK(f); + lx = hx, ly = hy; + } + } + if (lx < n2) { + // optimization of: stbv_draw_line(target, lx,ly, n,ly, n2); + for (j=lx; j < n2; ++j) + STBV_LINE_OP(target[j], stbv_inverse_db_table[ly]); + STBV_CHECK(f); + } + } + return TRUE; +} + +// The meaning of "left" and "right" +// +// For a given frame: +// we compute samples from 0..n +// window_center is n/2 +// we'll window and mix the samples from left_start to left_end with data from the previous frame +// all of the samples from left_end to right_start can be output without mixing; however, +// this interval is 0-length except when transitioning between short and long frames +// all of the samples from right_start to right_end need to be mixed with the next frame, +// which we don't have, so those get saved in a buffer +// frame N's right_end-right_start, the number of samples to mix with the next frame, +// has to be the same as frame N+1's left_end-left_start (which they are by +// construction) + +static int stbv_vorbis_decode_initial(stbv_vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + StbvMode *m; + int i, n, prev, next, window_center; + f->channel_buffer_start = f->channel_buffer_end = 0; + + retry: + if (f->eof) return FALSE; + if (!stbv_maybe_start_packet(f)) + return FALSE; + // check packet type + if (stbv_get_bits(f,1) != 0) { + if (STBV_IS_PUSH_MODE(f)) + return stbv_error(f,VORBIS_bad_packet_type); + while (STBV_EOP != stbv_get8_packet(f)); + goto retry; + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + i = stbv_get_bits(f, stbv_ilog(f->mode_count-1)); + if (i == STBV_EOP) return FALSE; + if (i >= f->mode_count) return FALSE; + *mode = i; + m = f->mode_config + i; + if (m->blockflag) { + n = f->blocksize_1; + prev = stbv_get_bits(f,1); + next = stbv_get_bits(f,1); + } else { + prev = next = 0; + n = f->blocksize_0; + } + +// WINDOWING + + window_center = n >> 1; + if (m->blockflag && !prev) { + *p_left_start = (n - f->blocksize_0) >> 2; + *p_left_end = (n + f->blocksize_0) >> 2; + } else { + *p_left_start = 0; + *p_left_end = window_center; + } + if (m->blockflag && !next) { + *p_right_start = (n*3 - f->blocksize_0) >> 2; + *p_right_end = (n*3 + f->blocksize_0) >> 2; + } else { + *p_right_start = window_center; + *p_right_end = n; + } + + return TRUE; +} + +static int stbv_vorbis_decode_packet_rest(stbv_vorb *f, int *len, StbvMode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) +{ + StbvMapping *map; + int i,j,k,n,n2; + int zero_channel[256]; + int really_zero_channel[256]; + +// WINDOWING + + n = f->blocksize[m->blockflag]; + map = &f->mapping[m->mapping]; + +// FLOORS + n2 = n >> 1; + + STBV_CHECK(f); + + for (i=0; i < f->channels; ++i) { + int s = map->chan[i].mux, floor; + zero_channel[i] = FALSE; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return stbv_error(f, VORBIS_invalid_stream); + } else { + StbvFloor1 *g = &f->floor_config[floor].floor1; + if (stbv_get_bits(f, 1)) { + short *finalY; + stbv_uint8 step2_flag[256]; + static int range_list[4] = { 256, 128, 86, 64 }; + int range = range_list[g->floor1_multiplier-1]; + int offset = 2; + finalY = f->finalY[i]; + finalY[0] = stbv_get_bits(f, stbv_ilog(range)-1); + finalY[1] = stbv_get_bits(f, stbv_ilog(range)-1); + for (j=0; j < g->partitions; ++j) { + int pclass = g->partition_class_list[j]; + int cdim = g->class_dimensions[pclass]; + int cbits = g->class_subclasses[pclass]; + int csub = (1 << cbits)-1; + int cval = 0; + if (cbits) { + StbvCodebook *c = f->codebooks + g->class_masterbooks[pclass]; + STBV_DECODE(cval,f,c); + } + for (k=0; k < cdim; ++k) { + int book = g->subclass_books[pclass][cval & csub]; + cval = cval >> cbits; + if (book >= 0) { + int temp; + StbvCodebook *c = f->codebooks + book; + STBV_DECODE(temp,f,c); + finalY[offset++] = temp; + } else + finalY[offset++] = 0; + } + } + if (f->valid_bits == STBV_INVALID_BITS) goto error; // behavior according to spec + step2_flag[0] = step2_flag[1] = 1; + for (j=2; j < g->values; ++j) { + int low, high, pred, highroom, lowroom, room, val; + low = g->stbv_neighbors[j][0]; + high = g->stbv_neighbors[j][1]; + //stbv_neighbors(g->Xlist, j, &low, &high); + pred = stbv_predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); + val = finalY[j]; + highroom = range - pred; + lowroom = pred; + if (highroom < lowroom) + room = highroom * 2; + else + room = lowroom * 2; + if (val) { + step2_flag[low] = step2_flag[high] = 1; + step2_flag[j] = 1; + if (val >= room) + if (highroom > lowroom) + finalY[j] = val - lowroom + pred; + else + finalY[j] = pred - val + highroom - 1; + else + if (val & 1) + finalY[j] = pred - ((val+1)>>1); + else + finalY[j] = pred + (val>>1); + } else { + step2_flag[j] = 0; + finalY[j] = pred; + } + } + +#ifdef STB_VORBIS_NO_DEFER_FLOOR + stbv_do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); +#else + // defer final floor computation until _after_ residue + for (j=0; j < g->values; ++j) { + if (!step2_flag[j]) + finalY[j] = -1; + } +#endif + } else { + error: + zero_channel[i] = TRUE; + } + // So we just defer everything else to later + + // at this point we've decoded the floor into buffer + } + } + STBV_CHECK(f); + // at this point we've decoded all floors + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + // re-enable coupled channels if necessary + memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); + for (i=0; i < map->coupling_steps; ++i) + if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { + zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; + } + + STBV_CHECK(f); +// RESIDUE STBV_DECODE + for (i=0; i < map->submaps; ++i) { + float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; + int r; + stbv_uint8 do_not_decode[256]; + int ch = 0; + for (j=0; j < f->channels; ++j) { + if (map->chan[j].mux == i) { + if (zero_channel[j]) { + do_not_decode[ch] = TRUE; + residue_buffers[ch] = NULL; + } else { + do_not_decode[ch] = FALSE; + residue_buffers[ch] = f->channel_buffers[j]; + } + ++ch; + } + } + r = map->submap_residue[i]; + stbv_decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + STBV_CHECK(f); + +// INVERSE COUPLING + for (i = map->coupling_steps-1; i >= 0; --i) { + int n2 = n >> 1; + float *m = f->channel_buffers[map->chan[i].magnitude]; + float *a = f->channel_buffers[map->chan[i].angle ]; + for (j=0; j < n2; ++j) { + float a2,m2; + if (m[j] > 0) + if (a[j] > 0) + m2 = m[j], a2 = m[j] - a[j]; + else + a2 = m[j], m2 = m[j] + a[j]; + else + if (a[j] > 0) + m2 = m[j], a2 = m[j] + a[j]; + else + a2 = m[j], m2 = m[j] - a[j]; + m[j] = m2; + a[j] = a2; + } + } + STBV_CHECK(f); + + // finish decoding the floors +#ifndef STB_VORBIS_NO_DEFER_FLOOR + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + stbv_do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); + } + } +#else + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + for (j=0; j < n2; ++j) + f->channel_buffers[i][j] *= f->floor_buffers[i][j]; + } + } +#endif + +// INVERSE MDCT + STBV_CHECK(f); + for (i=0; i < f->channels; ++i) + stbv_inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); + STBV_CHECK(f); + + // this shouldn't be necessary, unless we exited on an error + // and want to flush to get to the next packet + stbv_flush_packet(f); + + if (f->first_decode) { + // assume we start so first non-discarded sample is sample 0 + // this isn't to spec, but spec would require us to read ahead + // and decode the size of all current frames--could be done, + // but presumably it's not a commonly used feature + f->current_loc = -n2; // start of first frame is positioned for discard + // we might have to discard samples "from" the next frame too, + // if we're lapping a large block then a small at the start? + f->discard_samples_deferred = n - right_end; + f->current_loc_valid = TRUE; + f->first_decode = FALSE; + } else if (f->discard_samples_deferred) { + if (f->discard_samples_deferred >= right_start - left_start) { + f->discard_samples_deferred -= (right_start - left_start); + left_start = right_start; + *p_left = left_start; + } else { + left_start += f->discard_samples_deferred; + *p_left = left_start; + f->discard_samples_deferred = 0; + } + } else if (f->previous_length == 0 && f->current_loc_valid) { + // we're recovering from a seek... that means we're going to discard + // the samples from this packet even though we know our position from + // the last page header, so we need to update the position based on + // the discarded samples here + // but wait, the code below is going to add this in itself even + // on a discard, so we don't need to do it here... + } + + // check if we have ogg information about the sample # for this packet + if (f->last_seg_which == f->end_seg_with_known_loc) { + // if we have a valid current loc, and this is final: + if (f->current_loc_valid && (f->page_flag & STBV_PAGEFLAG_last_page)) { + stbv_uint32 current_end = f->known_loc_for_packet; + // then let's infer the size of the (probably) short final frame + if (current_end < f->current_loc + (right_end-left_start)) { + if (current_end < f->current_loc) { + // negative truncation, that's impossible! + *len = 0; + } else { + *len = current_end - f->current_loc; + } + *len += left_start; // this doesn't seem right, but has no ill effect on my test files + if (*len > right_end) *len = right_end; // this should never happen + f->current_loc += *len; + return TRUE; + } + } + // otherwise, just set our sample loc + // guess that the ogg granule pos refers to the _middle_ of the + // last frame? + // set f->current_loc to the position of left_start + f->current_loc = f->known_loc_for_packet - (n2-left_start); + f->current_loc_valid = TRUE; + } + if (f->current_loc_valid) + f->current_loc += (right_start - left_start); + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + *len = right_end; // ignore samples after the window goes to 0 + STBV_CHECK(f); + + return TRUE; +} + +static int stbv_vorbis_decode_packet(stbv_vorb *f, int *len, int *p_left, int *p_right) +{ + int mode, left_end, right_end; + if (!stbv_vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; + return stbv_vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); +} + +static int stbv_vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) +{ + int prev,i,j; + // we use right&left (the start of the right- and left-window sin()-regions) + // to determine how much to return, rather than inferring from the rules + // (same result, clearer code); 'left' indicates where our sin() window + // starts, therefore where the previous window's right edge starts, and + // therefore where to start mixing from the previous buffer. 'right' + // indicates where our sin() ending-window starts, therefore that's where + // we start saving, and where our returned-data ends. + + // mixin from previous window + if (f->previous_length) { + int i,j, n = f->previous_length; + float *w = stbv_get_window(f, n); + for (i=0; i < f->channels; ++i) { + for (j=0; j < n; ++j) + f->channel_buffers[i][left+j] = + f->channel_buffers[i][left+j]*w[ j] + + f->previous_window[i][ j]*w[n-1-j]; + } + } + + prev = f->previous_length; + + // last half of this data becomes previous window + f->previous_length = len - right; + + // @OPTIMIZE: could avoid this copy by double-buffering the + // output (flipping previous_window with channel_buffers), but + // then previous_window would have to be 2x as large, and + // channel_buffers couldn't be temp mem (although they're NOT + // currently temp mem, they could be (unless we want to level + // performance by spreading out the computation)) + for (i=0; i < f->channels; ++i) + for (j=0; right+j < len; ++j) + f->previous_window[i][j] = f->channel_buffers[i][right+j]; + + if (!prev) + // there was no previous packet, so this data isn't valid... + // this isn't entirely true, only the would-have-overlapped data + // isn't valid, but this seems to be what the spec requires + return 0; + + // truncate a short frame + if (len < right) right = len; + + f->samples_output += right-left; + + return right - left; +} + +static int stbv_vorbis_pump_first_frame(stb_vorbis *f) +{ + int len, right, left, res; + res = stbv_vorbis_decode_packet(f, &len, &left, &right); + if (res) + stbv_vorbis_finish_frame(f, len, left, right); + return res; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API +static int stbv_is_whole_packet_present(stb_vorbis *f, int end_page) +{ + // make sure that we have the packet available before continuing... + // this requires a full ogg parse, but we know we can fetch from f->stream + + // instead of coding this out explicitly, we could save the current read state, + // read the next packet with stbv_get8() until end-of-packet, check f->eof, then + // reset the state? but that would be slower, esp. since we'd have over 256 bytes + // of state to restore (primarily the page segment table) + + int s = f->next_seg, first = TRUE; + stbv_uint8 *p = f->stream; + + if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag + for (; s < f->segment_count; ++s) { + p += f->segments[s]; + if (f->segments[s] < 255) // stop at first short segment + break; + } + // either this continues, or it ends it... + if (end_page) + if (s < f->segment_count-1) return stbv_error(f, VORBIS_invalid_stream); + if (s == f->segment_count) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return stbv_error(f, VORBIS_need_more_data); + first = FALSE; + } + for (; s == -1;) { + stbv_uint8 *q; + int n; + + // check that we have the page header ready + if (p + 26 >= f->stream_end) return stbv_error(f, VORBIS_need_more_data); + // validate the page + if (memcmp(p, stbv_ogg_page_header, 4)) return stbv_error(f, VORBIS_invalid_stream); + if (p[4] != 0) return stbv_error(f, VORBIS_invalid_stream); + if (first) { // the first segment must NOT have 'continued_packet', later ones MUST + if (f->previous_length) + if ((p[5] & STBV_PAGEFLAG_continued_packet)) return stbv_error(f, VORBIS_invalid_stream); + // if no previous length, we're resynching, so we can come in on a continued-packet, + // which we'll just drop + } else { + if (!(p[5] & STBV_PAGEFLAG_continued_packet)) return stbv_error(f, VORBIS_invalid_stream); + } + n = p[26]; // segment counts + q = p+27; // q points to segment table + p = q + n; // advance past header + // make sure we've read the segment table + if (p > f->stream_end) return stbv_error(f, VORBIS_need_more_data); + for (s=0; s < n; ++s) { + p += q[s]; + if (q[s] < 255) + break; + } + if (end_page) + if (s < n-1) return stbv_error(f, VORBIS_invalid_stream); + if (s == n) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return stbv_error(f, VORBIS_need_more_data); + first = FALSE; + } + return TRUE; +} +#endif // !STB_VORBIS_NO_PUSHDATA_API + +static int stbv_start_decoder(stbv_vorb *f) +{ + stbv_uint8 header[6], x,y; + int len,i,j,k, max_submaps = 0; + int longest_floorlist=0; + + // first page, first packet + + if (!stbv_start_page(f)) return FALSE; + // validate page flag + if (!(f->page_flag & STBV_PAGEFLAG_first_page)) return stbv_error(f, VORBIS_invalid_first_page); + if (f->page_flag & STBV_PAGEFLAG_last_page) return stbv_error(f, VORBIS_invalid_first_page); + if (f->page_flag & STBV_PAGEFLAG_continued_packet) return stbv_error(f, VORBIS_invalid_first_page); + // check for expected packet length + if (f->segment_count != 1) return stbv_error(f, VORBIS_invalid_first_page); + if (f->segments[0] != 30) return stbv_error(f, VORBIS_invalid_first_page); + // read packet + // check packet header + if (stbv_get8(f) != STBV_VORBIS_packet_id) return stbv_error(f, VORBIS_invalid_first_page); + if (!stbv_getn(f, header, 6)) return stbv_error(f, VORBIS_unexpected_eof); + if (!stbv_vorbis_validate(header)) return stbv_error(f, VORBIS_invalid_first_page); + // vorbis_version + if (stbv_get32(f) != 0) return stbv_error(f, VORBIS_invalid_first_page); + f->channels = stbv_get8(f); if (!f->channels) return stbv_error(f, VORBIS_invalid_first_page); + if (f->channels > STB_VORBIS_MAX_CHANNELS) return stbv_error(f, VORBIS_too_many_channels); + f->sample_rate = stbv_get32(f); if (!f->sample_rate) return stbv_error(f, VORBIS_invalid_first_page); + stbv_get32(f); // bitrate_maximum + stbv_get32(f); // bitrate_nominal + stbv_get32(f); // bitrate_minimum + x = stbv_get8(f); + { + int log0,log1; + log0 = x & 15; + log1 = x >> 4; + f->blocksize_0 = 1 << log0; + f->blocksize_1 = 1 << log1; + if (log0 < 6 || log0 > 13) return stbv_error(f, VORBIS_invalid_setup); + if (log1 < 6 || log1 > 13) return stbv_error(f, VORBIS_invalid_setup); + if (log0 > log1) return stbv_error(f, VORBIS_invalid_setup); + } + + // framing_flag + x = stbv_get8(f); + if (!(x & 1)) return stbv_error(f, VORBIS_invalid_first_page); + + // second packet! + if (!stbv_start_page(f)) return FALSE; + + if (!stbv_start_packet(f)) return FALSE; + do { + len = stbv_next_segment(f); + stbv_skip(f, len); + f->bytes_in_seg = 0; + } while (len); + + // third packet! + if (!stbv_start_packet(f)) return FALSE; + + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (STBV_IS_PUSH_MODE(f)) { + if (!stbv_is_whole_packet_present(f, TRUE)) { + // convert error in ogg header to write type + if (f->error == VORBIS_invalid_stream) + f->error = VORBIS_invalid_setup; + return FALSE; + } + } + #endif + + stbv_crc32_init(); // always init it, to avoid multithread race conditions + + if (stbv_get8_packet(f) != STBV_VORBIS_packet_setup) return stbv_error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = stbv_get8_packet(f); + if (!stbv_vorbis_validate(header)) return stbv_error(f, VORBIS_invalid_setup); + + // codebooks + + f->codebook_count = stbv_get_bits(f,8) + 1; + f->codebooks = (StbvCodebook *) stbv_setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); + if (f->codebooks == NULL) return stbv_error(f, VORBIS_outofmem); + memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); + for (i=0; i < f->codebook_count; ++i) { + stbv_uint32 *values; + int ordered, sorted_count; + int total=0; + stbv_uint8 *lengths; + StbvCodebook *c = f->codebooks+i; + STBV_CHECK(f); + x = stbv_get_bits(f, 8); if (x != 0x42) return stbv_error(f, VORBIS_invalid_setup); + x = stbv_get_bits(f, 8); if (x != 0x43) return stbv_error(f, VORBIS_invalid_setup); + x = stbv_get_bits(f, 8); if (x != 0x56) return stbv_error(f, VORBIS_invalid_setup); + x = stbv_get_bits(f, 8); + c->dimensions = (stbv_get_bits(f, 8)<<8) + x; + x = stbv_get_bits(f, 8); + y = stbv_get_bits(f, 8); + c->entries = (stbv_get_bits(f, 8)<<16) + (y<<8) + x; + ordered = stbv_get_bits(f,1); + c->sparse = ordered ? 0 : stbv_get_bits(f,1); + + if (c->dimensions == 0 && c->entries != 0) return stbv_error(f, VORBIS_invalid_setup); + + if (c->sparse) + lengths = (stbv_uint8 *) stbv_setup_temp_malloc(f, c->entries); + else + lengths = c->codeword_lengths = (stbv_uint8 *) stbv_setup_malloc(f, c->entries); + + if (!lengths) return stbv_error(f, VORBIS_outofmem); + + if (ordered) { + int current_entry = 0; + int current_length = stbv_get_bits(f,5) + 1; + while (current_entry < c->entries) { + int limit = c->entries - current_entry; + int n = stbv_get_bits(f, stbv_ilog(limit)); + if (current_entry + n > (int) c->entries) { return stbv_error(f, VORBIS_invalid_setup); } + memset(lengths + current_entry, current_length, n); + current_entry += n; + ++current_length; + } + } else { + for (j=0; j < c->entries; ++j) { + int present = c->sparse ? stbv_get_bits(f,1) : 1; + if (present) { + lengths[j] = stbv_get_bits(f, 5) + 1; + ++total; + if (lengths[j] == 32) + return stbv_error(f, VORBIS_invalid_setup); + } else { + lengths[j] = NO_CODE; + } + } + } + + if (c->sparse && total >= c->entries >> 2) { + // convert sparse items to non-sparse! + if (c->entries > (int) f->setup_temp_memory_required) + f->setup_temp_memory_required = c->entries; + + c->codeword_lengths = (stbv_uint8 *) stbv_setup_malloc(f, c->entries); + if (c->codeword_lengths == NULL) return stbv_error(f, VORBIS_outofmem); + memcpy(c->codeword_lengths, lengths, c->entries); + stbv_setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! + lengths = c->codeword_lengths; + c->sparse = 0; + } + + // compute the size of the sorted tables + if (c->sparse) { + sorted_count = total; + } else { + sorted_count = 0; + #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + for (j=0; j < c->entries; ++j) + if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) + ++sorted_count; + #endif + } + + c->sorted_entries = sorted_count; + values = NULL; + + STBV_CHECK(f); + if (!c->sparse) { + c->codewords = (stbv_uint32 *) stbv_setup_malloc(f, sizeof(c->codewords[0]) * c->entries); + if (!c->codewords) return stbv_error(f, VORBIS_outofmem); + } else { + unsigned int size; + if (c->sorted_entries) { + c->codeword_lengths = (stbv_uint8 *) stbv_setup_malloc(f, c->sorted_entries); + if (!c->codeword_lengths) return stbv_error(f, VORBIS_outofmem); + c->codewords = (stbv_uint32 *) stbv_setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); + if (!c->codewords) return stbv_error(f, VORBIS_outofmem); + values = (stbv_uint32 *) stbv_setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); + if (!values) return stbv_error(f, VORBIS_outofmem); + } + size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; + if (size > f->setup_temp_memory_required) + f->setup_temp_memory_required = size; + } + + if (!stbv_compute_codewords(c, lengths, c->entries, values)) { + if (c->sparse) stbv_setup_temp_free(f, values, 0); + return stbv_error(f, VORBIS_invalid_setup); + } + + if (c->sorted_entries) { + // allocate an extra slot for sentinels + c->sorted_codewords = (stbv_uint32 *) stbv_setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); + if (c->sorted_codewords == NULL) return stbv_error(f, VORBIS_outofmem); + // allocate an extra slot at the front so that c->sorted_values[-1] is defined + // so that we can catch that case without an extra if + c->sorted_values = ( int *) stbv_setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); + if (c->sorted_values == NULL) return stbv_error(f, VORBIS_outofmem); + ++c->sorted_values; + c->sorted_values[-1] = -1; + stbv_compute_sorted_huffman(c, lengths, values); + } + + if (c->sparse) { + stbv_setup_temp_free(f, values, sizeof(*values)*c->sorted_entries); + stbv_setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries); + stbv_setup_temp_free(f, lengths, c->entries); + c->codewords = NULL; + } + + stbv_compute_accelerated_huffman(c); + + STBV_CHECK(f); + c->lookup_type = stbv_get_bits(f, 4); + if (c->lookup_type > 2) return stbv_error(f, VORBIS_invalid_setup); + if (c->lookup_type > 0) { + stbv_uint16 *mults; + c->minimum_value = stbv_float32_unpack(stbv_get_bits(f, 32)); + c->delta_value = stbv_float32_unpack(stbv_get_bits(f, 32)); + c->value_bits = stbv_get_bits(f, 4)+1; + c->sequence_p = stbv_get_bits(f,1); + if (c->lookup_type == 1) { + c->lookup_values = stbv_lookup1_values(c->entries, c->dimensions); + } else { + c->lookup_values = c->entries * c->dimensions; + } + if (c->lookup_values == 0) return stbv_error(f, VORBIS_invalid_setup); + mults = (stbv_uint16 *) stbv_setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); + if (mults == NULL) return stbv_error(f, VORBIS_outofmem); + for (j=0; j < (int) c->lookup_values; ++j) { + int q = stbv_get_bits(f, c->value_bits); + if (q == STBV_EOP) { stbv_setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return stbv_error(f, VORBIS_invalid_setup); } + mults[j] = q; + } + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int len, sparse = c->sparse; + float last=0; + // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop + if (sparse) { + if (c->sorted_entries == 0) goto stbv_skip; + c->multiplicands = (stbv_codetype *) stbv_setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); + } else + c->multiplicands = (stbv_codetype *) stbv_setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); + if (c->multiplicands == NULL) { stbv_setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return stbv_error(f, VORBIS_outofmem); } + len = sparse ? c->sorted_entries : c->entries; + for (j=0; j < len; ++j) { + unsigned int z = sparse ? c->sorted_values[j] : j; + unsigned int div=1; + for (k=0; k < c->dimensions; ++k) { + int off = (z / div) % c->lookup_values; + float val = mults[off]; + val = mults[off]*c->delta_value + c->minimum_value + last; + c->multiplicands[j*c->dimensions + k] = val; + if (c->sequence_p) + last = val; + if (k+1 < c->dimensions) { + if (div > UINT_MAX / (unsigned int) c->lookup_values) { + stbv_setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); + return stbv_error(f, VORBIS_invalid_setup); + } + div *= c->lookup_values; + } + } + } + c->lookup_type = 2; + } + else +#endif + { + float last=0; + STBV_CHECK(f); + c->multiplicands = (stbv_codetype *) stbv_setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); + if (c->multiplicands == NULL) { stbv_setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return stbv_error(f, VORBIS_outofmem); } + for (j=0; j < (int) c->lookup_values; ++j) { + float val = mults[j] * c->delta_value + c->minimum_value + last; + c->multiplicands[j] = val; + if (c->sequence_p) + last = val; + } + } +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + stbv_skip:; +#endif + stbv_setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values); + + STBV_CHECK(f); + } + STBV_CHECK(f); + } + + // time domain transfers (notused) + + x = stbv_get_bits(f, 6) + 1; + for (i=0; i < x; ++i) { + stbv_uint32 z = stbv_get_bits(f, 16); + if (z != 0) return stbv_error(f, VORBIS_invalid_setup); + } + + // Floors + f->floor_count = stbv_get_bits(f, 6)+1; + f->floor_config = (StbvFloor *) stbv_setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); + if (f->floor_config == NULL) return stbv_error(f, VORBIS_outofmem); + for (i=0; i < f->floor_count; ++i) { + f->floor_types[i] = stbv_get_bits(f, 16); + if (f->floor_types[i] > 1) return stbv_error(f, VORBIS_invalid_setup); + if (f->floor_types[i] == 0) { + StbvFloor0 *g = &f->floor_config[i].floor0; + g->order = stbv_get_bits(f,8); + g->rate = stbv_get_bits(f,16); + g->bark_map_size = stbv_get_bits(f,16); + g->amplitude_bits = stbv_get_bits(f,6); + g->amplitude_offset = stbv_get_bits(f,8); + g->number_of_books = stbv_get_bits(f,4) + 1; + for (j=0; j < g->number_of_books; ++j) + g->book_list[j] = stbv_get_bits(f,8); + return stbv_error(f, VORBIS_feature_not_supported); + } else { + stbv_floor_ordering p[31*8+2]; + StbvFloor1 *g = &f->floor_config[i].floor1; + int max_class = -1; + g->partitions = stbv_get_bits(f, 5); + for (j=0; j < g->partitions; ++j) { + g->partition_class_list[j] = stbv_get_bits(f, 4); + if (g->partition_class_list[j] > max_class) + max_class = g->partition_class_list[j]; + } + for (j=0; j <= max_class; ++j) { + g->class_dimensions[j] = stbv_get_bits(f, 3)+1; + g->class_subclasses[j] = stbv_get_bits(f, 2); + if (g->class_subclasses[j]) { + g->class_masterbooks[j] = stbv_get_bits(f, 8); + if (g->class_masterbooks[j] >= f->codebook_count) return stbv_error(f, VORBIS_invalid_setup); + } + for (k=0; k < 1 << g->class_subclasses[j]; ++k) { + g->subclass_books[j][k] = stbv_get_bits(f,8)-1; + if (g->subclass_books[j][k] >= f->codebook_count) return stbv_error(f, VORBIS_invalid_setup); + } + } + g->floor1_multiplier = stbv_get_bits(f,2)+1; + g->rangebits = stbv_get_bits(f,4); + g->Xlist[0] = 0; + g->Xlist[1] = 1 << g->rangebits; + g->values = 2; + for (j=0; j < g->partitions; ++j) { + int c = g->partition_class_list[j]; + for (k=0; k < g->class_dimensions[c]; ++k) { + g->Xlist[g->values] = stbv_get_bits(f, g->rangebits); + ++g->values; + } + } + // precompute the sorting + for (j=0; j < g->values; ++j) { + p[j].x = g->Xlist[j]; + p[j].id = j; + } + qsort(p, g->values, sizeof(p[0]), stbv_point_compare); + for (j=0; j < g->values; ++j) + g->sorted_order[j] = (stbv_uint8) p[j].id; + // precompute the stbv_neighbors + for (j=2; j < g->values; ++j) { + int low,hi; + stbv_neighbors(g->Xlist, j, &low,&hi); + g->stbv_neighbors[j][0] = low; + g->stbv_neighbors[j][1] = hi; + } + + if (g->values > longest_floorlist) + longest_floorlist = g->values; + } + } + + // StbvResidue + f->residue_count = stbv_get_bits(f, 6)+1; + f->residue_config = (StbvResidue *) stbv_setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); + if (f->residue_config == NULL) return stbv_error(f, VORBIS_outofmem); + memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); + for (i=0; i < f->residue_count; ++i) { + stbv_uint8 residue_cascade[64]; + StbvResidue *r = f->residue_config+i; + f->residue_types[i] = stbv_get_bits(f, 16); + if (f->residue_types[i] > 2) return stbv_error(f, VORBIS_invalid_setup); + r->begin = stbv_get_bits(f, 24); + r->end = stbv_get_bits(f, 24); + if (r->end < r->begin) return stbv_error(f, VORBIS_invalid_setup); + r->part_size = stbv_get_bits(f,24)+1; + r->classifications = stbv_get_bits(f,6)+1; + r->classbook = stbv_get_bits(f,8); + if (r->classbook >= f->codebook_count) return stbv_error(f, VORBIS_invalid_setup); + for (j=0; j < r->classifications; ++j) { + stbv_uint8 high_bits=0; + stbv_uint8 low_bits=stbv_get_bits(f,3); + if (stbv_get_bits(f,1)) + high_bits = stbv_get_bits(f,5); + residue_cascade[j] = high_bits*8 + low_bits; + } + r->residue_books = (short (*)[8]) stbv_setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); + if (r->residue_books == NULL) return stbv_error(f, VORBIS_outofmem); + for (j=0; j < r->classifications; ++j) { + for (k=0; k < 8; ++k) { + if (residue_cascade[j] & (1 << k)) { + r->residue_books[j][k] = stbv_get_bits(f, 8); + if (r->residue_books[j][k] >= f->codebook_count) return stbv_error(f, VORBIS_invalid_setup); + } else { + r->residue_books[j][k] = -1; + } + } + } + // precompute the classifications[] array to avoid inner-loop mod/divide + // call it 'classdata' since we already have r->classifications + r->classdata = (stbv_uint8 **) stbv_setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + if (!r->classdata) return stbv_error(f, VORBIS_outofmem); + memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + for (j=0; j < f->codebooks[r->classbook].entries; ++j) { + int classwords = f->codebooks[r->classbook].dimensions; + int temp = j; + r->classdata[j] = (stbv_uint8 *) stbv_setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); + if (r->classdata[j] == NULL) return stbv_error(f, VORBIS_outofmem); + for (k=classwords-1; k >= 0; --k) { + r->classdata[j][k] = temp % r->classifications; + temp /= r->classifications; + } + } + } + + f->mapping_count = stbv_get_bits(f,6)+1; + f->mapping = (StbvMapping *) stbv_setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); + if (f->mapping == NULL) return stbv_error(f, VORBIS_outofmem); + memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); + for (i=0; i < f->mapping_count; ++i) { + StbvMapping *m = f->mapping + i; + int mapping_type = stbv_get_bits(f,16); + if (mapping_type != 0) return stbv_error(f, VORBIS_invalid_setup); + m->chan = (StbvMappingChannel *) stbv_setup_malloc(f, f->channels * sizeof(*m->chan)); + if (m->chan == NULL) return stbv_error(f, VORBIS_outofmem); + if (stbv_get_bits(f,1)) + m->submaps = stbv_get_bits(f,4)+1; + else + m->submaps = 1; + if (m->submaps > max_submaps) + max_submaps = m->submaps; + if (stbv_get_bits(f,1)) { + m->coupling_steps = stbv_get_bits(f,8)+1; + for (k=0; k < m->coupling_steps; ++k) { + m->chan[k].magnitude = stbv_get_bits(f, stbv_ilog(f->channels-1)); + m->chan[k].angle = stbv_get_bits(f, stbv_ilog(f->channels-1)); + if (m->chan[k].magnitude >= f->channels) return stbv_error(f, VORBIS_invalid_setup); + if (m->chan[k].angle >= f->channels) return stbv_error(f, VORBIS_invalid_setup); + if (m->chan[k].magnitude == m->chan[k].angle) return stbv_error(f, VORBIS_invalid_setup); + } + } else + m->coupling_steps = 0; + + // reserved field + if (stbv_get_bits(f,2)) return stbv_error(f, VORBIS_invalid_setup); + if (m->submaps > 1) { + for (j=0; j < f->channels; ++j) { + m->chan[j].mux = stbv_get_bits(f, 4); + if (m->chan[j].mux >= m->submaps) return stbv_error(f, VORBIS_invalid_setup); + } + } else + // @SPECIFICATION: this case is missing from the spec + for (j=0; j < f->channels; ++j) + m->chan[j].mux = 0; + + for (j=0; j < m->submaps; ++j) { + stbv_get_bits(f,8); // discard + m->submap_floor[j] = stbv_get_bits(f,8); + m->submap_residue[j] = stbv_get_bits(f,8); + if (m->submap_floor[j] >= f->floor_count) return stbv_error(f, VORBIS_invalid_setup); + if (m->submap_residue[j] >= f->residue_count) return stbv_error(f, VORBIS_invalid_setup); + } + } + + // Modes + f->mode_count = stbv_get_bits(f, 6)+1; + for (i=0; i < f->mode_count; ++i) { + StbvMode *m = f->mode_config+i; + m->blockflag = stbv_get_bits(f,1); + m->windowtype = stbv_get_bits(f,16); + m->transformtype = stbv_get_bits(f,16); + m->mapping = stbv_get_bits(f,8); + if (m->windowtype != 0) return stbv_error(f, VORBIS_invalid_setup); + if (m->transformtype != 0) return stbv_error(f, VORBIS_invalid_setup); + if (m->mapping >= f->mapping_count) return stbv_error(f, VORBIS_invalid_setup); + } + + stbv_flush_packet(f); + + f->previous_length = 0; + + for (i=0; i < f->channels; ++i) { + f->channel_buffers[i] = (float *) stbv_setup_malloc(f, sizeof(float) * f->blocksize_1); + f->previous_window[i] = (float *) stbv_setup_malloc(f, sizeof(float) * f->blocksize_1/2); + f->finalY[i] = (stbv_int16 *) stbv_setup_malloc(f, sizeof(stbv_int16) * longest_floorlist); + if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return stbv_error(f, VORBIS_outofmem); + memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + f->floor_buffers[i] = (float *) stbv_setup_malloc(f, sizeof(float) * f->blocksize_1/2); + if (f->floor_buffers[i] == NULL) return stbv_error(f, VORBIS_outofmem); + #endif + } + + if (!stbv_init_blocksize(f, 0, f->blocksize_0)) return FALSE; + if (!stbv_init_blocksize(f, 1, f->blocksize_1)) return FALSE; + f->blocksize[0] = f->blocksize_0; + f->blocksize[1] = f->blocksize_1; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (stbv_integer_divide_table[1][1]==0) + for (i=0; i < STBV_DIVTAB_NUMER; ++i) + for (j=1; j < STBV_DIVTAB_DENOM; ++j) + stbv_integer_divide_table[i][j] = i / j; +#endif + + // compute how much temporary memory is needed + + // 1. + { + stbv_uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); + stbv_uint32 classify_mem; + int i,max_part_read=0; + for (i=0; i < f->residue_count; ++i) { + StbvResidue *r = f->residue_config + i; + unsigned int actual_size = f->blocksize_1 / 2; + unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size; + unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; + int n_read = limit_r_end - limit_r_begin; + int part_read = n_read / r->part_size; + if (part_read > max_part_read) + max_part_read = part_read; + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(stbv_uint8 *)); + #else + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); + #endif + + // maximum reasonable partition size is f->blocksize_1 + + f->temp_memory_required = classify_mem; + if (imdct_mem > f->temp_memory_required) + f->temp_memory_required = imdct_mem; + } + + f->first_decode = TRUE; + + if (f->alloc.alloc_buffer) { + assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); + // check if there's enough temp memory so we don't error later + if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) + return stbv_error(f, VORBIS_outofmem); + } + + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + + return TRUE; +} + +static void stbv_vorbis_deinit(stb_vorbis *p) +{ + int i,j; + if (p->residue_config) { + for (i=0; i < p->residue_count; ++i) { + StbvResidue *r = p->residue_config+i; + if (r->classdata) { + for (j=0; j < p->codebooks[r->classbook].entries; ++j) + stbv_setup_free(p, r->classdata[j]); + stbv_setup_free(p, r->classdata); + } + stbv_setup_free(p, r->residue_books); + } + } + + if (p->codebooks) { + STBV_CHECK(p); + for (i=0; i < p->codebook_count; ++i) { + StbvCodebook *c = p->codebooks + i; + stbv_setup_free(p, c->codeword_lengths); + stbv_setup_free(p, c->multiplicands); + stbv_setup_free(p, c->codewords); + stbv_setup_free(p, c->sorted_codewords); + // c->sorted_values[-1] is the first entry in the array + stbv_setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); + } + stbv_setup_free(p, p->codebooks); + } + stbv_setup_free(p, p->floor_config); + stbv_setup_free(p, p->residue_config); + if (p->mapping) { + for (i=0; i < p->mapping_count; ++i) + stbv_setup_free(p, p->mapping[i].chan); + stbv_setup_free(p, p->mapping); + } + STBV_CHECK(p); + for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) { + stbv_setup_free(p, p->channel_buffers[i]); + stbv_setup_free(p, p->previous_window[i]); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + stbv_setup_free(p, p->floor_buffers[i]); + #endif + stbv_setup_free(p, p->finalY[i]); + } + for (i=0; i < 2; ++i) { + stbv_setup_free(p, p->A[i]); + stbv_setup_free(p, p->B[i]); + stbv_setup_free(p, p->C[i]); + stbv_setup_free(p, p->window[i]); + stbv_setup_free(p, p->stbv_bit_reverse[i]); + } + #ifndef STB_VORBIS_NO_STDIO + if (p->close_on_free) fclose(p->f); + #endif +} + +STBVDEF void stb_vorbis_close(stb_vorbis *p) +{ + if (p == NULL) return; + stbv_vorbis_deinit(p); + stbv_setup_free(p,p); +} + +static void stbv_vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) +{ + memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start + if (z) { + p->alloc = *z; + p->alloc.alloc_buffer_length_in_bytes = (p->alloc.alloc_buffer_length_in_bytes+3) & ~3; + p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; + } + p->eof = 0; + p->error = VORBIS__no_error; + p->stream = NULL; + p->codebooks = NULL; + p->page_crc_tests = -1; + #ifndef STB_VORBIS_NO_STDIO + p->close_on_free = FALSE; + p->f = NULL; + #endif +} + +STBVDEF int stb_vorbis_get_sample_offset(stb_vorbis *f) +{ + if (f->current_loc_valid) + return f->current_loc; + else + return -1; +} + +STBVDEF stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) +{ + stb_vorbis_info d; + d.channels = f->channels; + d.sample_rate = f->sample_rate; + d.setup_memory_required = f->setup_memory_required; + d.setup_temp_memory_required = f->setup_temp_memory_required; + d.temp_memory_required = f->temp_memory_required; + d.max_frame_size = f->blocksize_1 >> 1; + return d; +} + +STBVDEF int stb_vorbis_get_error(stb_vorbis *f) +{ + int e = f->error; + f->error = VORBIS__no_error; + return e; +} + +static stb_vorbis * stbv_vorbis_alloc(stb_vorbis *f) +{ + stb_vorbis *p = (stb_vorbis *) stbv_setup_malloc(f, sizeof(*p)); + return p; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +STBVDEF void stb_vorbis_flush_pushdata(stb_vorbis *f) +{ + f->previous_length = 0; + f->page_crc_tests = 0; + f->discard_samples_deferred = 0; + f->current_loc_valid = FALSE; + f->first_decode = FALSE; + f->samples_output = 0; + f->channel_buffer_start = 0; + f->channel_buffer_end = 0; +} + +static int stbv_vorbis_search_for_page_pushdata(stbv_vorb *f, stbv_uint8 *data, int data_len) +{ + int i,n; + for (i=0; i < f->page_crc_tests; ++i) + f->scan[i].bytes_done = 0; + + // if we have room for more scans, search for them first, because + // they may cause us to stop early if their header is incomplete + if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { + if (data_len < 4) return 0; + data_len -= 3; // need to look for 4-byte sequence, so don't miss + // one that straddles a boundary + for (i=0; i < data_len; ++i) { + if (data[i] == 0x4f) { + if (0==memcmp(data+i, stbv_ogg_page_header, 4)) { + int j,len; + stbv_uint32 crc; + // make sure we have the whole page header + if (i+26 >= data_len || i+27+data[i+26] >= data_len) { + // only read up to this page start, so hopefully we'll + // have the whole page header start next time + data_len = i; + break; + } + // ok, we have it all; compute the length of the page + len = 27 + data[i+26]; + for (j=0; j < data[i+26]; ++j) + len += data[i+27+j]; + // scan everything up to the embedded crc (which we must 0) + crc = 0; + for (j=0; j < 22; ++j) + crc = stbv_crc32_update(crc, data[i+j]); + // now process 4 0-bytes + for ( ; j < 26; ++j) + crc = stbv_crc32_update(crc, 0); + // len is the total number of bytes we need to scan + n = f->page_crc_tests++; + f->scan[n].bytes_left = len-j; + f->scan[n].crc_so_far = crc; + f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24); + // if the last frame on a page is continued to the next, then + // we can't recover the sample_loc immediately + if (data[i+27+data[i+26]-1] == 255) + f->scan[n].sample_loc = ~0; + else + f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24); + f->scan[n].bytes_done = i+j; + if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) + break; + // keep going if we still have room for more + } + } + } + } + + for (i=0; i < f->page_crc_tests;) { + stbv_uint32 crc; + int j; + int n = f->scan[i].bytes_done; + int m = f->scan[i].bytes_left; + if (m > data_len - n) m = data_len - n; + // m is the bytes to scan in the current chunk + crc = f->scan[i].crc_so_far; + for (j=0; j < m; ++j) + crc = stbv_crc32_update(crc, data[n+j]); + f->scan[i].bytes_left -= m; + f->scan[i].crc_so_far = crc; + if (f->scan[i].bytes_left == 0) { + // does it match? + if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { + // Houston, we have page + data_len = n+m; // consumption amount is wherever that scan ended + f->page_crc_tests = -1; // drop out of page scan mode + f->previous_length = 0; // decode-but-don't-output one frame + f->next_seg = -1; // start a new page + f->current_loc = f->scan[i].sample_loc; // set the current sample location + // to the amount we'd have decoded had we decoded this page + f->current_loc_valid = f->current_loc != ~0U; + return data_len; + } + // delete entry + f->scan[i] = f->scan[--f->page_crc_tests]; + } else { + ++i; + } + } + + return data_len; +} + +// return value: number of bytes we used +STBVDEF int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, // the file we're decoding + const stbv_uint8 *data, int data_len, // the memory available for decoding + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ) +{ + int i; + int len,right,left; + + if (!STBV_IS_PUSH_MODE(f)) return stbv_error(f, VORBIS_invalid_api_mixing); + + if (f->page_crc_tests >= 0) { + *samples = 0; + return stbv_vorbis_search_for_page_pushdata(f, (stbv_uint8 *) data, data_len); + } + + f->stream = (stbv_uint8 *) data; + f->stream_end = (stbv_uint8 *) data + data_len; + f->error = VORBIS__no_error; + + // check that we have the entire packet in memory + if (!stbv_is_whole_packet_present(f, FALSE)) { + *samples = 0; + return 0; + } + + if (!stbv_vorbis_decode_packet(f, &len, &left, &right)) { + // save the actual error we encountered + enum STBVorbisError error = f->error; + if (error == VORBIS_bad_packet_type) { + // flush and resynch + f->error = VORBIS__no_error; + while (stbv_get8_packet(f) != STBV_EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + if (error == VORBIS_continued_packet_flag_invalid) { + if (f->previous_length == 0) { + // we may be resynching, in which case it's ok to hit one + // of these; just discard the packet + f->error = VORBIS__no_error; + while (stbv_get8_packet(f) != STBV_EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + } + // if we get an error while parsing, what to do? + // well, it DEFINITELY won't work to continue from where we are! + stb_vorbis_flush_pushdata(f); + // restore the error that actually made us bail + f->error = error; + *samples = 0; + return 1; + } + + // success! + len = stbv_vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + if (channels) *channels = f->channels; + *samples = len; + *output = f->outputs; + return (int) (f->stream - data); +} + +STBVDEF stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char *data, int data_len, // the memory available for decoding + int *data_used, // only defined if result is not NULL + int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + stbv_vorbis_init(&p, alloc); + p.stream = (stbv_uint8 *) data; + p.stream_end = (stbv_uint8 *) data + data_len; + p.push_mode = TRUE; + if (!stbv_start_decoder(&p)) { + if (p.eof) + *error = VORBIS_need_more_data; + else + *error = p.error; + return NULL; + } + f = stbv_vorbis_alloc(&p); + if (f) { + *f = p; + *data_used = (int) (f->stream - data); + *error = 0; + return f; + } else { + stbv_vorbis_deinit(&p); + return NULL; + } +} +#endif // STB_VORBIS_NO_PUSHDATA_API + +STBVDEF unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + if (STBV_USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start); + #ifndef STB_VORBIS_NO_STDIO + return (unsigned int) (ftell(f->f) - f->f_start); + #endif +} + +#ifndef STB_VORBIS_NO_PULLDATA_API +// +// DATA-PULLING API +// + +static stbv_uint32 stbv_vorbis_find_page(stb_vorbis *f, stbv_uint32 *end, stbv_uint32 *last) +{ + for(;;) { + int n; + if (f->eof) return 0; + n = stbv_get8(f); + if (n == 0x4f) { // page header candidate + unsigned int retry_loc = stb_vorbis_get_file_offset(f); + int i; + // check if we're off the end of a file_section stream + if (retry_loc - 25 > f->stream_len) + return 0; + // check the rest of the header + for (i=1; i < 4; ++i) + if (stbv_get8(f) != stbv_ogg_page_header[i]) + break; + if (f->eof) return 0; + if (i == 4) { + stbv_uint8 header[27]; + stbv_uint32 i, crc, goal, len; + for (i=0; i < 4; ++i) + header[i] = stbv_ogg_page_header[i]; + for (; i < 27; ++i) + header[i] = stbv_get8(f); + if (f->eof) return 0; + if (header[4] != 0) goto invalid; + goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24); + for (i=22; i < 26; ++i) + header[i] = 0; + crc = 0; + for (i=0; i < 27; ++i) + crc = stbv_crc32_update(crc, header[i]); + len = 0; + for (i=0; i < header[26]; ++i) { + int s = stbv_get8(f); + crc = stbv_crc32_update(crc, s); + len += s; + } + if (len && f->eof) return 0; + for (i=0; i < len; ++i) + crc = stbv_crc32_update(crc, stbv_get8(f)); + // finished parsing probable page + if (crc == goal) { + // we could now check that it's either got the last + // page flag set, OR it's followed by the capture + // pattern, but I guess TECHNICALLY you could have + // a file with garbage between each ogg page and recover + // from it automatically? So even though that paranoia + // might decrease the chance of an invalid decode by + // another 2^32, not worth it since it would hose those + // invalid-but-useful files? + if (end) + *end = stb_vorbis_get_file_offset(f); + if (last) { + if (header[5] & 0x04) + *last = 1; + else + *last = 0; + } + stbv_set_file_offset(f, retry_loc-1); + return 1; + } + } + invalid: + // not a valid page, so rewind and look for next one + stbv_set_file_offset(f, retry_loc); + } + } +} + + +#define STBV_SAMPLE_unknown 0xffffffff + +// seeking is implemented with a binary search, which narrows down the range to +// 64K, before using a linear search (because finding the synchronization +// pattern can be expensive, and the chance we'd find the end page again is +// relatively high for small ranges) +// +// two initial interpolation-style probes are used at the start of the search +// to try to bound either side of the binary search sensibly, while still +// working in O(log n) time if they fail. + +static int stbv_get_seek_page_info(stb_vorbis *f, StbvProbedPage *z) +{ + stbv_uint8 header[27], lacing[255]; + int i,len; + + // record where the page starts + z->page_start = stb_vorbis_get_file_offset(f); + + // parse the header + stbv_getn(f, header, 27); + if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') + return 0; + stbv_getn(f, lacing, header[26]); + + // determine the length of the payload + len = 0; + for (i=0; i < header[26]; ++i) + len += lacing[i]; + + // this implies where the page ends + z->page_end = z->page_start + 27 + header[26] + len; + + // read the last-decoded sample out of the data + z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); + + // restore file state to where we were + stbv_set_file_offset(f, z->page_start); + return 1; +} + +// rarely used function to seek back to the preceeding page while finding the +// start of a packet +static int stbv_go_to_page_before(stb_vorbis *f, unsigned int limit_offset) +{ + unsigned int previous_safe, end; + + // now we want to seek back 64K from the limit + if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset) + previous_safe = limit_offset - 65536; + else + previous_safe = f->first_audio_page_offset; + + stbv_set_file_offset(f, previous_safe); + + while (stbv_vorbis_find_page(f, &end, NULL)) { + if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) + return 1; + stbv_set_file_offset(f, end); + } + + return 0; +} + +// implements the search logic for finding a page and starting decoding. if +// the function succeeds, current_loc_valid will be true and current_loc will +// be less than or equal to the provided sample number (the closer the +// better). +static int stbv_seek_to_sample_coarse(stb_vorbis *f, stbv_uint32 sample_number) +{ + StbvProbedPage left, right, mid; + int i, start_seg_with_known_loc, end_pos, page_start; + stbv_uint32 delta, stream_length, padding; + double offset, bytes_per_sample; + int probe = 0; + + // find the last page and validate the target sample + stream_length = stb_vorbis_stream_length_in_samples(f); + if (stream_length == 0) return stbv_error(f, VORBIS_seek_without_length); + if (sample_number > stream_length) return stbv_error(f, VORBIS_seek_invalid); + + // this is the maximum difference between the window-center (which is the + // actual granule position value), and the right-start (which the spec + // indicates should be the granule position (give or take one)). + padding = ((f->blocksize_1 - f->blocksize_0) >> 2); + if (sample_number < padding) + sample_number = 0; + else + sample_number -= padding; + + left = f->p_first; + while (left.last_decoded_sample == ~0U) { + // (untested) the first page does not have a 'last_decoded_sample' + stbv_set_file_offset(f, left.page_end); + if (!stbv_get_seek_page_info(f, &left)) goto error; + } + + right = f->p_last; + assert(right.last_decoded_sample != ~0U); + + // starting from the start is handled differently + if (sample_number <= left.last_decoded_sample) { + if (stb_vorbis_seek_start(f)) + return 1; + return 0; + } + + while (left.page_end != right.page_start) { + assert(left.page_end < right.page_start); + // search range in bytes + delta = right.page_start - left.page_end; + if (delta <= 65536) { + // there's only 64K left to search - handle it linearly + stbv_set_file_offset(f, left.page_end); + } else { + if (probe < 2) { + if (probe == 0) { + // first probe (interpolate) + double data_bytes = right.page_end - left.page_start; + bytes_per_sample = data_bytes / right.last_decoded_sample; + offset = left.page_start + bytes_per_sample * (sample_number - left.last_decoded_sample); + } else { + // second probe (try to bound the other side) + double error = ((double) sample_number - mid.last_decoded_sample) * bytes_per_sample; + if (error >= 0 && error < 8000) error = 8000; + if (error < 0 && error > -8000) error = -8000; + offset += error * 2; + } + + // ensure the offset is valid + if (offset < left.page_end) + offset = left.page_end; + if (offset > right.page_start - 65536) + offset = right.page_start - 65536; + + stbv_set_file_offset(f, (unsigned int) offset); + } else { + // binary search for large ranges (offset by 32K to ensure + // we don't hit the right page) + stbv_set_file_offset(f, left.page_end + (delta / 2) - 32768); + } + + if (!stbv_vorbis_find_page(f, NULL, NULL)) goto error; + } + + for (;;) { + if (!stbv_get_seek_page_info(f, &mid)) goto error; + if (mid.last_decoded_sample != ~0U) break; + // (untested) no frames end on this page + stbv_set_file_offset(f, mid.page_end); + assert(mid.page_start < right.page_start); + } + + // if we've just found the last page again then we're in a tricky file, + // and we're close enough. + if (mid.page_start == right.page_start) + break; + + if (sample_number < mid.last_decoded_sample) + right = mid; + else + left = mid; + + ++probe; + } + + // seek back to start of the last packet + page_start = left.page_start; + stbv_set_file_offset(f, page_start); + if (!stbv_start_page(f)) return stbv_error(f, VORBIS_seek_failed); + end_pos = f->end_seg_with_known_loc; + assert(end_pos >= 0); + + for (;;) { + for (i = end_pos; i > 0; --i) + if (f->segments[i-1] != 255) + break; + + start_seg_with_known_loc = i; + + if (start_seg_with_known_loc > 0 || !(f->page_flag & STBV_PAGEFLAG_continued_packet)) + break; + + // (untested) the final packet begins on an earlier page + if (!stbv_go_to_page_before(f, page_start)) + goto error; + + page_start = stb_vorbis_get_file_offset(f); + if (!stbv_start_page(f)) goto error; + end_pos = f->segment_count - 1; + } + + // prepare to start decoding + f->current_loc_valid = FALSE; + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + f->previous_length = 0; + f->next_seg = start_seg_with_known_loc; + + for (i = 0; i < start_seg_with_known_loc; i++) + stbv_skip(f, f->segments[i]); + + // start decoding (optimizable - this frame is generally discarded) + if (!stbv_vorbis_pump_first_frame(f)) + return 0; + if (f->current_loc > sample_number) + return stbv_error(f, VORBIS_seek_failed); + return 1; + +error: + // try to restore the file to a valid state + stb_vorbis_seek_start(f); + return stbv_error(f, VORBIS_seek_failed); +} + +// the same as stbv_vorbis_decode_initial, but without advancing +static int stbv_peek_decode_initial(stbv_vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + int bits_read, bytes_read; + + if (!stbv_vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) + return 0; + + // either 1 or 2 bytes were read, figure out which so we can rewind + bits_read = 1 + stbv_ilog(f->mode_count-1); + if (f->mode_config[*mode].blockflag) + bits_read += 2; + bytes_read = (bits_read + 7) / 8; + + f->bytes_in_seg += bytes_read; + f->packet_bytes -= bytes_read; + stbv_skip(f, -bytes_read); + if (f->next_seg == -1) + f->next_seg = f->segment_count - 1; + else + f->next_seg--; + f->valid_bits = 0; + + return 1; +} + +STBVDEF int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) +{ + stbv_uint32 max_frame_samples; + + if (STBV_IS_PUSH_MODE(f)) return stbv_error(f, VORBIS_invalid_api_mixing); + + // fast page-level search + if (!stbv_seek_to_sample_coarse(f, sample_number)) + return 0; + + assert(f->current_loc_valid); + assert(f->current_loc <= sample_number); + + // linear search for the relevant packet + max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2; + while (f->current_loc < sample_number) { + int left_start, left_end, right_start, right_end, mode, frame_samples; + if (!stbv_peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) + return stbv_error(f, VORBIS_seek_failed); + // calculate the number of samples returned by the next frame + frame_samples = right_start - left_start; + if (f->current_loc + frame_samples > sample_number) { + return 1; // the next frame will contain the sample + } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) { + // there's a chance the frame after this could contain the sample + stbv_vorbis_pump_first_frame(f); + } else { + // this frame is too early to be relevant + f->current_loc += frame_samples; + f->previous_length = 0; + stbv_maybe_start_packet(f); + stbv_flush_packet(f); + } + } + // the next frame will start with the sample + assert(f->current_loc == sample_number); + return 1; +} + +STBVDEF int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) +{ + if (!stb_vorbis_seek_frame(f, sample_number)) + return 0; + + if (sample_number != f->current_loc) { + int n; + stbv_uint32 frame_start = f->current_loc; + stb_vorbis_get_frame_float(f, &n, NULL); + assert(sample_number > frame_start); + assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end); + f->channel_buffer_start += (sample_number - frame_start); + } + + return 1; +} + +STBVDEF int stb_vorbis_seek_start(stb_vorbis *f) +{ + if (STBV_IS_PUSH_MODE(f)) { return stbv_error(f, VORBIS_invalid_api_mixing); } + stbv_set_file_offset(f, f->first_audio_page_offset); + f->previous_length = 0; + f->first_decode = TRUE; + f->next_seg = -1; + return stbv_vorbis_pump_first_frame(f); +} + +STBVDEF unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) +{ + unsigned int restore_offset, previous_safe; + unsigned int end, last_page_loc; + + if (STBV_IS_PUSH_MODE(f)) return stbv_error(f, VORBIS_invalid_api_mixing); + if (!f->total_samples) { + unsigned int last; + stbv_uint32 lo,hi; + char header[6]; + + // first, store the current decode position so we can restore it + restore_offset = stb_vorbis_get_file_offset(f); + + // now we want to seek back 64K from the end (the last page must + // be at most a little less than 64K, but let's allow a little slop) + if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset) + previous_safe = f->stream_len - 65536; + else + previous_safe = f->first_audio_page_offset; + + stbv_set_file_offset(f, previous_safe); + // previous_safe is now our candidate 'earliest known place that seeking + // to will lead to the final page' + + if (!stbv_vorbis_find_page(f, &end, &last)) { + // if we can't find a page, we're hosed! + f->error = VORBIS_cant_find_last_page; + f->total_samples = 0xffffffff; + goto done; + } + + // check if there are more pages + last_page_loc = stb_vorbis_get_file_offset(f); + + // stop when the last_page flag is set, not when we reach eof; + // this allows us to stop short of a 'file_section' end without + // explicitly checking the length of the section + while (!last) { + stbv_set_file_offset(f, end); + if (!stbv_vorbis_find_page(f, &end, &last)) { + // the last page we found didn't have the 'last page' flag + // set. whoops! + break; + } + previous_safe = last_page_loc+1; + last_page_loc = stb_vorbis_get_file_offset(f); + } + + stbv_set_file_offset(f, last_page_loc); + + // parse the header + stbv_getn(f, (unsigned char *)header, 6); + // extract the absolute granule position + lo = stbv_get32(f); + hi = stbv_get32(f); + if (lo == 0xffffffff && hi == 0xffffffff) { + f->error = VORBIS_cant_find_last_page; + f->total_samples = STBV_SAMPLE_unknown; + goto done; + } + if (hi) + lo = 0xfffffffe; // saturate + f->total_samples = lo; + + f->p_last.page_start = last_page_loc; + f->p_last.page_end = end; + f->p_last.last_decoded_sample = lo; + + done: + stbv_set_file_offset(f, restore_offset); + } + return f->total_samples == STBV_SAMPLE_unknown ? 0 : f->total_samples; +} + +STBVDEF float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) +{ + return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; +} + + + +STBVDEF int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) +{ + int len, right,left,i; + if (STBV_IS_PUSH_MODE(f)) return stbv_error(f, VORBIS_invalid_api_mixing); + + if (!stbv_vorbis_decode_packet(f, &len, &left, &right)) { + f->channel_buffer_start = f->channel_buffer_end = 0; + return 0; + } + + len = stbv_vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + f->channel_buffer_start = left; + f->channel_buffer_end = left+len; + + if (channels) *channels = f->channels; + if (output) *output = f->outputs; + return len; +} + +#ifndef STB_VORBIS_NO_STDIO + +STBVDEF stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) +{ + stb_vorbis *f, p; + stbv_vorbis_init(&p, alloc); + p.f = file; + p.f_start = (stbv_uint32) ftell(file); + p.stream_len = length; + p.close_on_free = close_on_free; + if (stbv_start_decoder(&p)) { + f = stbv_vorbis_alloc(&p); + if (f) { + *f = p; + stbv_vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + stbv_vorbis_deinit(&p); + return NULL; +} + +STBVDEF stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) +{ + unsigned int len, start; + start = (unsigned int) ftell(file); + fseek(file, 0, SEEK_END); + len = (unsigned int) (ftell(file) - start); + fseek(file, start, SEEK_SET); + return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); +} + +STBVDEF stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) +{ + FILE *f = fopen(filename, "rb"); + if (f) + return stb_vorbis_open_file(f, TRUE, error, alloc); + if (error) *error = VORBIS_file_open_failure; + return NULL; +} +#endif // STB_VORBIS_NO_STDIO + +STBVDEF stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + if (data == NULL) return NULL; + stbv_vorbis_init(&p, alloc); + p.stream = (stbv_uint8 *) data; + p.stream_end = (stbv_uint8 *) data + len; + p.stream_start = (stbv_uint8 *) p.stream; + p.stream_len = len; + p.push_mode = FALSE; + if (stbv_start_decoder(&p)) { + f = stbv_vorbis_alloc(&p); + if (f) { + *f = p; + stbv_vorbis_pump_first_frame(f); + if (error) *error = VORBIS__no_error; + return f; + } + } + if (error) *error = p.error; + stbv_vorbis_deinit(&p); + return NULL; +} + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#define STBV_PLAYBACK_MONO 1 +#define STBV_PLAYBACK_LEFT 2 +#define STBV_PLAYBACK_RIGHT 4 + +#define STBV_L (STBV_PLAYBACK_LEFT | STBV_PLAYBACK_MONO) +#define STBV_C (STBV_PLAYBACK_LEFT | STBV_PLAYBACK_RIGHT | STBV_PLAYBACK_MONO) +#define STBV_R (STBV_PLAYBACK_RIGHT | STBV_PLAYBACK_MONO) + +static stbv_int8 stbv_channel_position[7][6] = +{ + { 0 }, + { STBV_C }, + { STBV_L, STBV_R }, + { STBV_L, STBV_C, STBV_R }, + { STBV_L, STBV_R, STBV_L, STBV_R }, + { STBV_L, STBV_C, STBV_R, STBV_L, STBV_R }, + { STBV_L, STBV_C, STBV_R, STBV_L, STBV_R, STBV_C }, +}; + + +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + typedef union { + float f; + int i; + } stbv_float_conv; + typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; + #define STBV_FASTDEF(x) stbv_float_conv x + // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round + #define STBV_MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) + #define STBV_ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) + #define STBV_FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + STBV_MAGIC(s), temp.i - STBV_ADDEND(s)) + #define stbv_check_endianness() +#else + #define STBV_FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) + #define stbv_check_endianness() + #define STBV_FASTDEF(x) +#endif + +static void stbv_copy_samples(short *dest, float *src, int len) +{ + int i; + stbv_check_endianness(); + for (i=0; i < len; ++i) { + STBV_FASTDEF(temp); + int v = STBV_FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + dest[i] = v; + } +} + +static void stbv_compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE; + stbv_check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE) { + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + if (stbv_channel_position[num_c][j] & mask) { + for (i=0; i < n; ++i) + buffer[i] += data[j][d_offset+o+i]; + } + } + for (i=0; i < n; ++i) { + STBV_FASTDEF(temp); + int v = STBV_FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o+i] = v; + } + } +} + +static void stbv_compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE >> 1; + // o is the offset in the source data + stbv_check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE >> 1) { + // o2 is the offset in the output data + int o2 = o << 1; + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + int m = stbv_channel_position[num_c][j] & (STBV_PLAYBACK_LEFT | STBV_PLAYBACK_RIGHT); + if (m == (STBV_PLAYBACK_LEFT | STBV_PLAYBACK_RIGHT)) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } else if (m == STBV_PLAYBACK_LEFT) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + } + } else if (m == STBV_PLAYBACK_RIGHT) { + for (i=0; i < n; ++i) { + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } + } + for (i=0; i < (n<<1); ++i) { + STBV_FASTDEF(temp); + int v = STBV_FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o2+i] = v; + } + } +} + +static void stbv_convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) +{ + int i; + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + static int channel_selector[3][2] = { {0}, {STBV_PLAYBACK_MONO}, {STBV_PLAYBACK_LEFT, STBV_PLAYBACK_RIGHT} }; + for (i=0; i < buf_c; ++i) + stbv_compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + for (i=0; i < limit; ++i) + stbv_copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); + for ( ; i < buf_c; ++i) + memset(buffer[i]+b_offset, 0, sizeof(short) * samples); + } +} + +STBVDEF int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) +{ + float **output; + int len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len > num_samples) len = num_samples; + if (len) + stbv_convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); + return len; +} + +static void stbv_convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) +{ + int i; + stbv_check_endianness(); + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + assert(buf_c == 2); + for (i=0; i < buf_c; ++i) + stbv_compute_stereo_samples(buffer, data_c, data, d_offset, len); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + int j; + for (j=0; j < len; ++j) { + for (i=0; i < limit; ++i) { + STBV_FASTDEF(temp); + float f = data[i][d_offset+j]; + int v = STBV_FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + *buffer++ = v; + } + for ( ; i < buf_c; ++i) + *buffer++ = 0; + } + } +} + +STBVDEF int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) +{ + float **output; + int len; + if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts); + len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len) { + if (len*num_c > num_shorts) len = num_shorts / num_c; + stbv_convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); + } + return len; +} + +STBVDEF int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) +{ + float **outputs; + int len = num_shorts / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + stbv_convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); + buffer += k*channels; + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +STBVDEF int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + stbv_convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +#ifndef STB_VORBIS_NO_STDIO +STBVDEF int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // NO_STDIO + +STBVDEF int stb_vorbis_decode_memory(const stbv_uint8 *mem, int len, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // STB_VORBIS_NO_INTEGER_CONVERSION + +STBVDEF int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) +{ + float **outputs; + int len = num_floats / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int i,j; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + for (j=0; j < k; ++j) { + for (i=0; i < z; ++i) + *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j]; + for ( ; i < channels; ++i) + *buffer++ = 0; + } + n += k; + f->channel_buffer_start += k; + if (n == len) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} + +STBVDEF int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < num_samples) { + int i; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= num_samples) k = num_samples - n; + if (k) { + for (i=0; i < z; ++i) + memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k); + for ( ; i < channels; ++i) + memset(buffer[i]+n, 0, sizeof(float) * k); + } + n += k; + f->channel_buffer_start += k; + if (n == num_samples) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} +#endif // STB_VORBIS_NO_PULLDATA_API + +/* Version history + 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files + 1.11 - 2017-07-23 - fix MinGW compilation + 1.10 - 2017-03-03 - more robust seeking; fix negative stbv_ilog(); clear error in open_memory + 1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version + 1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks; + avoid discarding last frame of audio data + 1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API + some more crash fixes when out of memory or with corrupt files + 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) + some crash fixes when out of memory or with corrupt files + 1.05 - 2015-04-19 - don't define __forceinline if it's redundant + 1.04 - 2014-08-27 - fix missing const-correct case in API + 1.03 - 2014-08-07 - Warning fixes + 1.02 - 2014-07-09 - Declare qsort compare function _cdecl on windows + 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float + 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel + (API change) report sample rate for decode-full-file funcs + 0.99996 - bracket #include for macintosh compilation by Laurent Gomila + 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem + 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence + 0.99993 - remove assert that fired on legal files with empty tables + 0.99992 - rewind-to-start + 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo + 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++ + 0.9998 - add a full-decode function with a memory source + 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition + 0.9996 - query length of vorbis stream in samples/seconds + 0.9995 - bugfix to another optimization that only happened in certain files + 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors + 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation + 0.9992 - performance improvement of IMDCT; now performs close to reference implementation + 0.9991 - performance improvement of IMDCT + 0.999 - (should have been 0.9990) performance improvement of IMDCT + 0.998 - no-CRT support from Casey Muratori + 0.997 - bugfixes for bugs found by Terje Mathisen + 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen + 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen + 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen + 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen + 0.992 - fixes for MinGW warning + 0.991 - turn fast-float-conversion on by default + 0.990 - fix push-mode seek recovery if you seek into the headers + 0.98b - fix to bad release of 0.98 + 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode + 0.97 - builds under c++ (typecasting, don't use 'class' keyword) + 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code + 0.95 - clamping code for 16-bit functions + 0.94 - not publically released + 0.93 - fixed all-zero-floor case (was decoding garbage) + 0.92 - fixed a memory leak + 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION + 0.90 - first public release +*/ + +#endif // STB_VORBIS_IMPLEMENTATION + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +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. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +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 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. +------------------------------------------------------------------------------ +*/ diff --git a/shaders/2d.frag b/shaders/2d.frag new file mode 100644 index 0000000..003444b --- /dev/null +++ b/shaders/2d.frag @@ -0,0 +1,38 @@ +#version 430 core + +in Fragment +{ + // @Performance: Are this fields aligned to vec4? Is it better to reorder them? + vec2 position; + vec4 color; + vec2 uv; +} frag; + +uniform int width; // Width of the screen in pixels +uniform int height; // Height of the screen in pixels + +uniform int has_texture0; +uniform sampler2D texture0; +uniform int texture_channels0; +uniform vec4 color0; + +out vec4 FragColor; + +void main() +{ + FragColor = frag.color; + + if(has_texture0 != 0) + { + vec4 texture_color; + if(texture_channels0 == 1) + texture_color = vec4(1.0, 1.0, 1.0, texture(texture0, frag.uv).r); + else if(texture_channels0 == 3) + texture_color = vec4(texture(texture0, frag.uv).rgb, 1.0); + else + texture_color = texture(texture0, frag.uv); + + FragColor = color0 * frag.color * texture_color; + FragColor.rgb *= FragColor.a; // Premultiplied alpha + } +} diff --git a/shaders/2d.vert b/shaders/2d.vert new file mode 100644 index 0000000..00ba431 --- /dev/null +++ b/shaders/2d.vert @@ -0,0 +1,30 @@ +#version 430 core + +layout (location = 0) in vec2 position; +layout (location = 1) in vec4 color; +layout (location = 2) in vec2 uv; + +out Fragment +{ + // @Performance: Are this fields aligned to vec4? Is it better to reorder them? + vec2 position; + vec4 color; + vec2 uv; +} frag; + +uniform int width; // Width of the screen in pixels +uniform int height; // Height of the screen in pixels + +void main() +{ + frag.position = position; + frag.color = color; + frag.uv = uv; + + vec2 screen = vec2(width, height); + + vec2 p = position / screen * 2.0 - 1.0; // Position relative to the screen size, in range [-1,+1) + p.y = -p.y; // OpenGL coordinates have y up, pixel coordinates have y down + + gl_Position = vec4(p, 0.0, 1.0); +} diff --git a/shaders/environment_map.frag b/shaders/environment_map.frag new file mode 100644 index 0000000..60b85d5 --- /dev/null +++ b/shaders/environment_map.frag @@ -0,0 +1,13 @@ +#version 430 core + +in vec3 frag_position; + +out vec4 FragColor; + +uniform samplerCube environment_map; + + +void main() +{ + FragColor = texture(environment_map, frag_position.xzy); +} diff --git a/shaders/environment_map.vert b/shaders/environment_map.vert new file mode 100644 index 0000000..38c5b05 --- /dev/null +++ b/shaders/environment_map.vert @@ -0,0 +1,18 @@ +#version 430 core + +layout (location = 0) in vec3 position; + +out vec3 frag_position; + +uniform mat4 view_matrix; +uniform mat4 view_matrix_inverse; +uniform mat4 model_matrix; + +uniform float time; + +void main() +{ + frag_position = position; + vec4 world_position = model_matrix * vec4(position, 1.0); + gl_Position = view_matrix * world_position; +} diff --git a/shaders/pbr.frag b/shaders/pbr.frag new file mode 100644 index 0000000..4c6ad36 --- /dev/null +++ b/shaders/pbr.frag @@ -0,0 +1,320 @@ +#version 430 core + +const float PI = 3.141592653589793238462643383; +const float TAU = 6.283185307179586476925286766; +const float EPSILON = 0.000001; + +in vec3 frag_position; +in vec3 frag_normal; +in vec3 frag_tangent; +in vec3 frag_bitangent; +in vec2 frag_texture_coord; +in vec4 view_position; + +out vec4 FragColor; + +uniform int has_albedo_texture; +uniform sampler2D albedo_texture; +uniform vec4 albedo_factor; +uniform int has_metallic_texture; +uniform sampler2D metallic_texture; +uniform float metallic_factor; +uniform int has_roughness_texture; +uniform sampler2D roughness_texture; +uniform float roughness_factor; +uniform int has_normal_texture; +uniform sampler2D normal_texture; +uniform int has_emissive_texture; +uniform sampler2D emissive_texture; +uniform vec4 emissive_factor; + +uniform mat4 view_matrix; +uniform mat4 view_matrix_inverse; +uniform mat4 model_matrix; + +uniform int has_shadow_map; +uniform sampler2DShadow shadow_map; +uniform mat4 shadow_matrix; + +uniform int has_environment_map; +uniform samplerCube environment_map; + + +struct SunLight +{ + vec3 direction; + float _padding0; + vec3 color; + float intensity; +}; +struct PointLight +{ + vec3 position; + float _padding0; + vec3 color; + float intensity; +}; +struct SpotLight +{ + vec3 position; + float inner_radius; + vec3 color; + float intensity; + vec3 direction; + float outer_radius; +}; + +#define MAX_SUN_LIGHTS 4 +#define MAX_POINT_LIGHTS 128 +#define MAX_SPOT_LIGHTS 128 +layout (std140) uniform lights +{ + uint sun_light_count; + uint point_light_count; + uint spot_light_count; + float ambient_light; + SunLight sun_lights[MAX_SUN_LIGHTS]; + PointLight point_lights[MAX_POINT_LIGHTS]; + SpotLight spot_lights[MAX_SPOT_LIGHTS]; +}; + +struct MaterialInfo +{ + vec4 Albedo; + float Metallic; + float Roughness; + vec4 Emissive; +}; + +uniform float time; +uniform float width; +uniform float height; + + +float clamped_dot(vec3 v, vec3 w) +{ + return max( dot(v, w) , 0.0); +} + +vec3 DiffuseBRDF(MaterialInfo material, vec3 L, vec3 V) +{ + // Lambertian + return material.Albedo.xyz / PI; +} + +float SpecularNDF(MaterialInfo material, vec3 N, vec3 H) +{ + // GGX + float a = material.Roughness * material.Roughness; + float a2 = a * a; + + float n_dot_h = clamped_dot(N, H); + float n_dot_h2 = n_dot_h * n_dot_h; + + float d = n_dot_h2 * (a2 - 1.0) + 1.0; + return a2 / (PI * d*d + EPSILON); +} + +float SpecularGeometricAttenuation(MaterialInfo material, vec3 N, vec3 L, vec3 V, vec3 H) +{ + // Schlick + float r = material.Roughness + 1; + float k = r*r / 8.0; + + float n_dot_v = clamped_dot(N, V); + float n_dot_l = clamped_dot(N, L); + + float denom_v = n_dot_v * (1.0 - k) + k; + float denom_l = n_dot_l * (1.0 - k) + k; + return (n_dot_v / denom_v) * (n_dot_l / denom_l); +} + +vec3 SpecularFresnel(vec3 F0, vec3 V, vec3 H) +{ + // Schlick, Epic paper + float v_dot_h = clamped_dot(V, H); + float exp = (-5.55473 * v_dot_h - 6.98316) * v_dot_h; + return F0 + (1.0 - F0) * pow(2, exp); +} + +vec3 BRDF(MaterialInfo material, vec3 radiance, vec3 N, vec3 L, vec3 V, vec3 H) +{ + // Cook-Torrance + vec3 F0 = mix(vec3(0.04), material.Albedo.xyz, material.Metallic); + + float D = SpecularNDF(material, N, H); + vec3 F = SpecularFresnel(F0, N, H); + float G = SpecularGeometricAttenuation(material, N, L, V, H); + + float n_dot_v = clamped_dot(N, V); + float n_dot_l = clamped_dot(N, L); + + float specular_denom = (4.0 * n_dot_l * n_dot_v) + EPSILON; + vec3 specular = D * F * G / specular_denom; + vec3 diffuse = (1.0 - F) * DiffuseBRDF(material, L, V); + + return (diffuse + specular) * radiance * n_dot_l; +} + + + +void main() +{ + vec4 final_color = vec4(0,0,0,1); + + // PBR Parameters + MaterialInfo material; + material.Albedo = albedo_factor; + if(has_albedo_texture != 0) + material.Albedo = albedo_factor * texture(albedo_texture, frag_texture_coord); + + material.Metallic = metallic_factor; + if(has_metallic_texture != 0) + material.Metallic = metallic_factor * texture(metallic_texture, frag_texture_coord).b; + + material.Roughness = roughness_factor; + if(has_roughness_texture != 0) + material.Roughness = roughness_factor * texture(roughness_texture, frag_texture_coord).g; + + material.Emissive = emissive_factor; + if(has_emissive_texture != 0) + material.Emissive = emissive_factor * texture(emissive_texture, frag_texture_coord); + + vec3 Normal = normalize(frag_normal); + if(has_normal_texture != 0) + { + vec3 normal_map = normalize(texture(normal_texture, frag_texture_coord).rgb * 2.0 - 1.0); + mat3 TBN = mat3(normalize(frag_tangent), normalize(frag_bitangent), normalize(frag_normal)); + Normal = normalize(TBN * normal_map); + } + + + vec4 camera_position_4 = view_matrix_inverse * vec4(view_position.xy / view_position.w, 0, 1); + vec3 camera_position = camera_position_4.xyz / camera_position_4.w; + // vec4 pixel_position_4 = view_matrix_inverse * view_position; + // vec3 pixel_position = pixel_position_4.xyz / pixel_position_4.w; + // vec3 view_direction = normalize(pixel_position - camera_position); + vec3 view_direction = normalize(frag_position - camera_position); + vec3 V = -view_direction; + vec3 N = Normal; + + + + // Sun lights + for(uint i = 0; i < sun_light_count; i++) + { + SunLight light = sun_lights[i]; + + float shadow = 1.0; + if(has_shadow_map != 0) + { + shadow = 0; + vec2 texel_size = 1.0 / textureSize(shadow_map, 0); + float bias = 0.0001; + + vec4 from_light_view = shadow_matrix * vec4(frag_position, 1.0); + from_light_view /= from_light_view.w; + + from_light_view = from_light_view * 0.5 + 0.5; // [-1,1] => [0,1] uv coords + from_light_view.z = from_light_view.z - bias; // Bias to attenuate z fighting + + for(int x = -1; x <= 1; x++) + for(int y = -1; y <= 1; y++) + { + float s = texture(shadow_map, from_light_view.xyz + vec3(x * texel_size.x, y * texel_size.y, 0)); + s = clamp(s, 0.0, 1.0); + shadow += s; + } + shadow /= 9; + } + + vec3 L = -light.direction; + vec3 H = normalize(V + L); + + vec3 radiance = light.color * light.intensity * shadow; + vec3 color = BRDF(material, radiance, N, L, V, H); + final_color.xyz += color; + } + + // Point lights + for(uint i = 0; i < point_light_count; i++) + { + PointLight light = point_lights[i]; + + vec3 diff = light.position - frag_position; + float dist2 = abs(dot(diff, diff)); + float attenuation = 1.0 / (dist2 + EPSILON); + float intensity = light.intensity * attenuation; + + vec3 L = normalize(light.position - frag_position); + vec3 H = normalize(V + L); + + vec3 radiance = light.color * intensity; + vec3 color = BRDF(material, radiance, N, L, V, H); + final_color.xyz += color; + } + + // Spot lights + for(uint i = 0; i < spot_light_count; i++) + { + SpotLight light = spot_lights[i]; + + vec3 light_direction = normalize(frag_position - light.position); + + float dist2 = abs(dot(light.position, frag_position)); + float attenuation = 1.0 / (dist2 + EPSILON); + float intensity = light.intensity * attenuation; + + float angle = acos(dot(light.direction, light_direction)); + float spot_factor = (angle - light.outer_radius) / (light.inner_radius - light.outer_radius); + spot_factor = clamp(spot_factor, 0.0, 1.0); + + intensity *= spot_factor; + + vec3 L = normalize(light.position - frag_position); + vec3 H = normalize(V + L); + + vec3 radiance = light.color * intensity; + vec3 color = BRDF(material, radiance, N, L, V, H); + final_color.xyz += color; + } + + // Environment map light + if(has_environment_map != 0) + { + vec3 L = normalize(reflect(-V, N)); + vec3 H = N;//normalize(V + L); + + float levels = textureQueryLevels(environment_map); + vec3 radiance = textureLod(environment_map, L.xzy, material.Roughness * levels).rgb; + + //vec3 color = BRDF(material, radiance, N, L, V, H); + vec3 F0 = mix(vec3(0.04), material.Albedo.xyz, material.Metallic); + + float D = SpecularNDF(material, N, H); + vec3 F = SpecularFresnel(F0, N, H); + float G = SpecularGeometricAttenuation(material, N, L, V, H); + + float n_dot_v = clamped_dot(N, V); + float n_dot_l = clamped_dot(N, L); + + float specular_denom = (4.0 * n_dot_l * n_dot_v) + EPSILON; + vec3 specular = D * F * G / specular_denom; + vec3 diffuse = (1.0 - F) * DiffuseBRDF(material, L, V); + + vec3 color = (diffuse /*+ specular*/) * radiance * n_dot_l; + + final_color.xyz += color; + } + + // Ambient light + final_color.xyz += ambient_light * material.Albedo.xyz; + + final_color.a = material.Albedo.a; + + // Emissive color + final_color.xyz += material.Emissive.xyz * material.Emissive.a; + + FragColor = final_color; + //FragColor.xyz = Normal; +} diff --git a/shaders/pbr.vert b/shaders/pbr.vert new file mode 100644 index 0000000..643204a --- /dev/null +++ b/shaders/pbr.vert @@ -0,0 +1,34 @@ +#version 430 core + +layout (location = 0) in vec3 position; +layout (location = 1) in vec3 normal; +layout (location = 2) in vec3 tangent; +layout (location = 3) in vec2 texture_coord; + +out vec3 frag_position; +out vec3 frag_normal; +out vec3 frag_tangent; +out vec3 frag_bitangent; +out vec2 frag_texture_coord; +out vec4 view_position; + +uniform mat4 view_matrix; +uniform mat4 view_matrix_inverse; +uniform mat4 model_matrix; + +uniform float time; + +uniform mat4 shadow_matrix; + +void main() +{ + mat3 model_inverse_matrix = mat3(transpose(inverse(model_matrix))); // @Performance: Compute this only once, before calling the shader + frag_normal = normalize(model_inverse_matrix * normal ); + frag_tangent = normalize(model_inverse_matrix * tangent); + frag_bitangent = normalize(model_inverse_matrix * cross(normal, tangent)); + vec4 world_position = model_matrix * vec4(position, 1.0); + frag_position = world_position.xyz / world_position.w; + frag_texture_coord = texture_coord; + gl_Position = view_matrix * world_position; + view_position = gl_Position; +} diff --git a/shaders/postprocessing.frag b/shaders/postprocessing.frag new file mode 100644 index 0000000..12ab0bb --- /dev/null +++ b/shaders/postprocessing.frag @@ -0,0 +1,23 @@ +#version 430 core + +out vec4 FragColor; + +in vec2 frag_uv; + +uniform sampler2D texture0; // rendered 3D +uniform sampler2D texture1; // rendered HUD + + +void main() +{ + vec3 hdr_color = texture(texture0, frag_uv).rgb; + + // HDR - TODO + vec3 sdr_color = min(hdr_color, 1.0); + + + vec4 hud_color = texture(texture1, frag_uv); + + vec3 mixed = mix(sdr_color, hud_color.rgb, hud_color.a); // TODO: SDR color + FragColor = vec4(mixed, 1.0); +} diff --git a/shaders/postprocessing.vert b/shaders/postprocessing.vert new file mode 100644 index 0000000..701c0db --- /dev/null +++ b/shaders/postprocessing.vert @@ -0,0 +1,11 @@ +#version 430 core + +layout (location = 0) in vec2 position; +layout (location = 1) in vec2 uv; +out vec2 frag_uv; + +void main() +{ + frag_uv = uv; + gl_Position = vec4(position, 0.0, 1.0); +} diff --git a/shaders/shadow_map.frag b/shaders/shadow_map.frag new file mode 100644 index 0000000..fa4b68f --- /dev/null +++ b/shaders/shadow_map.frag @@ -0,0 +1,5 @@ +#version 430 core + +void main() +{ +} diff --git a/shaders/shadow_map.vert b/shaders/shadow_map.vert new file mode 100644 index 0000000..d4b3fd9 --- /dev/null +++ b/shaders/shadow_map.vert @@ -0,0 +1 @@ +#include "pbr.vert"

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 IJ*3b70dp)e4FCWD literal 0 HcmV?d00001 diff --git a/assets/fonts/DejaVuSerif.ttf b/assets/fonts/DejaVuSerif.ttf new file mode 100644 index 0000000000000000000000000000000000000000..0b803d206c1a4f19559d14f4d3cbd5bbfe0a86a1 GIT binary patch literal 380132 zcmeFad3+Sb)<0a;-Lv=XYbKdwX0ieiLfA22fUvJ3B5(ymNLa#>un4jv$SNRih=_m) zM3GHIL_h?LD2m`!Kv@J7Ok9wQ7`ceh^ZQoy1O~U~`8>~k?tTAwr*f)JSD&us)TvXa zmhKQr2ywxaNN$@pJvxuxTXl&L+6l1y>T0b-m}+xtEU-BIpTe zSLdt2T)=gfzMoK@a89aQLCHIHcK5>Ycx{s*D*CHWE&}ZR z+0UOOkq8m_Qv$IOp>xN)AhF%ky(N4SdynN3d)<#%Tl8riB>h@-?*YGUi?;CF-_sd> z#}<8<-@POAc}yqnckbE=Fc;4x}6DN$w{F zWH1>)#*hhQ8kt4rlPA!=%gJi8j%+5|$S$&vyiX31Bjf}*ODf0(a+zEu3Y7>|h_;aX z1G(?|o!|C%F^5=;PRrxfr&NC!k5PR!M&U7P3iohaO+WUjKg0#1xx(oGo4!Ew!bV{? z*z}$79kB}+gbT#MWzor{Fb*;|k@(G%&67#Id8&B^NdQN+koxLp9Q+w4f5ye1@pCzi zCu!jPQou^UTEKe176AG#!PBH1@B!ds0CW;L1^5c^4d5aGdWBqz3OHJo$^ZudeVHZ# zQUEmpS%8KB=n&cx&<@ZA&;!s1kPjFL7zP*xc!=>wTJ}5ta~>1xt2Dol`Teth9g`Q6 zvWmyXsOjHKLHMJx-MssUCI7>_q1?niLwD?@=?nG6 z`cnN^{UrT#{T%&5{Zsm-`jz^%`t|xP`tADN`f~jT`j7R;^r!S+>A%rm)K}@R8Hhn^ zkPQxl&yZ+HG1N3<85$aL3@r`q3|$O841EmwhJl7*hEaxx3{wn`80Hxk85SFs8CDry zGHf(#HS93#G3+-SG<;(C+;G}(-te8_lHrQsx=}FdjTWQJ=r;z9X~s-rwz09XxiQb! z!Pw2%(|EtJz&O}A!Z^k_!8px0%Q)ZogmH;+Ih5UMz&hh*<2K_i<38Z;0}dIF7*8NP zYpgI{FkUuZH7X{_WHdpmnLMThQ`nSYs%@%oYHG?gwJ~)vbvN}g^~1eFKrx^cFcvV$ zG~G1Ew9xbv@TI1ernRQ^CdjS{vTK6unjpI-$gT;pYdU5+WrEzAAh)KArYh4lnaEmM zmL0NBPLxyRnsSy5`IK|ymU26}i`+x*Bj?KlR@F;6g0LpTdCAMk{EiFvtswRxR+vw53&mwBK0ee)sn5%UT2S#yQ? z0w^wlErATSv-~mOW2ZOscormX==%}w6S!ubhq@f^s^LNiY=v6ST` zg_fr*OD!ucYc1<7TP)ixyDjCG4=f*Bj#*AwzOsB{xoD}fT(c4@`mhy!*or=EMIW}J z4_ncPt?0wnEC6_9&9Sz$wnK>iYeoOHqW@aaf34`h)?onjU+Y7FDS$@+^8kwgivi03 zs{r7kbt7OaU;1j^-fYX5UfbRg{tn~`uI&2yNpa)n0E`T2p1f&5n0oj1Y zfaZWaTL)V=TTk2lwgTH=+X&kj#7zKz=eAk4`L-u)OKi(+t8MFSn{C@{yKMVx@7oU9 zj@VAv&e|$$7i^bpS8a-2vK#F-yT_hj58E^Bwe9uoP3^h%Hug^T?)F~xe)d9pvAxtj z);`HT-9E>@(EgNtsePq=t$n?Hi+#I&x4qo{f&F9qG5aa|SN3o07wuK{YYyVjI%J2# z;d3N9QXDlMS&oK|97jt>J4Y8s4@VzIzGI+cm}8XVA;%QQBaV5FMUKUeWsX&jmmC`% zTOB(bdmQ^62OXa{K6jjUoOgWZxa7Fvxb763dZ)$da{8S?XPPt9neA-sZ0^i+c5rrc z_H^FwEN~8Xj&P1~PH;|h&T`IoKH*&AT<%=$T<6^E+~(Zn+~<6s5a%Jl5$6f#S!adw zg7dQTs#9@EE~Crl^0*RQVONH$wyVCYsVmpj#?{Hy-POz0&sFFuc9puux+b}%yXLqS zx}I_^b**%*b**=8acy_)c9pw6aDD7L<~rs2%Jq%wqN~bv%}v}|x9oPfeeOheio2#e z%iYkO<8JA0=kDU};qK$kcMo(AbB}UA|FYa^iuUcpxVp$cYDX;z{#lda^x@J`vxo+ms@Jj*?+J?lK1J=;9HJo`NFdk%SycusiEdMZ2@ zJeNIJJ&ITI8of5J$D7~{do#SXz4g6Ky}8~t-cH``-d^5*-a>D&x70hR7ccpi&cfEIuce{7Dx7_=I_haud?egReLH-6eEWR| zeV_O~_nr2g_kHKP*bf3Clczmvbazn8zCztCUoFZGZ0Px4Rq&+#wxKjmNQ zU+G`#U+>@I-|pY-FZX}o|JZ-bf6D)r{~P~Bf0h4QJc-xF%khqQUwmSGN_@@utoVlU zIq@yy+r@W@?-Ac8K0kh7{IK{@@ejpMiGL)1Ui_l?#qrDHSH-^+zcGGm{EqlN@%!Ts z#(xt3dHm`4^YP!sUy8pHe?37+&?i_DTnYY!U_x3#WMUM8cAUVPC@g35ODnB%DY%n^2K(A>neu z)dVFV1&je(z!OLagaa9Y+JX9krh(i*n?R>P_du^ezd&K2n2lbmKZ$9Hpl4ae8;bY}B(X7l)%x=V>HUP|Qg$2t9Rj=#Y17dU=^;|Dn2M1ox> zVE&AHwZm{n1(kjkn?wtGB2kp z&w?twYFqFUirsnoD*s>5>;b*X;aHiB^W$zPwioyjKdG z1409yLbTWO){K$lC}+lSni#2zaE*Rnd%B>ODpu$GN#DFp=fvh7qfgZI zg+sj7usgt?m>znYX2@WzAC-;R-W@Bkhq=VEm7&|x5#3z&+&rGAqsqW-JzbTGSl`7{ zgYAISGC2jGjKF;XZu@{E6wCv8^e-$os;JJl|*ev(NHpW8^{Yq0wIU ztj-5+=(ud0;XapVDXKA8Z;FmbF*+pLBUJf3&wJ>6%~nueVm|Z$d+K}cmoh(ELe5)N z+lwlH)Dq6&)~k2_TlkdBZ)zdXE0xk=lDwQ^QUMB&apPU3TV2 z;^HjMt2*pik@qiQ2YZ$tW$CCR&TaJ3ahX4*j?U`n#z!0}m&HrDjN4)iuhf=OW1@O5 zc8uV46pg&S>hl^k=eWuZRkpKud=`(7#nX6v8jt6)M`v(+4v(M1@L2f~6Ir~N$Z29^ z<7Phsz2M}}I@uU4Eao`3CH@qT854y(Mo-7{^!Z360 z!=+akF1^CzmulLhBukZN;K%YUc5IE+?JBO?6{%b*Qh7SR@brJ-IG4kymWxSKSj%bF za+=t>oZ|7Pcs$o3Sl?#6{k!I4<_lPN5~>(3R56?$;qgbJ_*oXi*2r&*|BTao#%Z>3 zd>c7uwcI4@IWc8)9dHSZ15_NQr+K}A#kT5Sr zUfsDoa65q<(p&{U5AnKiJr=c@$MfFJWmx=x;~((0KFRG*1IJHsocCGr8BW9HR_xDk zdWO+dYXIdj;5=UJ&&v=qM-YEu^n&`V8qY^n>2Ew<9TWe_WiOdO8>g*{GJnnS9F8aO zG3AWpL^|0lUe)TV6%lJE$Ic6?M|}2dbvgO7F*f#XbC4Jdk1>kwxgXW6!0Wq-(MugU{wtF) zaWTgS@OB))o)zxo_?N*@c^_QP`rvJIig_{dPL__S)|bZ%$2k38$5-+)tLgOM^*zDsb%K{u9i!v< zTrWCqviPV~%tuUBR>h}z{9CNW>07+aJ9v3^@N_=s{P~>Wf7UL-+l-#7Jnzfsqx$4g z9zUCxEh8pgwexxUQC-C6ed1j_-3Pn zKfGS`xc;oiWq20TZ^A5wi$_>F#Uq?X)h+6nHIb)zki}Q`HHOp0y1SXraLqJEBXJ!i zj$pWOH;WfXar|D6zt8dY9DgV#ejLa9@c4%rPWc>A{FcYR$KoYD$G>N|kj`l&jt^!y zA&ghz5l-_4j}LKtAb(c1OJ^}${D)>cQs8YWetL^uEhqLA8Oma9Z$#HHKlP@^^1YU-<45w#!UVPn2 z*vjL7;4~XKeuAfYlBc8lZM?98(`?}BPv-c`oL=I*Rog3Oj1yz=I-I5sr%z>gbbMUE z;}@`a%I6(wJpF;wG+}v3gz0LQd^L@j%5W)%$8&vnTc3)qAut+|^7J8oF~GgJpNJsY%4}jRh~y} zOIELFIzKTQI+=~*Dh(aY$MloGqv83gG!h>NB`(9!GVpYS^*mqRuZ7FJjVAN<;&WRm z$Z=lgXnA7nUOI`_i}xwv9i9#!Q>xeHBENz^yxm2$E-~v2)igZ)HvHK(yk4q|jp8L~ z7whx8e4a(V;Wdr98(rLL$G$afM>~+V zv=dG(b)em7chZsG3oEt@?M-`=Zu9}#pWID{&|=bqj-Vq*Pdb{8Cil??G2`k*C(%iy zFI`K^NI$xst|$5QJpG#Vr{B@<$pCtV{z8gqght2^0Y6eKXapA-DtH7RnJD;$5Sb#R z2&rV2kS?T?IYLdLCYdW_3Ylb{P#2cvqe4UIpant`p($A?w8Xq_kTKBkzbGV#F;MKNCMEhs2ZODe|#+4)c~z#7eP}91*`2zb8k< zAH*NX=i<-e&*ZqIm-OU>)JSSXPDD~G`T3;WpkEc!KWI2;I zm21m&Xr5eGu20*@jpQb@gM6pljCPiX%42Ca`B8a0y9 zU#9Ti$vw&?wd5+tV)4J=Ax;qe#ohll=Y_cD1V)Ifxu+?qLD)*{|L1-Y*xQkzkwuX= zB1!-L!bQ+CBTFM2-~-fOYU(^v_9`@2=X-`@EbU*E>-e{tIXMRLksNWfl5cb7jV zj6BHVnH*OCV)5!Y7DgUqj?q>-9@_=6h>4}Vh5s4nRf_*>{ztHmAL)O4&$v~#|9dPl zl;wdwcz}nh&-4ugeswFnMZvLXpN+6t!_8mhK-8Uzd|G{{I-Q$De}P1LqovGD5~`p6 z3u4^*^Y;1kJBpj7$3MDnI1#xdRlZ>G&PF4DkwhI_r?_;?5HU9F2BjP0|$1ku)2TMoSEwXUw=%`*OL zM^3-V;~CX@{?FB1Ezf^0e>PY8Pt2F1^)@4Sz<=haT!O{{{AYfUcRt4bw(ZsH-^Tu{ z%cyK1X@LLGkJawabo_s)NPn7bB=D!V{$)2XMxBb<^Xvq}fBE}!>G<N4N*ch~NJBU~X$0rM-cw`ZB)NGrSwz9xje_f%B8@aPg3|dr1Q6P5O`k`5T-cZv@{@63KMzY6)ZxoEE!Vb5Z7b zWC8Fe;F8Fba58xcJ6>kK<7I}dE=TSw;JjodTo7-%ZbAB6$#(cV;Nr>KaEW9OT!`$2 zOT@dy`;bdHoPhVT_rrf5PQqKUAHY8dC*j@WL-0R_(~wW#G`yc^$yqo(`4!GYB5)=u z;N(IW8^9t-rUq&tGIrWrL`yw*@7YGZ)JqbmpZbBv(|F)P8YFfareXMLG>xRtESg1} zv=MDYQfX7#lz8b~^e*BPCJU2Enr^CYDxtdBy4k=V)jdkmbqjP0NQS&dUPFxXT6rz; z<8AfJ;IKfZp^dC)dnN$_+Q^Q!51_^pmj(@&11)cL9dC6#Z({>*Q6q0r6K_$Ox2T!7 zsD-zvmA9x3dyjcYwGEsDJ1%X({dRCJ-o|dc-Q58(o!~sAGn|)nh4bN^@4Hbx*7kmK z4_rK?vvJh=z~!$2mp>y7(GY3KrO-%|X)^E>nnD`URGLZ} zbGbCpbeayFNvBNf(z>J$ZAcrECR|dR(#Etgxsx`b(5PH$ZFCJ?L$dj9u#Rq^8&HOg z*fG=5O>`6RQ}h(l|AKyjG%sOyOhm;>0}0Zgv^AL&4GN)BadQSTmTL9 zB;@RAvY79=FNK^fBhN$stbpFx0=an|67vRR27A5GHG3f;<&cK=(eek;;vYkYoI*?f zN+Qr0A~b~y8o~?B5RVoKgX3A?Yg2lcFd035HtM$kJ$xeze=&5 zf0be>|0>0L{#A;l{Hqj8`By2X^RH4&|ATt4a#oi}gHp0>xM;DR20Rx7Yk`9H#h72Oz;K3f^em{KUWEd&re(`8VUpjj9nDJyR z=PPnXt|soYwxOKg*#6_CQ@;^H!CiJox?P!?IYe2<@(XdxEj6NqWQlG~;1NAxVJG1AdU^MK4up!cRKo=*Xchn?V zL@@6)?}fk5jM32ij`~LrWFV#{tqDJqX2P#cYr|(cTcp`E z8-9ISA3oFEBG=g>w9K8rn^AC^TN)zH1GNC&4(}+4v^(t%zX!hKA<~{W4Nk4?E)xQm3Cd-Ha+y%S z#3OK-pj;*>(|_P(b$?<#ib*w2L_+#m&*CzU7y~e^;U4%@#Aywpm8UTr{AN}#MgLd7 zQsa?X`?M!rF;WQdFT=+h18~ew;FS2v3bDvPbGlCQPoU^@lOhpUM)U*Z&%O=>dxc^D zjA8;#NVPyr3bcR?R6eAL{%3v>umym39?dvAVtySo8ua~7$fx}L6ood>-K0-L-$bvE zc}g(PH?Ky_nwaNx(dT74o~I!|uNci!%#-k~n91hp<{3B>^N4vGzFS0z5q)u^uu0e~ zye4cFUKh3tZ{kddfYO=BPdI%uO9+`~o9E)h7Q32feiYA`c+dM$Frob(KwIq<-_g8* ze#&~hXx?DnXx;=WkqjUY%Z0#LtDMD&vu-3uCb}CvDPR$x9B=}4WMxO7|47%-yv+y-;D>;}5ycM!-)`*2h8Q7;dlkCr z<~P~bS%6mp-x0;X0lw3?95e*y>4?w?Pyi|fvVeQLR61D%{#F#1fbYgLJwQ|pm;iLNAahDPl)2r;Ms}% zS$uQK0Irh^!-MG5jW8-?7#0v}Xb6lL)G{y{_Ev*R)AS}yli$$bBvq8A*-e@pPBRjB z*iSUHfS+L)h_rJ#%@pKy*KIT!LmsCY1)A2J1|zef6~}N!EGDice{Kd#7(anL06&8n z3cyf$V62y*I~dx*&oJDcV%tARvCZu%>SnU%Sc<@6QxpyCv{*0L$;A9N8wG$-^B!2$ zMDl~2{X_#>>&h^cpv>)gUUPxfSFYE8o->bh*KS)1B z|D^tD{WHjsN`CZC*pr%inrCsDsD|QaWT4@SWJr;78$`Qi)Wm(l}Lku+QyCoPbk zlggwwq@7Z^^uBaZ`bFc`1aLO=0nKd9Jj-3ScWu`LLjw;5rUqsP-VPi{{58p)6i8~F z)H&(Cq~1w?OX{06IcaNBdD5pzCzJk>RF$Lz(}GQd&4Vq2?Sox|_XO_?76wNK7Y3IF z*9O-IHw9k}z8>5Wd@Ce|3?XaC6Y_@wp>QZAlpd-T$_}*;bqsY0%?!;Aldu#vgypb1 z91PzPZWeA8ZWq2c+&^3to*mv4em7Z2HYMAWH=MW*6)i zBr9?L6yFa7r!R6&lPIV2#ZqyC_y{ycb$Yd z)A%)YaRRr$W-d6r()MwH1jYp>g453h$`i5Nk>pDXCbdiI7UlEoTgJXh=ZgP5Sa633HfYauXI}{h?bXuq;IGw{e-7PdX zv?wfuwP91(8uo^h!;Ql&!Rc<{{BU7-dU$@g9GuoC+me0B!6>JnQ#p-^bmO$U((==W zfYa}S)8C|D0;gqgy0{j}%+DNAbbw*7jJtq)cBHUzS$ZrmRC* zm$JcS6U(NRJzBO1oW2Tkxth~NA(3Agrz7Vg`yzWHyCZK!c1CtYwntvaF7mp_ipVoq zH=Q1t7MT>87?}_mk8_7ZB1Mq_k*<->k&cn}k+zXmkrt7=BF!S{k>rRiV!`UZe5Kb< zSAV)#`F-Wt%F~s{E6XeQRL-lMRryHe^vY?KQz|D{PO6+(Iid2Q%5jxrE5}rht{hoe zT3J##ta51O;L1Uj11pOv3o8Fs*|V}oFXNjJD>NQcb&U_?$@tA_^Rxygfx_EUtan0*q6(`)Sr3h%)T>w&b)nQ_nGFWJDqNGI`4GO!1RGh0|P~06qOVWEh;MN zU-Upx-=fw<^^58j)h@~`auvSkd(XGuQ`3{=3D_@NelfFE;s5{l@8N*VveBQ7X0{7q z5HJP6^!-YV8rYwK^+<$|0XQuo!2ZK~kVH5QcnWY9@GanZz=bH3A-o8<1b7MXQxsl7 z_|GV;SF;fB!u1B=tyI8FoQPdjU}=cGR1p4wFdxt#^hj4M1&jlJ8Q}!LRN&VTJ_3Nv z!-~(|Z{wgtxDfCHKo58k@FgGtfV89s5dIAz>M0cg{}5p@U^wu@2$7eByhPMrLYmS7 z;3p9-0z3yCGYM%q0QDBZ3uztT4dAG^v;(jcKmmII`5cX9;`#D17oirIV#}Q5gYz2N5;Whxuq`L#*egN7{mxGX{brN_7 zgkJz4Q#zI>WGD%CsBQqlD*y%fK(#9b(}0%%uL)=hd@(|#6>JV1?Hz2Xf^HqcJV1NI zzk;wMpeyiK5#9~B2ROCPcs ztAg$l!u2ZXenhxQ1s&=UWcdMfki8)I5qupu>K5D%Kt4LuFZh-Ux?czh2>{5W?ixaa z3cBkE%>XOnZy-b)hCFqE2DdNB_W}pc!}$R8Jw0SETnH!v4t|EG17-sUKg06@XeR?JBP-v#z`>hjTr)6a zAk+g;RznShHh>*C%9QK_fbWJn2vOeT5b(MPQ&lk3L&%;780sUOtb(Bd!sh@R5#JEu z8!E7wSn^>N4B&MN>dwH>iC{xQeSN^t86o(PhPE-H9n$Usv;_`6r=h-SgMi~P5eUBlfUib8lYWVi8dHIfMEJc5G9{!Y+N!1n%h2FU zP1KoztU-uAQWI&*XxmySXD!Hx44!48JemE0qYjw^04R%$ie#dUnWKTHBYY4r4mkLe zIUay~MS>VXO3}r1tKax?7GRSlp@{ngCOart4{wTsW0G4JMLbOv^N8m3b zL>rWK1^x=c!2s}G-hgl-U=r|62&VyN1Ai4EOKSn}HxVuZJPv#p!gVUhyAfUmL=gYB z`Z?t)ltX?Gc!mn{MMAE;s{+0}POki21@^VsE9f%}@D*}w76G)t(H9ipiWT@Tu$37= zf0(iId89r7d@*Cze+0AQBZGj~A@nHr!j3ipjy1`ncLLB3=2i%E0pPD0U%5Woh66EU zjq_*+0C;WgjIcXkDDWPH9>dz>u{FR)5c=8YfG>fU;#<^46o^2m@ORsft<$Q%>hQMP zF&FBSY>!HVRadregWVZEZ#Uz_ftA>>)9k=5u?wdPJj9C?)i|tkVh0x5j>UThb2ez#T|OzV_J#yAEvEx;u8O zdth&{CsuoV-P|GUtF9`L{;16?G8?;uk79R_eZl-itN^aY*_kz@413J4V27FQEN>zk z$!78@PSd=OokF&={06>pzLUH~cIzI)?oS~pB13eKlZUXoQA&nF*N-K|(0X&oQvFFX zT7OOdEA}m%W2Lj8)hX`p;+&{g2Se!^vdb)37V2WB>9Q z>@B!;({(ejn>ZhPigU=DMlab*o9bTC{is``Tg&!Lb&JS&-CUe0ScW~%`PlhffE~}r z$RhGMS;%)g33ff%PUmyvCt;4Tl#CP>3(w$f!wJH3!n49$2!N4{Mw@RZAHsbKtP@s5 zM}jLWfq#v+mRbB>_6{p-GJzb&de3&yZKpLrCoo6rfR6%JO-Eygb~~Q=5UZ*Jo-g3{ zuZbUVw^aN{7)J!Lo48bjq;iVw;+>Ktexw3_>O<@z?Ev4Ag61Qv$P%<6+h1>q)H{Id z$LS2RnjXbTrv+rRgxe%Qjaa2FhAYq<1-jUg{Td&~e1FiF-skY9~L)<;RkOBT!G;(X5$HoDa4K0sZzR2pO zES3gpuz!Re@6VNCuWR3kma<8?5QDpXrM%{j~3&vf2}Om zS*~6lrL6&Odn4zB5ZcTH4KX~|VHNuu^5p`Dh1$$oEall8w(=w8*UN1ek=psH>*ZOw zP2RS@ZF}4Lwq=ublVy>8k!_lNnyu7cYRk9h+a9n!VCib@YPrjLm&Inc*=$yPiP{eR zVt%b~E^Tr+%^4Ei$qlHtQmOdfj`|)D*9l*vtZIEt8Y+I*dLcXsbF{Zud69AC7Y5lxJrjVV>=x3Rx6ubtW2e))TRa z#330K1T+Y7kQ_=y4;siBNd1G>525vyHQi}bWhHRs>%r>=Dj(AOyDQ~%z+iEvvPGP& z^rtJ7{!5iD&ng9UIfG|um!)(C>LZek%BT3M2~J7jBv)NBFc+siTxn9sTKkSntHnu* zGDE>sT~U0%Unes^J8TTr_B%zLf!K@|Q5)3P$V^SZx~SIT6|!wrb2KKT*F58dUke^ zT$`o?LF?+!61AzYW#qWAqY4L&8d9nh43m~DuGn#^ys&6z@8R;iu~**x=F`IbLwUv# z5A=K}Nq9~-q*u4%(2aJ~HFv&9zs@b2w{jo$e3dGF-5ScZF|!OxPOA-%uYKRDT&5NQ zmL*2Lk&$z{8k$RxV=u8}$TIG;ijyK4);zhZxslKi10=kav;<1Z%M-%*U9#nw`EXsB<3*Ay@)BM1ocNlN| zg8}uj8-g3LczU&5gCov*G}#$*O+V~4ps44)0|tn%-Z!AIXU~EG_b%VD^ZAuKc7~T+ z@3SO+`L3NSR=l}G^W@s6pM0t8si(?rB)|0ZlVxR3KJ{{+Q#*H_Jh^k%N#X1-KWWV; zcfNJv}43BQ037_8o~?*+#j+9VCc$O!GA| zW(Tc_cUZI2Y>7U}lI<`zW)gFRaR!b_Mia&`)sJ4Fdt5w!<)Q+LV;8e=3p{OtpUOtd z59mQUTQp!yK=_4T{0UK>5qGHt}5X?ep0sdZQ5R(gEBeZW2I zzpoUEy1YJc?U!v|e7sjrs{d+cWl^*7Em!OehZSXL>Bv6K#*e&lw%eOSh7O(nRsI{y zN`oGvgSj5EZ=<9@B7$U7)z3k$e4uz#tpnOs%3fgs`lp#B3zOgWv~E6YMreBe#|XUSYvcSFirGbk?k;%HHAhI2}L>>G9zO#mZFWMP;=z6^1=4 z$KyzQGEX}`SJqLYA89e!bwsk*j+AFJ_0N-bHfZRu+l2H69u_>lboT6}RadVndkc!` z!}NaImp)uvFkDGjmMPCG={((#&`CT4S+bM%xwUL&lhL4)FajE7y{NIvmLU$k)*>4< z2A$oi!RLDL4|pV#!K`!Gm>N6XGL}x4GJJI`bLGPE2ZhLd=ab*Ube0=O1+s7TJla(3A)jA{4oYY9@ z=b>CH(TSrBRD;RAEEu&i%l~>#IfU*=4nnjA^e-C>8iec^I_lmyyz<6jp{_={ad;I& zRta@^eLE=M(s#)@jI0T{G7dOM20hU@u+;}XUf?R=2x}v6bDZ?32SMx5uh+Z!$Deqw z_0E~8ul(GHr_qOghVQ40hDNr%j`K@6&Y+6Kk%L)Il%kJ-X@@X+8K>PFIY&RlZZ=Mc zZpA1|Q1I*J9XC7g?zPp3@m!3cYc@9SP*#YRY1JGyCRf1#Xjf*QaM}HZP zvI=B&LQV-?q6}6ZSDsKX zi-eqGokFZa8I0ti+yto*JA`7PR?}J3N>rmAqNk$?L=nBvg!Y21g7&|VT~%&}s^Qg> zpjafPtvhZu_UQE%*q)0-O~e%0)7ajqaqnD1kOXNRLmi{k7c?eL6WOEEkX%l~L`c&h zwH_pjCR0iIln$Fmu}O8}of#LdO^EhK%x9%8C}#rcoEwr|#A+sEghGFjW{7#5-DQ@& zID6>T>Mceab(jL^9LzkkU1S=ODN3#oTt(RuM z9`t|q%HPjq*Q4P@PrbTy^Xmg2e`3a?)22X1?*iX9qaN&>_KMt8q0nG7&_bip(3u)# zGTH*+9&H>V;^RA7VmFIclU{Jxs-P^Gwy-napTC}SBiE8;j)Bs?+k%Fm zIcO18;pn(o?(w&do6XF#_V^p*$-!j>|Fa}2E4e-s4xTlp?Wv^2cbYPc4TZ+Wwn96j z4u`I(-B8_vOqfFUdcuc_Aa1+S>(Y%e!ovMDpq#rzuZjNa6^gc)rVbaYt{aCcCs})K zM0<@#pH3nha#KZZ;30pJ);7jkbpl^!W6O4T<%DF0-wzi{!#@9aCA+lp`Bkh$Q=Vzj*pD;EQD zv4pBEvXUY<%$f1YMe#%2`DsC8qDRy^+_Ki7@tM-V66mXHjRmP?rXIRnD*iE)*)G{M zHmyx(*E?mW*=E7?#b&qHkZYK`By|aP2|4 z$%4lgFL^U>{jf9m3hB4s-cT;lmNd2N#=nh}r;pgTyUpQ!`wqYV)_cchAC3Q&i+;ECdEOHx)60Jowha~1_rTAorCSBItLA3F1 ztu{Sm%3=krs#YJ>1IMw8%nITfO||^Q7+lpO$>Ee(GZ6Yk+_7TOq7@8k%$qdv(MKmv znm26SZsp3=e=0xke&v}Lt{y*r^#!_Q<*To*eBsqs#j$giESWQR$&v%!k5+tg?%XFU zKJvzInzQxr;jMG9G%1jWAxo?-age3HxhcdSEE1i=9OJ!3R?%N%fI3Wb868BMtdZRT zt;3^b(sUkmfe@+nKu=?~E|aJDB$^~7iAhqDCMhu~3C9vbN#PdG7Ooau3PZp?J^%sZh79B0Jw=qFy1l*^|1?*3LozR$sC z`94@q7nm)2o{dMh85i~#L*r=Y#*I67YSN@r=f+Jsb!yVMGdDOIckV2dN8^zd_{i+3 zbI41N<&$u(jagKV{<7pY5Ua)^x?%0`#R)dgIuvVHIb-sQ+E{n7EE!ZATau}9=Pr#9 zhn{ep^wXS_)?)6Plxx#46ICN(fE0*N^6JpfvzgL6auC+1L2%OsRCDsz%aY}qq7lUF z%1mYUI{Npu6br_z%*W|K%}H@NBw9xTxfWp*(T>7rQ>jlPUOmW5 zx~7QBS6*Go^z&`yfsulLWhhadR$ytMJUJI`mdDuuuah*OJPFAnUSFrwS*z4pN5}KB zJQ_JCR`Na-CONr54;sto4#|l{ZtD<3elje^c)!CT^gwX2Hp{tDtT{0cqSzUAK{6+EGJx=43r)MU<`q~pyMm_HCUu{on2Gps2 zY}<|t^%UB!)(6YxE}wxFeei6yvP@T^Sq0tE8EZ#(wrtU{b$XW+nRINOks_K~PcU?x zV4aXFPD+_j%P`3|p;n_V=@|}7yLfF2nZ#QX9D!!qgv9t}b%ica3DCsK4ZecH=8ml4 z)To?`=uqW|i%uz7kd?~%Pa3oVn-0PrQr9EWQrLK+72Ga|*CWBoXynyO&>HD!LWBB_ zMh#P7LwRA6*dQmW@f4k8TJC(Z@1_$cHuZh-&SmR%<+UnS4!_oCbI%XX7CtbX*4ps! z+585>l#4IEp}aQv;hD3hO{Kl}9i#onb?Tz*P%7wSqsPylGhx)itKGX@J8z zK4kK9o0hNG^gQ?)sp4~-qu{UuBmLckS-e6a#pyVW!!Foh`&xMvtZJQJM{RnY1z?f| zvrVtpbr!Ui5@N$-O)AkA7)86yYB8Izex*SNF*^;S6MZ7Obanm$7JM+psb1FbX6rCy zuu((LO(39%ieB^%Z^>}L9?JEw0p4n{TBQt4hBm{TVac@Da`2HpWPo{xX+%9Y%v3QX z)^pRPLW*+nM!Ok;>m|YcEdF1^?5tDp+&5A6HOr> z@9fx(Xq}s$s4FqJZ6)3UCn+&ZaoCJH>Pt+~V3#IdvV=U26a%rlLaD5~W3dXu3aZBK z=?j9d-zc9IFI)HWbH!8U%$zRG_Vy@`d4yW zC@<~Wf(g|d%64VLisg_b#@8y&S2t;v8z<{ZECohzR7c%btQa`Vh7@8qg*+%9VCSRY2FoHiS|vchqo?K!sI9u)hl{!2aJVA$_L7+9h`OD>2$hD>4wD$ zd>0k(Owe{J-;}Y+3(87m40y)MR1anHlJ2=R-A?Pn_G#vaO*+Fgu|!iqOPte8Qy|Ay zi{7erSv`33(qXg95yz$uIm94BjS=kz)Y;MvfH~sdKIcZ6> zU@oKHYSC5~jPZ^Y462wdm$E6{#cWLKc*(NKGvnHne*NBD=h@{@=)V3F;CG#eIjQx2 zrx~m!ohX)=H3e~QyGYTSu^}m^82#E5y~C2?!M~9ND}8#jaYA()gP&M~#vizek-(dM zB?lgC6inI!rGaTUVGEIr@|Ibuti2I({evscVWI7AQM()d?i+9JzJ6^tLqqankrM(itK+!XFVu~h&rI_>C*LhjEZpq3uXW^fg z=9^3Lg2)Q9mh}y$_tY}bYb-mKZE-Qrk?Dy@52dH@8S-#p#EjQl4HVja1bSbIslYMa zEm(=1VzC?bFp|oz!y3al2;oMBmHljCpn3_z&Fyvki^!v4v8#g*2A)zs8+caf3?BU7 z-gH|-LmNXo_kD)mw%+c+wh6WgZnM#7G#O>Q>uSL}t~RaRXLOt07Pr-7_c%OGx2vWx z)s!lynbR!k)(l%yV?%R83tMHuT8o@3w=w6M+u&cswzTHja~-+PTvx9D9%C<4FZo{c zy_Rm)UbbHLA*La6k-5k+&{|;6cjPug&%UJ7p+jx5`Om}ZdBjv(q zV51>6NmyrxGzc?R(MvDgm@0g$t8P`m6n#f~6{o zeNm!2z}wcD+tyhKTf$KY1Hx^C5#_S!blT2tr$Z-EqgbLxliFl-S_{FtpHnA#_0|-- zH^ptYn^O!aCMgBONVIt`oIi5@`~};2+j-W)Z2c5n*NZm3c&mw-iJ`8`MORv^yn*K! zbb5!?5vTJxk{lV1I*uC78m`8UyBtlNOz`C4>Pv5}Vz|}_ATA$}^zn&8idFxVEpyG157vlI#8;EPCs7y?$_s;>Z~QNsk$OtK1MNanIm(jn}B!HIgqmYT9_1ZR4*VRBRR1 z<_#O4vz3ckTONo9|7f+H7`1HJimQ~&1`!yC`lpvIdm5kEV`kyLvltn#omJke6t2Fo z-vkduQBiwVf}=I{G1Iwr+bMr6gr#3V5mk?`|>?vzJ7ACv{_oHD;|{%vWAkif=Mg zIig%`_}uWMmwzmbayx*J3iQ9lN9A*6c+*z7Z|a}^<+nVq=B<|5Kk=}_=j67-9*`*O zZK5Qi-%4U*DLN4)ir-zXnauZHd*ql#tCNn-Y_RK+q=3rp_ig7?M16DjT}f zJyYF<{t=EL>G?H-COH()d#!93*Vl0CQUW!IRjA3A%b^h2!Vo5}d~&NUm}9XB_4@s0 zCu^^DL+h%w8M_jx)&GB9yX@Yoh|mgy!(?oYAO4cFL-$3&n!UB+_^E8x z|0!%JqG<^kwUUXs@dk}aEYTI1F+3WrddaG_r08YLA+>@HwiN8)^Q-~b5Sd-2V2@H= z|Hj&t8G;!LZ(o<0A=G zHgT4>#1BCeXU1kvcCuuR1A)wR2SA?ZHvmldBq^yrqk^1g<52OLG{;uTz-;~eef)wy zY|Gsqbs$QF6N%pdb_!SUg})IJz*$(_8L{xpOJ9BU(i!&CY2|0-b^QI8rlPX4xmo$* zi!WHL1WCF1Tjfa@JE9q)Yaz#8!P-+Sj#YRdSbzX2k;Dgv3}y%r%q}vBi54q7!`us_ zH)`R6tTM~9^cEvRX(*n`VAGgvt(FK?0BmoE;6?{k5{&4I1EGV}dwMWY&-9{<+FN=9 zGW^9zL$oE@+DS~eq+2aIBn1Z;+%(e-B7a$2*YwHC*bD6B5%&E3Eq{Be6cX3T~6# zY?3vYifFWJH1Y_m32*7L86-`W!>pSn32rjIm`z5#8bW4sY9ZId9!V0>4?R1`ykS{4 zY-}OPH*AMdR{?7f=cQYUJTlnlnVQtREO#a6GLVNWE3-eAy~bQV37Y@ZOtym(!YjC$w>x5;IWwD_#Z4@7W6 zs+1-tLw3#+?~uBg?l9+D07bZj&sJftaLjYpiZ#*_O|8Dxu*6tv zT54WwS#DctU+GxxE&_{5NFr|p-&Y9K_?pOB>pP?zRgNA~mhTi3UIgsR_=9W;D|m_> z+aaI6-j8Ti>jm<(&^{vuP;&(P9L4@SI2UBgN+FI3rBMRJUBM2G$?Ng?#VEVi z>rr>x!*?4R4xB+&(0Hqv&0|HV1#{WVlD^7eoaOf-7NROrit>55!Ofr}bDQjpkfRZu zM&V+#>o~j}b)DPqd)VmKj{a>i*!vT4G3|L%*f$r=8B{bJ&mjhN!yss_I!~nFwR$~i zLML5{HPzG2n&%m49povpPViJ9NOrch)U#Zu(|L735Hg4cNvD%dvKho+(|Pqiz27T% zna|`ido4bj$L?`Ncp|)BpVv<$5zwW=?w4juv8OnaeaZgp7+?Xhd|ke!yRE=p;K=t3 zLUo+M;$Uf@cA#aLZ3tZ1L%c(xCJ5u$IB}dbPCHRIPG4jyGLN%NwM}(T^i1|nj+!Hs zav~|!&Vgh;$2127QfZrSpYN!2&+(Q-ltj+)&GFBPDnaf~9jg=Tq&n>i-E#dh(=zjN z%L?n=AedDknV@TxV})~ttKPlbQ|n#kTjsBgS_C5Es#so_dO}1LY7`GEJ6=|tpdDrS z0daH_3$h_++2tcjExUN2rRjvYs^xDm7SxDaD=U>a_BqtGms|Q&sC@m#Wzf|W&Ofq) z11)CJREqFNBRtVik&Skfkt8QQoOC2S85vyAu+Cbvvz)lD%np;w3d@$kqZ0+IE7{@D zK$QZgxC9dbS-!dL8=qW8g5`lTj{n3fZNw)|VE2ewaL3=x1_QK*?zb=v-NH^vb;fSS zfySxQ6i1nHnY7fX*BSIC$t+p?I=|j-wp-G5>H2K%ow_^q`G)SsKBfY5A9I1FkEIWV zf7%BbhM0;BlTB02Q!VADdFCqfJWG|O#JfbdgzwifOM^F!4mR%3@VQpmHeI6Y&M3KC z#EfGol2m(yo!F}MIa49}#BZ?bheRCamdut9DsZ2|3#sXYb+^Etl4P(W$VIg44f+u# zgDkGrLS05Wi&4@+T}F{cvp}Iy^Hk5MhkNscc=K&trLvcOY9hlQ2m{S#>6L|w`n1;_u)C*1-(X?<0V0F~#Ac~5c zP{k@G8?(8pb^r9&3hl_yd_I(pARg-id~lIL5IH8ILr9no*BlM-!`ofqd}Ew-OQ*!R zJxZkF+bhDT(ege8b%}^+X2`~fkp*P|d~dergbj%56|>*QFJmbfM0`Xo64ZbOE0@`1&Jty0M5rtg1W(2>9<~t1?`O*Xb-fE1s3k$>%iZwC6nMyyqg$MxOPZ^~1G1o#mYExraYIJ>2b&bAt#aaa2R$e1|$2*C2pX`d&6y$b7B=17up2)0FM}P zdfX!UL_h{C9B4}~cjY)M@AmkLb3grbZpGu>l!sXPjxAg7zklnN9Tl2jMazQH8OrZ} z{#ki*M(N=SR(tl%FFyX{v)?0y2sSUwHwOFU7xIFBVNXPZWO>N^prgUCMJkE1W|{qh z+pkTJBAxtg)xOb86I3qcoT!S%+(MpvvYfY9aLA4>#{~Y`^etp$|F?7Am{aN5y6(Z< z5A9mI&CmKAJ;8F3p3k~u=P2KAS^dq|Uw^w28E_%G`7*SAqk^ek*^c$|?X<-0)H&*0 zSU+8fWsB&{^w^~U%M|jW{l;`!z#kB@)RQcJ9TzZYKw4hXs-&hQ?y14L(dLu;Q=M;K zLI=;MoZF=AriHxr(`#7BW324tjxF2nzkeGRvf^;*43_`%&#c>wQZcRK`dj5^e*f7g zAAj*?>%6Hlv>O(Jk-=C|@2L~?do&H^$Od7bbBnnoT5s?m#fbTB>D-$C0TvX8`cgBZ zCE%#JN)+vkj|l=NLjr&!J1?I1oR5N*3p0%Bmd!`3Wowj#4jv0QcjcnyZw@}Pd|%wo z%)h^US^0{2zxW3eKhWNP@2Usvh@HIn(W?4qny}c-2&=z%{Kem%eu3zeAzww1sGfdOR$)90im1A21?CAfF8`$SY|kL_|1E@E7^(= z1rm z%_0&eEqiq2pFV(Nh_oip5 zAuF8MU_Y*0!H(Fk{uQi-pFuEw+8Kb@+B>i6+|(Il#2nmN5Q@@Se%I zZm_0R!A!qfXJHZRjZ&SaI?8F4SePV)Jr; zHNWZbAw}Llqi*$v1Dx#?&A;pSe`rKffap4}*;>(R>NNk_Qph5t6}K z=WYbqSp`XJ$utK*Z;l*z56QViJO|;c5Cq-^g+;WZFfX4YWb)7)+egcgQB3ta{jPs@ z=l=b&v-|(;Sj%@!n5U+?^MC=Jv-|bC-icEZ;tP5x1?&W$PpnWLOpLWhm?cAmH>$y9 zz@!>o564Ajc`-L{H(LPn%YjD`_6HK1h+vv2o)D+tUAn2TmhmO=3*+0ZWgMR;;{QVP ze6}W{CQ=lpcbG3R_zqN+_YO_GZ%%gqEO4P$pDbM0dxmbp*r^j+=daXNUavm%QbNK< zB~w4tJ&XKHi`Od}cKfi@ep4zJGXK=N&bE}+Sja$tS|3m|JH zSJgP^add3j92%;ohD)qfZAS}59iOm~X*u{8o!($Gc=Zupz2D~drs~sdXA1j+xF`t~u^9Z%NcL!!qMCQ=NU8W0`Y_tIo68 zwb{MNv&q{Kwad24{(|8}>l>{^T=x2 zxu@la-O4NXAEqPIY+?^XoB9~yMFo3qqoT%AU|+?t*jGd)2A$eEeYK1fNnCybos_A` zF{%e8bWCtaOzjSdkrSU0Vwif6+Z?j&(6N@w$1xM~#s+aR`JJGninuw#6T!amewRho zW(bll#=c$()TK41Bt*JR8eLsyX-ksVAoyIyUgpHics)+6wogPaU%->;>eDGRJwA|} znVQoD(;}vWlM-S)AzFb^8{RJp3nXfRxi@I)({)DIs;=9+Hg%=w?hp^^!lzDn;UOj7 zm2J#6Wk+O3o;RL1osT#ldA6%zI^w=@li$LU==8Vprw}Xf@UC)7-eY$?e(10w9w@0= zvF3oH4}FT9i#<}h@~||icIo-|IO9tB)qR|CRfw7UH>zJr-d9B-CGBH>Bf{aEh-nt*?R@B=ojV`ey@RAb z-{s59r+hQ$yGxfYU%qtdyV;ON6xLOF7XcOT;9i5wtQ5&7@qDw8BuofqGQkleS>&C5 zOPwt-wl3bd)4j!C62I2gm>icW==>3R3D(RQyAZJCB)5vra%O}}8}Nt_Tx0x}becZha$Iu#53>(l zxa~#PTjh+VmhX=Jl7MktQ(4Kr`bE(~rDf@TyhEh$AdJcemxc+pI)|X~Zo%c?XoSrc zx~V7DSX^x_u3=uk=g#B z5M)6yC4rm$z-bXzL{5S<|ZmcX%OmCY$;2D%GX4!;+w|8STBRYjEsU6)|p*+45 z@!X3wDG(Qk4Bro>N4n36atX+zwG_zejg5;r96irW>xD`8jS zuB5|phvN??98Nr(6cfx?mGO-G8P7A`XCj`7e8zXiea3UfdnV#czuH}wY z)$_zdLe(&QO3qY0Pn2U^Qz37?Z{^w!@_yHY??4bCwy7!zR> z?3M;MlJV=bVxzkxQfGItOoJvM3EAKw2pYbm+Cd;fT6WD%|dt9nst-X{kSzQCp^hwm6v*iYQ7 z-**50d!$p9Y^d^lWy|!9!zawRf5X-%XwLcIcOOCX_6fDY#0ZDhY;lY04Uu3sLZhKX zhOC1bqx7^j(;hHqbB7ZbdO+%Y;&gLy!5K5>*-^K+m{vtKk&7ur%kVtmsqjFBX<5~J z=s2hH_#=m^%R7?93vALcWpD1m%Hxk8W;3`bEeD$WSDdP-dh?=KP}%a}E89=~nte%Y ze*<}7@+!QCRX7GXQNAqv_39)ri2BBaadnsh_D zG2Pl-$gvj+gDr!t0JyNu73Nu&$V)U!wafHekg`OBToXE4I@!<4^5yLEQMj`+_Ophi z%5v;rWlM_q5gcxS7Gbce2-?$lXE!?+oNC(da=VQC4MsOUdYcHp*+6eg{w?9IU&kOr97=yxTvOk*eHJ@+yexCXGH#v!hW#$s0~D>s(A7Gq25T=yBh zfGL41&w`6j1H-ZwqAsGh+%C>ZU6L!5h=bUBz1`q6x=oR0ugm356*{prL#i>!lny~R z1))7CJpicxK2jgKkEV~d4~)@0;PdWb?r!Psyc1DFgM@)>pnjl%V!ei#3oV8ALPw#q z5J5m=gwbrYI9eJlkJgOVj@FIVk2Z`ojyFw$NIcFx+B*vxe2G{pmC7ZW5^agDL|XDMQT)SLXrw_(0hv%x=R2zy1TWVkGSn6EtTI#;fu*rDV zyA1npU^vZiT&X5e{-uja7n%M0y==`A<=h^)U$5;@UR%mOe+sr=oP}4!_lXt=w}I0} zmIU}2hO-R(H_$~CYYNUKPt|@mTvRv^Z$4tcP!b6aY(ene$~s>yVc9pn=~Ts z$AYtmPD`ZJ$Q@dB4rADlMQ$v!Q#Lpw;Kw@G-jSuApZ`oKw>q-GB+M|xdbrW`7RS|R zvS74&kR5=7WgRRG!BLv1n{1urDHFxIo$w~@Q73>K{oF+hH86rkQk zzeMgTzr|zo`Fws)6pB``OfgfU$iGgOfHl>YYEN~f`O^G>r~aDzj|L*gh_66J{%)@ti&N88R>22h%Ap;is9Z92b36|Pk4n>02u>F4uZN9 z_M_aq>){-KJ?X;;0Ryv`5@DYVt#BEWb?_~wY0|V{p{4+WGgIx8;31r#8LgS19jzUw zD>4?D=CM*(P)f9=x+TI=woJTRx?5hXS*%@dSY}>^kd#_mt$m4OiGNAdlITuYVoiQW zKFhD3umOv`I7q4Kz;%6H>#DM6cP-Wy^vv)7>T&7aFb}>ye&tlV?X3YldeNC13mf7U ztg%I?2quE@!q?P0>}#siHX7=oS;!TdGvEtsAe^f|iEoEZA$|9-kBugr4-t)3@0rO*mazc)P-kgkh>m0aj zP1*#5-|jJK;uB&FH722kd1_qM2?*eoQZ&h4!G%m2aFTdmz@|yzDPS;j&>t9f)CB%) za!4UKjeHQik$>c9Jp3lVBQ=!FizG1wq;Y9gP5Pw^UXJK}?TPu@diB~i|A}k82W%}W zU3<^0qOD(*@BD;`yB3!(-o?aEcFdc!Rr&mpdt(zfJgj`abrRk4Os~YSqd?@51j1L4 zg~5cc0J|8RO33L4u*=bd+L)gZ_SfN^rv|glUY{*a)<=0vKAR?9j;iqqHA%J_N3|Rc zc3`(*uoV2*?}_sSOmf>R^8hrwAGS$-;qd#auGfMb4aGL$EeV4HdNzM;DB*5z+iOG8 z6$WjYFl*h~*%P)5>T~V!`P+K;-Zua7Ykj_&xQ)di1%ojv%a^E&^%y0;JqaX1$UE@T*x^nBb&z5StQx57 zuPZ&j&QUQ$B9JOZ5HyqmCUiij0dj~~y9R&QM@gr4mwt>IWFMDKJ2JY$RZ}`;u}AvU z_Vu4Ger@~r-ltuJGvi{J-~W?kIG9oSQQtYBe7|Cyxw+iDYCgRK6E@*KF6B8I!-Jh7 zb$XP(a2sp%LadA|WM_?ewHI=Xju`*)g`Nzvqq(q{dIA3%F8dm{M9ED4i1ywoH1wLA?3r$ zmfwD<9I6nt1bDZWZTTC?4 zj1#hKfrOk`r&*BGlVhUkXHlN?NQ0DYfWY-iUvn%rU;(RtJT54sBIBI@Aa(fH*iV8`Qmpn)6>%4DL&u3 zE;wi@zCWLt-bq=#aPQuQiyDz*$8G>05UA}Jgk&}>I2~R_S7~HSq_foFjKPN;Vee+0 z&5jHivspL7sqq#Rgp6RQz??wB}#q)SfnI%7nc_^QKZLf)r0 zX-Gnh!y*jPio|6Q$bo;vWz>~W171TruEujf$GQMZ;V<8zxH;a^zgZ+{`u4g^*KbE3?maRJi83tK9QERi28- z3g5iwTGMjZO7|-FO3y0K^2j>hYB&BO*nGQtyJrxoH=IHf@&X7M=y(Ie^`-Nu{x3G~ zj543?9IKpWI_2}tV%jroA{+NOYuct<0xHO}gV_#c!RZQCRo-`NIr~RA;zO~O*Nrb% z?kA^;h&$pSJF3}@J3>dOEimBA3_;0*wFCyHX6v2<0lv#G43~Oo96ZQ&w;07AWr6-r z=T;P^-RLDz-kwS5D^v!1Xi;k@GB&qBwsZ<)RwY}^j;cQ8$^A512>}kdvO6~0THuz_ z0`8$bvxg>F&4!@~Q2}Xa&u(Hr^5Fw34UQW!$8q-vap#cFz!IB!yE&5wTbtM%oJ zMhlXg zCYx@-uGus0&dk1h>bggwV%}q;$IKdEy~?bu+cmp*eO#BjN3Go-&DZKpO}><`P2hIZ zx}ediMdrB8BN>oBuEMW4m&3few?3dfM-$3?U_P~B!bNda{CJfBU?bk0iXJGgf z?;)`$=+s|n-hq9hD|~anK0!{M@MOdh$?~M*h(UUykKu{j!%>g*IUI0n{b1x{2M$Zn zjwu+HFd}OhZXH}OO3S++yla0~73=jqkiWfkU+DtQmylZLB^xZR z^7dKD5xA;VB(+#@zi!Bt08L=CH6>BcZF}j{&tDk5Z{m2zqVlE7mo1rD?YX?0=@u-Q zKYzgj<=XDu$~EsV%LrxzDv zy@Yx(LL!Wf7{s<@!hhaVh_idGn$*}hc2deni#usOm6m!^(tBTwJn1^Ae<`XwD=oql zpCqRvo-IlCMu@RCd9si+Il?5u{fr}m3mYlwbUecU;r<4W0-~JHU3~|>l6OgL=8_Gq zDQuugJdlC5yaz20i8tXWmbP#ga7(x~E}vLt9X?`vzvCx&o+_U| zXXdo3mLJ3yTL#ImD(lCO&dpi7aM9|l@x_A%J{%LXS83QedgPc1GnLB(I|SbFh_Ds% zViM#M)b9*NBfLq^bOJ&o;gU0mR!Nv5YK*!m1~Y+qfyLwSlN>@QSoD`Z?XM%y+(X49 z^IYnOoT6NWuRkM6{g9Jtlq1TK8g_6jJJ|XqJ)h*lILL*?K(~twnnW3p1QM`^BecM- z;nFbB7+oCS3OE7>u0J9bx4g_A#_#>lv8d$Mv!I98&=Yu=dxGO(9g;kbkz_#{N#-Mm z8R)o2+car8x;l_jYRP!U$3efMXQmxdB-)*qP22EOzU@ng` zIHkxaF168-gOHZf9&!Z8h3XBYGBF^~&YL@8_`Hgt!6g};JHN1D{ z<%B`V^s>w493lD;25)i+)>ob8*X+WpzE|vLHS?m4CV={w1n)Gx#pVDS8Z@c5KOpr1 zBuIRSmIKaPyYl)aRs9RgV%(XbhK9m;$0byc8`*i#ZxZrUC`_=Uql5Zw8F=55ueh$V zHA7ntvzVfxGsllC*4Q73PrPSd-7&|-`R9fyBkKB1m`!!?O2w_>JD7jEFgBR!jIar* zk|FMu7}u*2uh`OFO?}07Hs*C{UI>0mNSdbgPXnTu3ciIoL+N>!=B>_}Pa82+A$EEb z09QvjBCNBAcxe5Q4L$CAuwus2<&j(F~= ziEqWeI$7U#O2=TA@6`*ViU50ULyd+stDuO4um_VL4>}+VxV*;0O&xSpv^x=AQy0A;NYOT z6aUNeuf*k|e@W#xl^6fUWX*-jYwu~Zue~RYbbNC1^tZH*r^SBkO=&n}Ip=Q-GbO^S z2Mi&lv?p+eB}}ngEpA&EoZcX%OFSw2|CCJZzH># zFD`%$af}kUPz#n4$m1do$v0xZV4u4ercdgeHY%y0UuI@-VV5zJ&E|CuM_yL+q@Fa# z9XR8=G@l}vqfanKCfGk9pb+ezM$cXm&T6#tkQ6QF4VN@Cj@fxr-_IX9+{PKAV=9fS7!3WPg^TGQq zKZ#8(qou6Syj3o2lQ~SXWYOvbqojwq+pMu_ z*c1t#ttmzw$s9eK&jINYF6moj4lZ#RP=gwRhk=PT?1GX(5r)c0{#7UC&vKGdJXR^L zVUyUTnzk>g5-_l2b!#-=+XK2cxsl(}@gfpkpk=98JlsX-$yvc61#>T~3>~XOv_?MU@ zCfnoUVny#2m_1HuXB^Y6g%&=x#brcoR z&H}_#OrfyaOWJWecp+)&(H`fZlE}aXcGF{mF}ESau^_sx)K8ecne}I3DBlh3ReBFwCa43;pc%9B@O^l zYEg$sD6>&QyuiyidA(IS`tyisxDliT^$}X2qIohg2nx+*kquCVz(zwN!SBNpv2;{eOh^CJ4G%8dM&U=kgOV~%C6ZyXXuvpmGbS|S9NHov!af(qODy#$;U@<2Axr9S9tsm?JGm=sCOb> zXxD#LsC@?7-_gNqQ?9ej%BuYeZJ(0oS(Q=Ep?c7$M7d1qOLDvKl50w^jeCIS!x zevc(tFF0Jb1cV+_)Q5_o6f*q~8Z98y(GGAT6eOfbGD1Tj3?&kM88(PQJg_-xt0vP37ZL92BFat z=nNB(WPmM=dJIbvti`}9H?V+#Qpf zrKiSP?($1_rI!0kGUGCn(%ljE_}C=1OPXDqVYfzTtw|}F{L1I>=B;LKRrbPlWWRcr zoG@h4L%*nhC?5QJd#4gr06Bl08U(|uo9 z`Qy*=iFv_O_5b=gA#ugTiAt4nYU|ZG&yRd*;shXm_22q^$%)bO#Yshkjj{CQF(s2H z6LLfCYhyRgSFRmU9#_T{6}>U=d^yt`KoacJ6UMzh7)t?q;5p}f-@AwQeIwuZo7-1v zZr{GDLp$xSI?f8UUCn*u<3nr%U9FT4wVodppM%a%s;?Adf{Et9+1XeJ(lL zMi?Dlz>oPI1kynOX_OoU1?Q3)b%cxP9LMP(7%L!k#F1cr6H`jFJFa~Szz%le(LK8#z5WqLWcujKv#(tGn8yNB9+Lv!X|C{Gut#K8XP=a$H3@Z& zq{Ki&oYdg3R>zeX>U0R;b2@zKS)DyXa#o_JcNZbxNDo9OY6JSrXooQe^#jfk*Z`S; zKfpf_3K51XFair-c^!?ELc*OQh#eeY$coI0=$xGgL?LHYUPGQ9dlo9zBYQMnUA87i z(**}A7eR)Ag0s6EZnwwdMTCB~C&$yp-qoJ#qZ^Z`4bg(c*;U^VecL|%E$se=@EwYj zlb+IVUV2ZR^3d_G-n;Or*?a%v-G)rwxAdDACL9^HK&~pQS~a@1Xx`)}kg^(#&hAcrJxgpFxj<2 z|AO}IWv%T?Zr?86+o3%_++W<=p*yH{*J;D;;%;gOFTi|sv512-f^Pc=&jbex znO)P9ecoudsjI%AE}hl6yY9(u&^NeuSfjVaZcX3U)7KD4*wW2f5)?J2IRDHEM!A`cm zRei+{NqlhlopYXbopqn}oKHWWaX#~W!TCN8+SxYyW<;xT`KC-h?l%))0BRo~sFH-P zHOp_oxZl^mcJaUd%*5@h7vIBE{Iff>rvu7jqZ{n0mla)UyUx%`-riz)NJS*^;GNqRC>#zrd_}Bk=4 zwtZjr{NKE!d~^9L1Q7WzB@Mg7JEW^}I)$b5dU%F&>3h>yzZB_!!SJWg{&?xD3k%<8 ze|}t9{vpEBS@sWCUxUdHI)h_qT~W1aN9(>-@bw<6 z?&rL={jA{Ysas+Ji3R1if9Sl=H)_Kubb zAytuGKgj1W{Ki4KUS1!PE!G74WTdCY#rkg+E*kt2a(-_WF0^S8_LwBd7ybxPMp8Nn z7(ZO$p&V+CQERXbaT3ZvXq7QQGx#qjXpD!HfeK#BT!^}DqZU=tplUQk8JuLeq(Qn9 zPgu>5kP1^54&AZaX3gqUMW42%y@Mc_;sgB~hCkS=H?kLs4+J+4m#PX1zWcIpNte03 zdi?Fn;mdQk4SjtH2qmxEQ+-a1c)11%4Y^$p19+d#7>_UJw1D^>;`fE^+v#kp?MrUo zF5cUro%USqU!k_Ev2C2DknNB0m*85VGs*KPa3(2_0(l6OMxBgpU2SS@u+M*SN3R_g9uu&OZ9@ zVLu2oQfPc-Ddn*9_F23gPr&{X{^%ht-*NrUvQm@BEJ&Y6TkNM#AqZPl^Zv0~OUw51S3F!aOZTgdKMxD><+s{Yb6uL7=kNbhoAN6e@ zpRSg_3C~~it=5fkASk1~PB-PoQeY5q<0R?N^!$C37t)+blD0Q z9s7emfyG30DB@Xd^^u*n`3)LRO=jBzMO3$~B&)>J> z_)0gz_r-g?uh}e!cyhb>Xs)9Dj66+vL!FWOzS>Qp`PcLLXV7zRyowmKt7dsM79*_p^3F_C(+oGc~V9Y$?JCvL!i0>%v%^a)`W z|8)s^lU7ruJjqn$vCK+Ihylu#aCaO^4YtSS4H|t$$7)@)D4G~%<(?Z|I4{ zF1#ks*?b#WbwO`pw9Uif);o1|hUz507h&S*(AvUEZb*X9Px%_p>=c)P(87Xm_^;FK?lw>ZCu3J*^ zS)J0rt%~ zX-(ie{P`%PmL=VRiRpt+H!&6ntdxueerk+_^LD0WM+zl|=m-h}b| zN$4Ct!uJ6%H)u!Ry*Lp3B>-(hOY6$6@iD8Faa!0((Eh$=u@Hy->CD@EDB~!0_XuNxSwKF7 zVOOhjIZUYFFv~%>)i$fssPO`+%9w0++T4gavYQ=Rk*W@atmfP)&y^gKldb~(2x!(& znmM2~Y^G!+$1Rb9qN8N#T5Rgg+?95Iy1)>&^vV+301Ng%d`NKNkR%|4l0C^$c8upq(gwk zb9XjHm(y%cjCPTJ5XyJFxd2!QbRaZc2O^%?QOM zH*zAtS2#wry2qLdzQ-Yc!|$+bO zCjP)_0>9BZel2(_`f(X)E?Y(XjE_$`8v21px09K*_2lCf?>>P3!Wh=Tc~6N_1YgfT zAxVq~+PpS*ETWt>$tgOAM4|(1Ua;JfuQnJRYf0A0DKJTu`tEY96(kfR5fauL%TquI z+$DnU_8(9l!Qm9t{iVI-8kSFW?!sb87La_TUf2=whtj;co{}?7Y{k&;_O)Ck#QGr% zr%tNvlD+Hn8}FU2ly6kNxoENSiSkx+1JLUe0cJmJ$j4HUCI3aa@$cpzC{K#`w>%WK zz)r&apd*l}jSW}<9Fw8~#{h?m(!4RRODDYDtRxM%b=J5PI4BZ1@=RlZU}qo;AO)OD z0+8LG=ZRAfbF?&+pQwqfi2&LOFk;h7(pRT9rJqd)$U_*9on8;6g*#xM(EGK&{5iJF zu?geaKu|I=c;H9~(_qf*KR5pn0z&ECbFKKtSHs{2*iXo++8FgtXNCMu@5=X|+pkpJ zk?T^hs_=HTKbPgnTLU_3*I$+8Z_}S-=X-9`pYFDOsHILa20cL(mf`}f1y9h!HHoaG z_*nm%=+%*HyiPY5f_;`y6}#Gdw?Qx3jhYybLx_s7`CK+foJ}xjtdelE|s%NK*Z0ngsrt#%}g?%GEC^A1l8HPH2*R=6Wv5sg7pP zu(k3jst0mq`Eocs&k#mFs;0aR@7u2{*cLVmH7)LKUmt`PPPHko109(ltK=dEE!*lg z2ePzLZXv7A?B3%L8Y0d6a3>AuWAww!Dh80RA`R1 z;b)y)dw$5k^J`ViihJH2G~|uCe=-)r(rSgTU@wNuyt8JOgrF>Dmz2zI1=j-2RU@7{ zKg7Ej=Ur-?2zZP1zP62bO`xTt{*ZToEzWt@I?kg){grWq$w2LpyhskhZ?uk2`W^Za z?~*^_`jge5ID~7SN8KupR*5sr~rj{+K_Wf_dWXVLmi}-e24l>hFd>9k|xG zU+c))iYMpuae82UU=QFfsQZFi;m_vnZrr>5xP1ft`1(A?$LClD+7TEZJ<(n#Z@KXe zcp&eOGZ`Ul54x>3v)*R(;zIV=k>+L8=tL?9M_w%YANel21RV}-l^f;H4K^JLm3Y9x z!2(qrZ63_f3Z{K?l>do1AHOHtz_f3U@&}{zl>q7BfbWbi1f~r`8(s3DPzC5oPzC|2 z-2`8F2w0oE;=lui-!dUn2$1-@X9d`=8f_9uz5GWr}1HT3YFF(rr!-tG^)i%n{UQe12ETrxFlMfpGK+`M8*Npk#=bAnp z&U`QaT--v^m3C(yIxKHj`>QsoKk|KS*I)A~*5RR^r}5m5{fP&mA3tMn^5?4K(-}rj z;z2R=0Isugyg?rj)@;r!2lE9S-GE;O$r1n?99R%e;x0ia>?MG>a@ai$7LrBvjUtyW zRG>jzcmTNKwJF+RYd8_?6UNr~O$lnzh<&0MvaedfuEN-YYW|5%>tQF_K?`!c!Sr-h z@8!UPzaKWTdk>QD+VDK1$d*Rpc1LT4lWIHP29LAVV(|;8oMFqw?+8ptG2e>JiEX+O z#2!-ZH_9`C6v7fQ^CU;N{BF4kBs3ufR)<_xc=-u#%*6%6I|7JjcJOEb@Nol zUQ_mL%`7qLv&A)|M(;U0a~4%^YZ-a^Efg6?(E56X>b#8`i$ZUjipf)7WRr#`JEl)N zxqj3rXXMOTsOMH8{?Mn-y$@&fzAiK#eS8#jZqOI*IGUSXE3NFXPIg3>2+U0>wvgj6 z9XJKC-dysanRj*Cb?`JHdn*(m%nl75vSn-GkRgRzw+tC7jxJYx-Nugtl22j-+grXZ1$HQwu5n*>4p2=wgqdpF<7=iz+LX{* z1H*unnfTVFawN4YZnu)Np*wz*}~ktN&l_R^B}bYMnM8QzZ)nAEfz zkB>u;wGjdZF12AvwcmgKA|dQm>1`F2HN5xI^}Df-9HWS0$FS#j1TAqXsF4?{mKSUP zm~vsS0Ou-bqk?+nv7wTBB@`n9x7ZJ;o#&#Wjvxv(tR^{BXId>muGQ7_WBF>yJmoIs zH+9OD_9gS=LS8aY`SMxikTN0ImrXi#0@cWeKx`g|l6mUfbOqXCy5&)LoBV~?#?(A} zKGM#Rsdoo$FFrj)U%q z*YvSdi>gzq7QXpkEAZ#P9-lv-%J|Kvk~iBLP{;4WpW-7rO}w+G-2uqMtgO1V-=3O0 z`H7=c)bEL-qeo$im?PwR%}Gr(X>~mHOr0N}7bm~A`RUqCkk2oSSi2Q}zMWIrrK@9X z&y|IJ`}LmDC1dQ=%=Ajnpuy`N96o&5@Gyw&p1lCE-LpH= z_;KZ-ZsW$UIJa^opp3_l>!$e1*{9`a=FC~R@RN_Lt4rs||El=;EU{2muJD*3HqUShvJl-yav&Imw$;-!gU`PQ@nV5r0s$fOLRI&TC+^N#6ehI0wW}WLc zZ{EWB=EA~L1BXZ{BW7LRx@F7O%inI@x}}fu=1%a;;jGi}VY{PzUAk{tGoZiS5%-#J za_6ircl4VPKWxt)*1Tu$-aU%Er@wThwpQt0yLfRed*eIMCh<(Qz9aUSmY*+s_>;CE zSCf0a_X_c}60u?hyWBE%g?!@lX*T>lvHB(U{Y&fzEIBrS)|~69;1dbt8=;rbB@ul? z(W7f2wA6Gwd$9z2Ak~!+I)c);6F65V`|WGIN=2sf^B2ypzkAQ_)vIPM1pU9VqW)TQ z(#rZD|5d+Ivwqa*N8YQP&#O#r-?ng8*DhVV;wWx=ZZL~@@QIc#%EygIjx^%E>fsNh zI?uJVe7mIsd>-Tw4ny3Y9M+2fn;J*7LD z`j79wSH5}cWaH5vndQ7@mvZgy)tm3z+)(lB#~wR=UqShAKYj1Xi~jiMHXivc){@=G zgAIpjJK;3C63NIycUOR4S>)s#In3AfNH>_^F+*tieGBz@jUBHPKZZzyvo;AuE}}2Hdvo*ufF5rmxz3m}B6$1YWeISm(o)DjR2Vk1q30?E<5X){UNHJgO zsq|&rlxwW}r{X8=D^e&~$~EP`uYJuXD~q1LZ~i}N4Tj#hrnAHLiEy|uuyN>afFTOo z5RzQ5DIp#bK2NYq(cW)6 zbJ{^6=jYFv`+@S(x}H7R+RD>==azNu5nQwO;i{5ZL;EKs&lz`&nO+@S7(z*ItG(&K zgfS6XQ#CzX{uVCh$fHeL-ojoW=7D1tL%MF= zbv|bT%jI*wNvt4g@-cSiX7pU3>du<^jFQ#ZA1b7U70sy?%3ioA%J}b&$J{^E#=-V0s1P&6 zh4aE<2AR3}8-#$Zc#N9pPDxD4>lq!REe1QyuIkmR>yDm}volMT>ix=B%4Gm!eT|_x$xu@-erc|%|JRT8RpuC8DUwyRRf<&a@X8~NESauu z@e8`fpLkHYOfu?Su#=D0dZ4#=y-s$GH#9%8570gv@^l8b;f-zK?ecq|eX8epMjweg zKZ9&>?PuRLf**hmkj$i}hUJnDMH<_^&D(o>^%^qx_D1;S)bf}e zUTD1E>iGEI*E=C!^bdd8m1DDCKq<`73*NSVBQSck1+Y)^)n@AL8pe_(XM--nYo~}( zDr6ybg)kVewR@#GS$zwie3g7v6Q}kyo3|h`P&E&4-wudl<^ zniUo^BtOKD=CWqiwORa*y>a=n()~N)tKy9yR*K^PeTdH!n5x6eyq+fUXlyTz2Nn-M zA=R0y@1p$Kx^-%4&6e$_NtvH@vW8`$`sT=yRDM%)p?Z;8W)m!xYwtq>j+Ug+V^D>Y zo~gzJYF@;9!Cr*`zK)8ybPIEhVSM{qwRDvUcU00}m^tgo{)1Zeq7Ldhcx4lzCAaD8 zZ2aMA({Y27&Ib3)U)t;K96*`Rvs!tH*VjxyOc|sky5*3<0OJO?ai~=;aYVuZ2QHX6 z-~)q!9uBj=^_4z*jaQ}?rUim%i zD#3UM4|;_4&WP~-@$?fjXZG!pf9IX^Hx3>&cFdFN+_-HV^xLJ4&tvcU&~B-dYsLS# z&D5k5v(C+ZYjI8geh)pcF)OQQkHxk3$`!L;9RJ%t_3pK4{hplczI|8TH+Ya-(IYc+ zV$nlYg9c}3rKff3KQ=9G@!W6IGY>yd)~{b?R+o&7L6fsP_vo>d+knP_o@2QUD4O<= z;yEO@){D3lNSCfelGY$cr)p3lBNq%!l(Zo;K&~gGpya2z5|f~N(OGzQKg+jT^?99{ zKCYBWb(81Ydt^)U2jAp+?8R_7-HavN z=6TFa&*^M~uHvI9BQOL5!bq1~{K4<7V{_^Zlox5`Lv=tuCsAfYmB_ko%KUZuWQ zh^6G`YK%jM3gVXs7X*78Z2nbIOxCVqbZ;>clLa%OWHngaRdYc1K4 zyZ_AZ7u^;y9rsQq?wvJqU$js9mwF#z_pq+eHMIfE1@a>EXi*i8>+X&_!~j@E0_6qJ z)v4kMVQEk$I3Z8mGU39eP3+XBO&=7A7G?jv9~JaiF#CpZ|MnZgtg1Wj{ODd~KQIC2 zD!;m_Tt5256GvIZf3d0aDl0cAPhEXz-GZ{7`*Z)=DFI#Ik zf#+Y_ye@jtxM%+GA%A=he|Pc?XlLV@(2zoMy~-#Jtx~$GS~AH@WYtzMZtcCCUF*^_ z#@?N)%q5Y(t#n+7H5azb$9s{VK{G<`C8Ur%&P7DLM=W8@^a02Lv1Nl=av==nBwbL3 zw}%T&X#*2Awr`gTu3rp;hfWJaimng0@HOQ=2)rN843(3E%*d(CLqxBZm1BQU-esHF za$a0cnau_(Gn5HbVouyxFIuU}T+5mImZx~3xi)-Kbv&H>@OU_ie7vzNVb`}Tz!5~1 zH$PSWpj;&wQC3&M))JDaQVwySz(mzLSwk>4y18NB4NZ)8nQrfb89RUp*DL*XG0T^o{jPpxbaVk5 z@aW;v*-yPzHiu}8Vidy9B)-8eNY-_dIgUhzC{G zDnD)6I;ZU9D5XIvU(xapHc1*kr|h+-W|tm*R5?`;9lf&tyR*xd$LQE^>y^WwDkV^A z@JywfJOr_Y^q&9so>^9bXAWhL-~O3j7Ac>cRL1aUuEjGuH65z%RRjA?i(SmloUyD$6hCHwSU=r?1PiXo?VCc*$f?R z7UWc%Qx zzw5uSN1tG{RoEj5pM-|E>Y(Pr&lzaI-GiS=E{Zo`YuQfnVzRaT-JNLPi!QLM5O-2* z5P0ZD=3q^1y|R_n9Aq{0p=>=!AJVvk>@y|);Cly^c=p*r+BdP?vvuI)o}@gNI)j4; z8yg!n1=nZG4~KnhrY9(Fmi3|4Km-Nu0n86~j}dYFXb*O12fr|w)OHR0{pdef?XMK+ zac3faB15wW|7UF*;ec}ece;~7VY@PcBTQM`YtQe?QOj)RF)JET#J zQ>ql`Q9p~u>CleYGVO<<@iBAsA4C0b!4g?5f6jf!#P2rq_>Te9TigVdg~mrTi1-1I z=g34m;$5T%d4E0HH}L-dP3__)ind07u3Pi|kmRYqW^n8H?Q-$NO>@=pZ_fK(9m?-k zV+P2VJxQGphNtm)e|;`|`rCxJxbGelMLX}Wi=g%!o0UuOmgDzY`%fVmhtKDJ`mNUf zz0v>v8(VJaKdH6nH1P|suQ0*4*>{NV%#80wKLo4r=pNB(7e>vNMjHgDo0m;sxiA(zH>tvMHD%I~! zR|c?CWSD;Q&GpJV&nU-~a<-dIUd&P zaAotuKM|F5bqMhb8+^t;WxfVJI)jze=hmj z5Ers%KDCaeWkWmf-#*@wJ=NB~E#8tn9qLasHbk9|JdD+X*DeqBha3W4mP!#Mpj6me zsh#Hx;LO`oDOaG~na6vXp5VNxR|lSmc4HB5pWdMz&jqifnCBYxb#_NwazxoXfN~hT-40nv}XlPV!?Ec`NhmSh#^4vicukeKSFPN~6krLW z8q7-$JXfw7v4WG*4zmbLM)q?OXY<9=iHag74-TOi#lH5(VD9KZL^3dEa+tvPm+T?E5~JL?Ww55J7|l5lRF}Bm@%#v4`51_M#|N zYP+>mRW-C#T18u}F1lV_Zd-b_sJ5!yt6F4EexK*O?#{A8B1`s$jSY`>IU9HrEr#O=$_lr1V^Ynei^IJr_h}$N9K{`lUT|AGpm3I0ro?oVXBA&;M zKxbaPpYi;0B||*#a36>`jYX8T3;j4gPjOHu9G_S1;9CFJ#q({O`WJi+`Y#pz=ZSWl zVJ*IL(F5Wo)&3oxZ~PjE0(p?IKFj3gqJQuWnAdW&mlN$!a~Vj-<=&!yHQy2W7%rj+ zE5I+FN6tZ=v`EBposaoDU435kPoBVl-|>01f5+!FeuvgaKV3YJ z8xC-4{EmP>PxKFjL%aIC3jh5+l4}2s&mZ6^j{d-i*7~RDrdqbl!dMKzEN91|9n6-iR?B9vxTsVMcMc!ZAY=$CZ9M3Hw0%tj4)6)GrqM zhFb^iba!8Gad+nnSP`27U)BGRzQ{4&!Ye?fy$=c^Ibl0;vUT*xJqx6#dWWbHpdo01 z)ld$dq_fVK?C5%k=n>Etz*MEV#R-26zobkSuBk`J*8tlhG{3NPk#_^#N{Bso<)eh+ zm;Y+#m5!o1tQ4qM_#;sC`avDc+&nzt71UIgJ6YBJN?!KJZ70(8_90bpPMWJr0zw+# z8zc&K)4oZ%BAm?<;7ow9YJ2M;h)1P{o4oakk7q=>isu_RV}{#&+) z|HQ`He&f!p7iwONegy@+I`B384`u0uXQ!XOJeoiK?&IhF!)w{f@6_BQhr+K&l*Z)_O{cy^@XKzAuf4(FH)b~ zz5d+qC5639yADJ@MaWA@^?e}kKB%AO+B@v3)=#)|Qw<)`zRo9+|UxA^}10q#lC zOD*pQ`1`Bz?&tL2Xh6?Ey@2PGOGk}&Kc@^9?Vvhn7xY+@$5x4U2YRf@F9(GD;y{nZ z^BZ+5TR(5O0=l@daUXvX<2pQV9EtWNpx?DIOUt{o0?)YSiJqlnLgnUtzA2I zp!vC9VciEFp-y*WjA1EcAfqk?xT}q$NkYwRr3g{;4S|=+{o^aoim~~25wFG761!9= zycIAt8YC=Rvyp3|UGIOf_Ruf~*bZDHxM=>LKnG`Yu%_KI0*HQdpuXvX25ab(zv3lLQ)vF#q z+AOfPsE14LnD77V{I}n~cS@^?3u6O{;yRGr7Ou@ilK>|lYV4!Q(_H~)vrGW@kksT5 zM<;3%c{`CDCFE@NsxY_6J<}vq3n2f5tLQUn2PMgN&7lHr(?p~^JG|_Cb#-R5$30n} z)vCDaWx>zbtNaqoO5?4@Gx~3kGtnQK=`i9pfK2Hu8AY5lmmWESLGX_-T3C7%lUXK< zkliZNnQh-Twx^J%vH?P7;qCbi*WLVDN;VrBUGQq=It7ny-Oja+X zvP)Nek&tO|-hh90iAqPVMEF^ctDR9ebE-#R!TwRd29dsQ+|D5B8hE z09WfLOS`|f_G_?Co&4MQ)ODZCZ5i>l9VQRBRqsDM??-8X(UAS)6}ac)u7WC$V!2h@ zk)y*Ksb>k7rfME<6e=a{hexx>5=u^QwgVz2T55a`YrPt4?MrgBDB;d5Oto~18Kp8) zG73z{AH%q2`)q(T4~9sQ~hD2iPDq4o^)EtqC}Ym`-Yvho7!+>|9kOX3wyF(`{0nm4sgd$s_eR^+o+_0 zA>jf3|Fxtq%x6)2+yEQQgm->zlYyzfqJ;o`(p+jsHjiY)rry@L9 z%VU{nPlbX-BZ|g`J`~--P~L569*b3zStgP~xT1-Vl9DA?ZM#meaf1ps zKRKvS9cbFrQ*Tb|AF$0z_oggxk>YlIK{ev+ytnWzR2^DWn-JMlF%x^Z4xCwlY7r`$ zUi|*@j?E|Drbr$9?B29#w}{f&gCHF?fFHIp<9vBu^|7PNwtM-%g%BON^x>6{%%}LA zXP@GC=i_V+g^W58Fa&89OTh&azYT7XtTvul0TTrSA}hP6Z+M!Cg`EVUl(SE=KXFp{ zUE3=D`+M&xE)Az%WADF44m7s!kye)u%zXYF>beKB!SmU!3J zKK&i_Vq!#8vQC^aSFpi?j8Y0XBBD5n<_K9mjYO%;klGB!WMdQ=T>a$v#yqos<(na8 zVFSCZVZPs|Cxw-Tj_JKf&)Y3b?}HqX8{&R_Bma>o%YyZu_rkv!APV53-$7CrxVAlPG3wmxnr%E$dPgp`vp%L*rH>|V z8`+`5$nDi5Rt@j$X9|f+$V%&(WZ3m_PgYkC=e=TiTHnXv2@=$!YGdi09ottiX~2kQ zK3}$Q>A8I)dQLy^#;mFX3#xi01$ip^r>9TLk$%g@<} zORVfI6<+AF_5T*UgV3@%unvNFYCH|WY}TR#M0;#%Ggs5h%dqkFZ0Lxhhm2(zEAyUz zqnVqT)6Bx`t-kl|k~t;i--JcIwp)b(_lxjPGYbDSyZ0GT=RVgo`>u(zjzBZZrnPG~ z@qe;!`XsgXPa_}f-{HC@N01Ymti}yVT$vg+dkYY0us~q&yBBC+VR^12OO{W)m)jaZ0Us8 zYpMwi;ll+V6MYHrFoZ^-gGrW7i$FrZh;;GW2r?yuh{zFUw&Wz->kl%++*MMCgLB#K zx%=4R)pJqD?)iHsv-!??IGV71%{i%fH8Q*?9rzDoED_U+dnK0ERFtSqZZ#4p6@$XS zLS_}!3x#1&)T_PS@M$rXtSx4Hi>Xj}@yB=h*5s_N&1GxZOn&&8w+>0GtY1}Ee?|XW z8=iPLt|L?H*UFnYwp3DenpjiHTcCqsS-oAL$&dxHr2G`*gcyay0`?1AXfQ0X?9E>v zS-x_5ZJ*quyV=ODJF5?Uavixt`}JKmc`=g7jwl&=zIM9w&x66Sl!okz#yzopE>`DjX(S6wam z`nz{Ym9n>*N7*@lfh%I|2yS1pOUKwv@;=Zp5En?2g(VgwMRoeKyHK)^_>+{*OL&dW zoB!wA6y%8z&@~hWwgw@fd-qn$MF{XpNZk4qq9fl=NR$lC0bpatsAIz?PB{v>$0DqA zMloiS)2aYP4WYpS^|6N``pB(dp-6%j-m3^UZRMxH9HUd^cX~T|ukN+Vl~;FtGMdY61u{XVAiXgt+y9?bN-ihE|Z??7P-_5Zf-v0mFd@&}@rDMYqO+8-n#VxQ^2dKv9_X2&}v1+g-! z7ikWhxhqDC@WT1?fOrTmgm}%4Aqa5|1t70M5aRg7S>58}%fE^6G>t0VeKod4oMAg_ z!<&CJROuXaXR7|w>_s3VoJw6tiye=;vY3dD;DC&VD*d8GH@|1TYgYgC z#;lof8^Zbzn6rJ*AloS^+jISbm3e(79ShpHfuC*QTQ(OLzc9ah_T))lI$4jMD1#+N zqpQv1!IWm_vo-1}tJm?A zB#YCRlP7i0p8rB|F@h@__}Psc4!v4ddg2(ynhhV)E|^<9X`-2*w$K(M2sd2%Ay%mp zF&P82(f!~*Dey_JO3w(hNKG;Pmfv4aD4SY7#I>;e@NMRE0^c=@RxocfQZFWgO;wZUO}MEoWc%ti<{KF+X4bNB4%6HC7Ini z@HfUjR9ZHYc?D+@-Z0(R#(&CZlqA?mYoR%6=j0OR6oJa~{(-u7?K>fvdVamv;QV^u zK_#FOIzb~muO%dZn+07Gq9}diz2}-tF-llO@MAG9o1EG_rmTu7??iEwNK=ArI(RJM z8bT?vU8f|KI~7J`Cxs{JM;$3oDon~wlBb%R@s#!)nnT>SjxK-pbCyo_P8{|fO`)-C zOMgPTuaVpP_e&kNX8JytiPnkZ3;$I&mrdw1yC|(nZm)*Lo*!QF%*hGevZfDADzaAn z813aXaNrXw2BZa=qk7W($7}P4HdT}Nnver~MZ6Ud)IuAmMG~pLl~iqPKIn8~weuSE-b zMwtWC2CR5uU?KDJivF=GHMJ;d;Pk9+6HY#(?TKMO_N12vlchPHOG6o`>TzsHS|`>- zfa%)=rt0-U*#00WM;LET%js2Qjqev9l{T!lY_J!H@uASX9_b%hyo78p`qpjQLZMY5W)#C7!}RE$Y_5b`IYM!|JRyz>()T2`LJ-hDJOrM zO`1@^iV@Eu;{3@$8iD>&9-IEJcS*r1y?bxU<$3erbu+&FmD7)rgmCvfOIzbWe6yCQ z^iKE-kq;|)Az^cAp~!89&~YA8lys`TfxLhm0g^(Rz=eN)!-&gwT93~o>;MWnLOe!W z>@)0qYNC8ka0PIe!hI-Is5+sS!v!gC_F27p-B5{odxUrO~S232UR-f!(vbn9gwnh9@M+4ZT15AS2(L@ZFYqXJ-%SP+i@zuxi`s;m2J4EzOEm%#TtJL;=3w&E)rX$h%gV|qI2a`nZP)lI z)?lr9?@KA8sq|s>-OW~s`i0+<$lIHMH-b7g2iDPzDngM-OnU&@vlo*Ki(qTn&6OQ$ zC^8H%t+))?Ix#){{V!|?14eyRE&jL_Tbr?*-~fL21CImUWfL6sB?o!d7}2iae*Ono zAqy zvItLnaL?Rf5KO=>K36e{l}V$jZJjByx^%eq9m=`us_TUu2gpz)lJ2@d9J3&ckv`E4 zHlU;iCmW|cO~D0Y6_xp1Y=*ocpmEw z%cu6N#%|kr7~*zK8uyf}vOMe;@_trilexXWzrI{bP9EWHhV2FkSbRDpDxx94m!dx& zmL#XPmVy=i`YKYUwc(szb}G!rJ5u<r$CnmR zv>#u_*tKDq80Xflw|w$4(LbV2-@42?OJl4J;gyx&F*a=i&57c$D$uVVPB1}#A4i|+ zc@5pP;@9>2t#_8T;L2rZbssa4Cwv_GBgvig()NgP>Khhs@DGA2+!vf7cNBpHRhqB3 zT!9#k1Jmx0(NNK1_-Xuli+uRs1&>SDD`-Sz0JhuIaY&AMZS_@g053%RDTq0ghS zu@3*Yu{8qqm!l)z)gJf#7sKD(0@GQ8bxIxWEBSdJ!~FxENiQyD!Ley1LCxg}bP+-j ziZN+YqxP1r4OOi5von8Q1`05BypYw%bndh>F;Rr3YldWmrVkz*oLV$^@-sz6Y0XqCpzbO-!7#uxHO!fGN?{uRGG*zImmiUgS^P*U?FZ10Eu(aP6>6_Vszd zxslhYW9`&d25BjfFtyb|lB{p3gJDgO;hUfksZ(2=ErO#r_z4^EN%(!Js(C_NSPl@p zpX}3n?_M@a>av%e**n+v2$Ud2)eXN%d*PvKkxOjxRvb=YmlnBroDg{oz9fcxp(B72 zas=oYlnM;w770d>@dzHVNXJ>w-Nkm$CA(mgG(_LKfPYoco5fJdoz?6!O23mkqw?_K z${Fm|yVe;qtPK}8Z$X+J{Nu`&&4OlCK|kvUnB9cexFAn{s5F<3QtT1oI=UU(F?dCQ zFjz1n`kk6Ay?-;SGHOpnA8KO0Y;)-T`Kl^30{E}hDly~2s zQX?^ExyZSpKMQQQvuf3^i}}%cd`BHGUkngbe%la-{qq(u*bUYcRj&Tsm6@Ub@rGV3 zR%9lXz?aFpr;RLI%0IjC9skGDvXRq9uu8V@`q`PA0|PhDJbRrjkW6jg{pR$WHz{cU z=9{O#dAF^U%#ZPpKjN2H4IaFT1%1Riu|61I*x2=vg1&|j$FJ9LM+CKN8<>Rch%rSX zwp~?W%i)yuU5#n4#|`uo3qUkd*@jI|vV26iZwTS%LC&Mpcy?AX89HPB5kX zI|aC7Y|sG2sQv@%5B|rmDG*K@l*pZWL&f$JTIsyLpG(Pr==Q=w5D3W2J`=^-(~p^?03YA@@G~gjcVC1&ip073yVS%aLo~X8sI83 zOGa7^e2Y}Aft=nvkzFu_6b*4I?bf45-ht7h6czk7j&pkJDh?U!38Ymj*{7cMV#@6* z_(fHh-%KK#2o8>ms>bh|Cv^DGn$m)GN>X+BSh0p_NNa-Z8#SqEv|@r5i+;&c($ue) zUGdpGldjB5e$Y5fZbda1d;b1mks)q(3$ihueZ{UcfdM$cx5`rxN7~Fm+|Qwc6+Dc2 z?P*2ri9vC(wxH%jQ(E>3hF7JKu-s~dF@%T{qJ6zu&0vr1L+Y>vyjI(7GS&)SM|n=* zEy*<~A7(}$az|!QG(eajEC4ZfbFFkIDLZ=ATK>b{#KPWlpSAt@%rjE?Q_t4%wC5*Q zjjMiM@^|mjm;Yd8MHOB6JdoPK)(tDG9_~1HP+uIf3l)*5Gg5}eGec>n^jB7UpN^M)f9{q1ndOp1%xQ6dMU)F);`$5Aim@B^-uK5 z`SF8s6>h_XZLQl9Nj5=&k_jg( zs87kTK#DU;q6HSdrA7nJNC%IJTwQE1CGV1t^M_k0IFJU)A=&67+Sxs#`WEZWNSr`@ zH>0=l{`3i~`+FisLi-N(90`lV-!o;!4=-|P887#lJgBSCX`4Jt=PheEHo34##XTSR zNc@jO-Ui&%7&_d?Tg`!CiH%4~meL=v_GVxT!Of9H>&T3n!l zR1Lnf?b|?V6>P)A;d5m+hHvebSH|k9AMVq2)R`;FY4VhfFj`t>sKx9T2auUec8I@ zxqY6cvqyan6}ceLCu9)ETo7PT;37*%mx@ft$5;v9+oDS-saQXLr25_`E&G&Izj@G* zP?FY|s`qlZ^SWY{1Q$gK6R0MoYP69)hwy*!Am5TaXi)W`A%pjVttw;R?cB*D5)cV_T_p=sblYd17>G2FkAz4^N zeQ*kFhAo_iM3xPlcw|XX*7D1n7Y{AWd)-?7YTu%V7H{E~+3U0BmW*JrB_rm}WUtG| zv;WvO@twZN2+=G5&B@#T$cFmEw)xl;{5!U#p<^}SYU3ScHpY#_xGmD8fxSz$OLvqC zf#|eFdQD_Ti!014Ev-goG$Ci(lcvcR)I?~NeUS*wSUUKiCV6KuX{nki4QKN%^dJi` zo;&3wpv6tLr`3`cuoZK--+ul%|9l6ZI*YAPEDgPoDC-+r3=(A>SN9#h4~7%?zBlfB zrd9u@F0?QtCOh#PH@5HvGg;PVW?;t0S>`MrcHla{E_buVQ$G!tNYWQSWhFfYENlat2> zy+T96;9f&}O}e1pwK)KzNOeK!7{o6kSZwDZUjMvgc@+~d9A;nd;l~bIStlu^riK@@ zeKj?H>)5mFr_^^W^|J;*Jq@d)~seD z_m0OMSJapz-$egvEph*jC+yQ@)he6U&egevg5G^zf6uqbT z3B1N-&@>}W*VV$=K; zfDTvcr zJ;*<3N|wh!FEH)5KlTrOy}ZnG@#K=F=pP;)b!R>~`%$}gUsjBnbK=dpw)O(Q9Qq_? z#-w^tINYYf^DXKMWRCl~H27L=%UX0QA)12e*5jUXhAaSi(%ce2Rm%#j6|^sPeY168 zsOsw@_z~qL&A>2c$=yqf=h_($z}uXUtqt%s9)oFC1kh7l2xe3(CwB$}B76B^4vv}H zB+y&wi^%ZAj*;OxrQwm&epl0#-ENtvZSs_g2I;4ck@(j!AtxfjwxM;JGRkudr#gp( zZTwHqDZ3C>5p!v;q9T;*{%ZSJkNt?ycK)QJ?NosPq6vQ6mQzuVV zZ9BEnXSOXrjJJ|$=iEi9Dwlo4KAcom0qf3$@=5F?+sFL}tQxn*KVVJyY6#cjj?upf zxsqfG;&>oE5|i4>zTnpiY5vmMFni*g2l5toD|H%DG;-))*d2mbYg;vYDGw)IZZ6i~ zC$R<)pETdLWYxEAHX`%dJc5-WSTp$$dXA_LgnwWj@$;$4vi;5w#8Uco9V^`{9kN|H zB=PLcJnRUH%T3%YxjiX4tzb`D?=i6L{dgsGT+wjbp)*3XHyZsyXKat9B6cL%Gz8W7 zQckcNg3-4&oKSs<$=_Z0F0>UkX#s;s+d^s&)m;HjcdhwZAK9+<(RhSZCFKPiItNF9 z!D+3er54@`{0-sJ?vNX~pp}El;|{5mPhnp6a|(TdI~R|9Sv8>IN^2 zcG!FJ5w_b4L4e@J247R5bae=U2tW#$W*@<$a+R$L`G4+x-0Up~TctDyd2E_u9%QGX z4V#e?sAa%I!wuVfIa8cXo@TrQIMQ@I0Y$S`nh2A_$p2M89pebR{*<^iCXlw|8HWfs zdF)?g7P7h8qv)FS*DZlnHQ5hJI@H`5PWN zH*5=Kz(!{uJYGEFg2+PHg13jR3y?p^rCyVE{m8=)DA~;TOO^8$mu+75NZl8&4|%oH zs;rzbeO7$Jvh^E#<**+=uq|tm1TH4+0a9-JcB+)ScFMDYE@kL@;?BrH{AV>M;_8AFFi~gfFt5l>hA^ zNJy(G`5Os1k2q-p*{Fn$-89*lVy=EL^b|2y8IZ<=e;8t0TdZ;t1%MDqBUdcVS_LKu zK4fZC8ZEGVq1Sfied*zb>Lw9ubM16EtW}<^wCC54p3i(>GOl}j-fVtv$I{)Sm^7(Q zTB}N51n%GsKT_KL#Dpeyu?g@d8$Mj5-osjpyfeVt9;yc`31oEqiR`ps8_0Q9lOkJ) z`Vd;03y?`a?B(rRR|^>;J%&=GEozd*wHGmN(1tg_jt8B%UAUjR?vd9gPX6m6Wdd*Y z!vQbZZ~kwnDrOVGcC&l`4NDs~P%0#A#(zUjwQvUZ7kC*07(7WrqC+hRW|X>a5p;&x zZjCrFK zJ{UGWU{q^84IB?t3yrY)>w8o7SV=VsNyvKTpqi~4@L`r53XMN!Cx+*!5bX=i=#Hf7wT@4}Ok zdrnGD8Oz_U3<=FIY}dY0+L|fd%;bx^hJ|Hh%;KjqGg;WAwrx{VD*10fUmD{n*~uV@ z+8<5EGWF~`bWSLJzb@K`IfZv>Jt99nE~~3GzHPhMOeHWVxpPpUvh;zYXR<4cN2a7W zJDcaG1q4Ke9fogh7`dDM^S=KYpZW@#T1+}_(eG{na^@d8GU zYu7Y#kp8ih*w_@am)E;lw%U;?sm?B5br>r$Do@~OM`@c>re_pC!df|=3d(2q%9hG< zWPksJA@T7``7SJTWJ+p5aA=rvb7Q=(PfBV9mNJu_&$Mgqy^m%;5|J?+yx{%lYheqc zk_E_-4kL~Y49Z9j4Af^@+O~_gW@Sp!HfxXI5Lt2B81Lhol3K|;F@D0Lei2cD0cmw+ zXXliZQR*IH-ChM8s_sq`;_YM)aZ==7-5tV16vRBEct@zjo-e>=q?Yr;LxwPyQI#D6 z{6oh*Raov7cXLTjIR53x?>a?AcO3v-H*0L){udNec)k6hi0H7NLxN`qLx5HBb(_)$ zV-v-F0EJ{y-F?(gx$!_cC)QKChrYtlSGq=GRiaB?ijJ!EG}f!b?R;n?jvcG`qaXh) zJUX^_NLW}{rtIYv6WLx$tdt@^RL8N+AhH#G`~wud(==3r01iVA+o?=vJCK9KAl0~dxv*)>n({Nw)ymc0#3h>(l zx$MT$rI`M`-(>Sw>UU=EU)25J%@MRd&3%Mqw~=*DN(K7Rd+B6!a{3xM!K%H$FCH0& zu|J=xohZ5XPMZ60L5GQ`{%A=AP4tyw2CSUV-t4X97LK@iu=}F@Vn1fczp)kK4upOo zRkgg}Hss7ByBMKYG{=t8JM2qCN3z$Np~+G8r7aczd(&b>aLb zYS+s;sTZGsF_^28B>irv*TIX3#rU*gJ~7Od{meJBaokt`+V6jR>7~E@j*nVAr%#P% zse0*m?u(~3v!C^rrss5TDD&j*hAPotvYB`Xc3|Hx(?9rNx}i#aUR^`S=MY^O#FC{U z>T}9Gb$t7C*R}peWIMx3p))E7A@DqRmN4nu*`$m_U4FgukoGh~E#BER0_!H?Ys>Pn{Y26Xj?VjsUC+Zktq8D%|d8b9mo{~q6c$b;Y# zQ?N?NoC`qGm`~=na*v%`wLt>%b)8j}ZOe1(!#>a#ioV@7>NpUboH zDf*tlIF$NP$N;oZr{mz`TC^=v+W=n)>mqfQ{c+#oPDv(>1@5P)R9)kSJ$p9nk!tC6 z&jxk8684?cS*oSEFh~bdXC5ilZlDjD?u@>joMFg=u4i&~8>7zkq~dNI;lxJh+&!uC zE+Nac(@9k0Q|yPkz!P_vCR98TNQ8ZQK)VO<$BXdCEKmpEz;O5`ocm4bori0dS=9~| zd#1e2_%UO~w<&kozHZ%iL*BqXeFi?ZcCCP){Y{@P%`ik@uJJD$bZ)Y<8!%X)F(Gjq zjN>APfWsk%fa9Sy1hXq*Aqq*l#NIi{$vM3Z5xoYlH!{Q0+@_?iwut4dq5tauwsXE@uj0-0+0+13y&DTB|~t(LrG& zd+n&L-9i7)wI@r{->TiYQ#9IBp)pp#XGw1WVzVwb+vMu*f&+aL_8}A8NfeW}=eciB zXQ*Y$&s{TRWSBA%Bant(vuLYUG7QZ=EM>!bb`I(i%A$LQPo0+~)g^T7+@{R}XXoLA zI!z#aIHC`c`x(-qZ+|yCOy^~EcU72|(ZdIb;dL^=80^8^bw-~NzCe;-u{;<}6fr)R zg|$keYU%2KiP=4zb#5+hu5NB_ZQR`5Jls(4-ObC*?BVUXTpZDb_VuU|=wqv?dH}!ZdI734sQ9 z!j1;84?;htT2W!g4Tar6vwz02hK#@nbNfVikXDU|&5p_Ni&Wi5>7BkL-QQzi!HUO& zBhvjm;WYXzT`o~P$v4ex58`Q#$k0nHPK&Teh|nS| zUXd%GR|6;7I*Y)G*9l%vwpIFF`Azd@7ir_+;))wl<(>T0P*(D4jW#W+q*73t7~>|DL$?se?VwRaoOm&4*B^@duDf+ zPCS~E6Bw9eNel|~@0U@O(9x}p-{LOe5gj|m`uh0?1h%zwdnmC}8#i_B0lU-y9y164 zYN5cYcHKf*P>>tNqM9QAzHStA8F}oh68=W<6Prr-oslO#dx)hDR-6uGWqsSI%gQ=@ zI4kS>yIEQGepMdm|08drg8@kn(HFxWsUzP%B*Wgwdq{nhPgdUDN9%3>950FdBHA|D+Zx`|r#E(@Hl5WzmMurQ&i`fj zMA(V6m|7#a1*i=)tqT2V`}fffuHl_}Zs9#PwL%yty;6c{FnxdSI` zM>l|vdzde#e!oT^?G4DkD7~t?02#|Dz1j#44*#Tv9{AoJjqd7@K|_u*M={>@__SL) zFHYwd?ZlJKMnCF$+0YNI&9~@BTQ{t|wwA;@$#82@uZD16;z8aOEC`F-u5V{&L745h ze4Ag>#|kUQ-+lg2InqH zMW=}%+^tvJ0Owtb+^^83Q)Fyfq~YoGs5sB3A62B#g275uZ0B4{0QFOier7iJBjh4q zKO?$HCsD?BGd?mcHnNjTVZNm7at>%~csjt6+c`E$8C(!5DUUww85fm~ej4X1*L6$8 zn(1WRH@)6jD=ryYfzvK`u*AUWM{U104i;@Q8`z(XXk@k#wmy7AV+?V& zBux>Y$#okx?BNUd@JPXAGTTHvvqybKud$=HiNu0_14dNG_!RvUx&g-NM3dk(1l1&; z!Y*v!M~u^XBkvZ}Xd&-Zz5PW;P$J!GG|1E1ul;Dt%6?)y$LI%zT~b z3p_cp(nQ$IBwlzb2c4I2F zfu3+`(@D0r+>4)UJb`&4_YHI4Wz#8RF!;9`-7or|@x&W5l@<#2p01XiL}56(7@dt^ivUpTWWZ0*<+M+F}DwthPy; zn`p}s+hyJleUhw0wB^VK+Z91uh~@&fJ2X~_J%+*!Xqal7ut6HSfq!?dLAK$mbYHEQ<~)>zz2X!AzfDy%#1j61Q~y7z3%5PJc-bth2l z?HS%1ZDal`-QvA9x`naod46LrI?HtL+<~Vj+CJx>(HdYKr=s5}qD{x4q~nv&mSbPr z2GQ0y3U|&r7~2RY`_-cF&B@UTOa9o>|%5^x4YBryGwc%jkQ03-h~ou0XzAP}*JD zjQ~G<_s&<1XBwYEE;Q})yRifGwS^hI4N@QRxov?ho3CpeOEg8VYRIBAjhSshGfjEQ zfuyj8Pi+5bjBT7~r&W!z`iY~35pjpHybQC8q1{coGWe0hZw zi;Sl~tAE+PZi$zAthHG)I(i%J)<MnNQbzFG;>4&rG;y33o`_ ziHP9GSL7Eu8U54guY!S2PJa3~f)j%a2T8?BbhxLRn^%YCx9)E4UcghG@`2!iEGiGA z@G}LfS`p z>dwpah9RfVJC$D?x5RI~xU`t*S?9_xz6dpzjvm)LyI`zO@Yssn?1E_+7ky4KbeGqn zPrNfBn8%Nn9^pOW07z`bPhrvT?Kol^b7aSR(H*~j%UGH>Z^SJBpdkNQD+==fCuxiH zb(AyAErt3cKOgxV{A@@*!X1EH5i$kaEhXAMIf^hjY2iFugZ#R{Z_=An-e|JyLHZ!t zrTq_A$Q38~lke+0zR#aLiF#_b52gLKp;GcaH}d5f3}2owjGKFSr|G`wOhCsd@9c46GPL3U=I-0Hx@ZO z7fa7kyY79_-VfQ5e~{-uUkI;K;NTEGR6@NNyoZ$suxR>4tkJ-iC8$4T#ZhO~taw#~ zQb0kreoZN$Dqe+_N!7XD_XJc1(%;VT12c%>rMbMPPnU~~edrvMvj`um@J#fSD7}+&s4>nSLk}pRm)HgmeekVRNv7z^W zrnrmG%=do=Jm`wgV&#BvF*fz6^_MNa6UwgK-=ATa_)cV^<9AHDc&VP|CnI|X=D`~C zs4sPXbeaj)dn@eYQSN4Y&StC~<)X&>u6B3~ zkBIjj(08-nrK>I+{txBZ!Me$AhROaD+{)wQEFSF;F6a^MEV~CeyL-jNBj*)zA$@x- ziT;PO6RA!-uV*`Vh+Gu0ux-qu*aTG_2@NYUro>Xl6cLl1c%-ajO~;)bk#{%dK2J_6 z5eO#`5ts+1AdIJWa*!(L{k1PAuc@nJH|pvJJlKu%<(Hf5?g}?f+4(hgQ4tw?a3_?OW>Xqj^pXHBpcj{l zC{wGCV{g1yH9}fGo!=tB`LC0gGigNCdv7pSnzM~y-jG3}Ah2sw#xUOi9a=env8N4A8l>q)oKmIP~X zjM)FwG%v)(B52m+?d1m!0GD$trFKbAize4HD)A{P<*aaqt^TEB{Q89p{QA)sZHH&D z0iWKgom|Ou>(=qgn(4pqeuV$yNG?miilqI6k+c|vnBUJm!r~tp#>&6g#0=v`l$VVh z$L~DxIp2Y$#@bha*nMIgiMzo}G__5gyqW5TY5Ccy9rqfwE9bR(Vn6B=1#NDRb|`hn zs{M$(PNE%WncC?e`ls6E{X{#?CAAl5?L^B(yGqM-DlJ!IYy^$(CZ1=*s3Y?K#<)sP zP#uA4V+2E6$T zLYyqou{5dn`b{+&vZ|u*E)s4OS?bJFKXV`fq-6G64!w`o2fVlsWr=4^c zYGNeK&W*$rGCU)|yQ`TRD36P$0_SxT%P4$&^Pv%=4wfY?D9%ghKl0+yi?fPen_ZUM zd)|@pW2s`|(_I&39@_G0$*AhQt~+`UTp}C!)VqoLPuQ-`p^r}=vu4QPwt{voJM zK-8UrWt6C`7?XLaW=VESZq_8t-QSBz5B{aME?pAmQAI{S9NFU_R8aK zfr*_TQ8R759WcCOe5aJ0vhaL0JevZxH}`BPZZ1F${L}J{c^M?gwnTgRLy8Xm4qx}_9y=5CHC#Kl7flbma!+du+TFs;PTG3%vG#Ca38UJKDrUvZtfP>E>fH~ zwj$9NcX|@;aU?YJ*1NiU!{^Af%&E;{9h)m}by}rMxQ_h!pc*DGKgyr)x4^I75BGR} zy$_kfeZeV$uH#4L6a{N6d^+J)7G-zhyh~XbsOrEma?wN_AN(=j|Ki?Z#ha=P*y@Q7 zscQU5nPBJuLzD^fTc>OnXI-F=i&J1w`%r=qW;{UX;WXF9MR9-3UwO=Hs(*Q?U_fA7 zOn@a+Qe}TxAK|51j98Kmb?bc>hn}Xt;`@Ej>>B?9mcJ zFk+H-Ok`AQR&WTCjdw~)sOj1@iTyZ>Pu;;U^SAeNzd3B1K4@fUSV$;Ri#s`WjGuQe z3UO=~Y>|kY`xL`r|8qeLC@v!iHg%IOEOav&bU@DUAxY4UwCr$_P~{K*b~9Gzy?>s2ADrFQnQis@PZIn zOT`T$JpWOmSIAsVIhmv%5Q53Kq&!}dQEDOaQn>IUe6Yv(3z)s&J+oku^c z+f>X0Cn!ec9GXZ6WvdfEP{D(!9G<(hu&k?-Q~Qj(DN_r(u@IlUvbu|p9;4XD>50>G z4^A92GIw;F%ubVrRQ2l`H*VR;eR04|hjR%H5A%UAEo_hu2U8^80v$(9TyZ!fd|>XD z2k_s^p2_d@L-PTBy|(UC%TE3u)|W4yAU|XKNY1YAHfhs_nmqd{oweZAoll1a5B=)) zSRB*2vHZkOz!j1MskWb5)em$NbN&-^HtYIj$9Nd!x%#c9HqJ7e>#B3>>h2Zi>27g} zGsHP{QQ}ZfFHTDIMlz&pmyn+Jn&%}?Y$+~zzodriNlA%EJbe;;vVCxZ)o2rjp%<*VWmJIRN-TJ@#ItNlLd4;tdGQY%1LCoS^_=zv=3GaDNM#M)MdsDjs9uoYKTp2UL=BtLsUgqWm7k_g6ZiNJ z`Yz%u(~YFwjuzYwa8WRaL;2xrqWqO~K_EfOru?4UHuuSYawBS4cINB1^B={1FfI4z zOCK+o$R^GB=cS+Ji`AR?P0l|Q3(&rPw;NMM< z&&tPbRjVS!eO|az+Om_mYPyI2d#o>tZ&{>^Q=ZMrY5#y&-$JV503R^ z?wM6!bZM_k5I>cz#DT{olT8%4tI-eLq=LCI&}977J=d5AI(!ZWJa~4=?T((w%+2c& zACQyv?39Y0G3hDa#TQ(8XM9dTe2@M;vfjCvlpbRlR*)9WpuTxV36AKUJu&O?%ZqCV zW+qg1=D)pKGr`RET%7rqBEP@j>f>1xv-5)j4U_!FOnm+P#HmyBIlqE64nQuE1HhM5 zKuVej1+C^B!pk4E0YxN^EXzzJS42WXA_yt>QfM9y}p2bvxF~S z78=^>lkr#iDdfp`g}qnl*F<m*hc-iqp~ z;#FgRfFiIF_>u@@hf)~Yu)0z5qGO|m_2}Qn;ujN}GIn-+c6zTx#}@QT&+a^FY*O18 zKTDtfJ%)|IQtpZl%FnIJ+I4>7l$gwMBNl&tqI#JupKi#KmsOv*R6C|LGkWTTf9=Yu z%FPdgo&sxF(s;)Z3Ano8B%q9{9WFZv0(D6)18lVM1Ro(=6VFD3H!4={Hh()o=&<9x z7WL}b|7ib?_?->}B-|96KWx<2H{Ub@&Xzk)fU>M{Q`mU^hq)*Qu%yR zFwyg3O~ms#cpkbcApk&!!@cz%Kqds$Xr`jX$RZkC@-wn@zjOv_Iu((LeNISAo@O`p z*%nA2@_Ebi`NaH(|FG>lq1F}sKuQI&d}vGNb<6YF?))`>NW*P=KxArX zhnPVRwg$U_9}AYB>=9Kl4`(IpLrtJDma5q=k zMqAF=$DflgfCrMZ#>n7tJ_(E*pMU!1$yaZGKXE%df^-Nk^W{%|RX$}8i+*_*(SY2> zpY#&}Ll~$4PF9peqe9-rOf??MjJGCFJpeLCNp$82IrJ0f8CI;mdTH%0A6)5fz^b{+9WlpW(OXq}w{QV`9e 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