From 8b79ad18fae4911c0789d288a14bd91edb0ca246 Mon Sep 17 00:00:00 2001 From: Thomas Friedel Date: Tue, 11 Feb 2020 15:59:26 +0100 Subject: [PATCH] fully unified into one shader updated shadow test --- backend/goglbackend/clip.go | 4 +- backend/goglbackend/fill.go | 40 +++++------ backend/goglbackend/gogl.go | 32 ++------- backend/goglbackend/imagedata.go | 5 +- backend/goglbackend/images.go | 5 +- backend/goglbackend/shaders.go | 118 ++++++++++++------------------- testdata/Shadow.png | Bin 3396 -> 2013 bytes 7 files changed, 70 insertions(+), 134 deletions(-) diff --git a/backend/goglbackend/clip.go b/backend/goglbackend/clip.go index d6414c5..3ccec5c 100644 --- a/backend/goglbackend/clip.go +++ b/backend/goglbackend/clip.go @@ -40,9 +40,7 @@ func (b *GoGLBackend) Clip(pts [][2]float64) { gl.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh)) gl.Uniform1f(b.shd.GlobalAlpha, 1) gl.Uniform1i(b.shd.UseAlphaTex, 0) - gl.Uniform1i(b.shd.UseLinearGradient, 0) - gl.Uniform1i(b.shd.UseRadialGradient, 0) - gl.Uniform1i(b.shd.UseImagePattern, 0) + gl.Uniform1i(b.shd.Func, shdFuncSolid) gl.EnableVertexAttribArray(b.shd.Vertex) gl.ColorMask(false, false, false, false) diff --git a/backend/goglbackend/fill.go b/backend/goglbackend/fill.go index 34b8e3e..c3474f6 100644 --- a/backend/goglbackend/fill.go +++ b/backend/goglbackend/fill.go @@ -38,9 +38,7 @@ func (b *GoGLBackend) Clear(pts [4][2]float64) { gl.Uniform4f(b.shd.Color, 0, 0, 0, 0) gl.Uniform1f(b.shd.GlobalAlpha, 1) gl.Uniform1i(b.shd.UseAlphaTex, 0) - gl.Uniform1i(b.shd.UseLinearGradient, 0) - gl.Uniform1i(b.shd.UseRadialGradient, 0) - gl.Uniform1i(b.shd.UseImagePattern, 0) + gl.Uniform1i(b.shd.Func, shdFuncSolid) gl.Disable(gl.BLEND) @@ -134,9 +132,7 @@ func (b *GoGLBackend) Fill(style *backendbase.FillStyle, pts [][2]float64, canOv gl.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh)) gl.Uniform1f(b.shd.GlobalAlpha, 1) gl.Uniform1i(b.shd.UseAlphaTex, 0) - gl.Uniform1i(b.shd.UseLinearGradient, 0) - gl.Uniform1i(b.shd.UseRadialGradient, 0) - gl.Uniform1i(b.shd.UseImagePattern, 0) + gl.Uniform1i(b.shd.Func, shdFuncSolid) gl.EnableVertexAttribArray(b.shd.Vertex) gl.VertexAttribPointer(b.shd.Vertex, 2, gl.FLOAT, false, 0, nil) @@ -238,14 +234,16 @@ func (b *GoGLBackend) drawBlurred(size float64) { 0, 1, 0, 0, 1, 0, 1, 1} gl.BufferData(gl.ARRAY_BUFFER, len(data)*4, unsafe.Pointer(&data[0]), gl.STREAM_DRAW) - gl.UseProgram(b.bbshd.ID) - gl.Uniform1i(b.bbshd.Image, 0) - gl.Uniform2f(b.bbshd.CanvasSize, float32(b.fw), float32(b.fh)) + gl.UseProgram(b.shd.ID) + gl.Uniform1i(b.shd.Image, 0) + gl.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh)) + gl.Uniform1i(b.shd.UseAlphaTex, 0) + gl.Uniform1i(b.shd.Func, shdFuncBoxBlur) - gl.VertexAttribPointer(b.bbshd.Vertex, 2, gl.FLOAT, false, 0, nil) - gl.VertexAttribPointer(b.bbshd.TexCoord, 2, gl.FLOAT, false, 0, gl.PtrOffset(8*4)) - gl.EnableVertexAttribArray(b.bbshd.Vertex) - gl.EnableVertexAttribArray(b.bbshd.TexCoord) + gl.VertexAttribPointer(b.shd.Vertex, 2, gl.FLOAT, false, 0, nil) + gl.VertexAttribPointer(b.shd.TexCoord, 2, gl.FLOAT, false, 0, gl.PtrOffset(8*4)) + gl.EnableVertexAttribArray(b.shd.Vertex) + gl.EnableVertexAttribArray(b.shd.TexCoord) gl.Disable(gl.BLEND) @@ -284,22 +282,20 @@ func (b *GoGLBackend) drawBlurred(size float64) { b.disableTextureRenderTarget() b.box3(sizec, true) - gl.DisableVertexAttribArray(b.bbshd.Vertex) - gl.DisableVertexAttribArray(b.bbshd.TexCoord) + gl.DisableVertexAttribArray(b.shd.Vertex) + gl.DisableVertexAttribArray(b.shd.TexCoord) gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) } func (b *GoGLBackend) box3(size int, vertical bool) { - gl.Uniform1i(b.bbshd.BoxSize, int32(size)) + gl.Uniform1i(b.shd.BoxSize, int32(size)) if vertical { - gl.Uniform1i(b.bbshd.BoxVertical, 1) - gl.Uniform1f(b.bbshd.BoxScale, 1/float32(b.fh)) + gl.Uniform1i(b.shd.BoxVertical, 1) + gl.Uniform1f(b.shd.BoxScale, 1/float32(b.fh)) } else { - gl.Uniform1i(b.bbshd.BoxVertical, 0) - gl.Uniform1f(b.bbshd.BoxScale, 1/float32(b.fw)) + gl.Uniform1i(b.shd.BoxVertical, 0) + gl.Uniform1f(b.shd.BoxScale, 1/float32(b.fw)) } gl.DrawArrays(gl.TRIANGLE_FAN, 0, 4) - - gl.StencilFunc(gl.ALWAYS, 0, 0xFF) } diff --git a/backend/goglbackend/gogl.go b/backend/goglbackend/gogl.go index a7526da..f85f695 100644 --- a/backend/goglbackend/gogl.go +++ b/backend/goglbackend/gogl.go @@ -20,8 +20,7 @@ type GLContext struct { shadowBuf uint32 alphaTex uint32 - shd unifiedShader - bbshd boxBlurShader + shd unifiedShader offscr1 offscreenBuffer offscr2 offscreenBuffer @@ -58,15 +57,6 @@ func NewGLContext() (*GLContext, error) { return nil, err } - err = loadShader(boxVS, boxFS, &ctx.bbshd.shaderProgram) - if err != nil { - return nil, err - } - ctx.bbshd.shaderProgram.mustLoadLocations(&ctx.bbshd) - if err = glError(); err != nil { - return nil, err - } - gl.GenBuffers(1, &ctx.buf) if err = glError(); err != nil { return nil, err @@ -320,10 +310,7 @@ func (b *GoGLBackend) useShader(style *backendbase.FillStyle, useAlpha bool, alp gl.Uniform1f(b.shd.GlobalAlpha, float32(style.Color.A)/255) gl.Uniform1i(b.shd.AlphaTex, alphaTexSlot) gl.Uniform1i(b.shd.UseAlphaTex, alphaVal) - gl.Uniform1i(b.shd.UseLinearGradient, 1) - gl.Uniform1i(b.shd.UseRadialGradient, 0) - gl.Uniform1i(b.shd.UseImagePattern, 0) - gl.Uniform1i(b.shd.UseImage, 0) + gl.Uniform1i(b.shd.Func, shdFuncLinearGradient) return b.shd.Vertex, b.shd.TexCoord } if rg := style.RadialGradient; rg != nil { @@ -342,10 +329,7 @@ func (b *GoGLBackend) useShader(style *backendbase.FillStyle, useAlpha bool, alp gl.Uniform1f(b.shd.GlobalAlpha, float32(style.Color.A)/255) gl.Uniform1i(b.shd.AlphaTex, alphaTexSlot) gl.Uniform1i(b.shd.UseAlphaTex, alphaVal) - gl.Uniform1i(b.shd.UseLinearGradient, 0) - gl.Uniform1i(b.shd.UseRadialGradient, 1) - gl.Uniform1i(b.shd.UseImagePattern, 0) - gl.Uniform1i(b.shd.UseImage, 0) + gl.Uniform1i(b.shd.Func, shdFuncRadialGradient) return b.shd.Vertex, b.shd.TexCoord } if ip := style.ImagePattern; ip != nil { @@ -375,10 +359,7 @@ func (b *GoGLBackend) useShader(style *backendbase.FillStyle, useAlpha bool, alp gl.Uniform1f(b.shd.GlobalAlpha, float32(style.Color.A)/255) gl.Uniform1i(b.shd.AlphaTex, alphaTexSlot) gl.Uniform1i(b.shd.UseAlphaTex, alphaVal) - gl.Uniform1i(b.shd.UseLinearGradient, 0) - gl.Uniform1i(b.shd.UseRadialGradient, 0) - gl.Uniform1i(b.shd.UseImagePattern, 1) - gl.Uniform1i(b.shd.UseImage, 0) + gl.Uniform1i(b.shd.Func, shdFuncImagePattern) return b.shd.Vertex, b.shd.TexCoord } @@ -389,10 +370,7 @@ func (b *GoGLBackend) useShader(style *backendbase.FillStyle, useAlpha bool, alp gl.Uniform1f(b.shd.GlobalAlpha, 1) gl.Uniform1i(b.shd.AlphaTex, alphaTexSlot) gl.Uniform1i(b.shd.UseAlphaTex, alphaVal) - gl.Uniform1i(b.shd.UseLinearGradient, 0) - gl.Uniform1i(b.shd.UseRadialGradient, 0) - gl.Uniform1i(b.shd.UseImagePattern, 0) - gl.Uniform1i(b.shd.UseImage, 0) + gl.Uniform1i(b.shd.Func, shdFuncSolid) return b.shd.Vertex, b.shd.TexCoord } diff --git a/backend/goglbackend/imagedata.go b/backend/goglbackend/imagedata.go index 86eaa3e..cb08b53 100644 --- a/backend/goglbackend/imagedata.go +++ b/backend/goglbackend/imagedata.go @@ -90,10 +90,7 @@ func (b *GoGLBackend) PutImageData(img *image.RGBA, x, y int) { gl.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh)) gl.Uniform1f(b.shd.GlobalAlpha, 1) gl.Uniform1i(b.shd.UseAlphaTex, 0) - gl.Uniform1i(b.shd.UseLinearGradient, 0) - gl.Uniform1i(b.shd.UseRadialGradient, 0) - gl.Uniform1i(b.shd.UseImagePattern, 0) - gl.Uniform1i(b.shd.UseImage, 1) + gl.Uniform1i(b.shd.Func, shdFuncImage) gl.VertexAttribPointer(b.shd.Vertex, 2, gl.FLOAT, false, 0, nil) gl.VertexAttribPointer(b.shd.TexCoord, 2, gl.FLOAT, false, 0, gl.PtrOffset(8*4)) gl.EnableVertexAttribArray(b.shd.Vertex) diff --git a/backend/goglbackend/images.go b/backend/goglbackend/images.go index b980af5..fc6dc8c 100644 --- a/backend/goglbackend/images.go +++ b/backend/goglbackend/images.go @@ -199,10 +199,7 @@ func (b *GoGLBackend) DrawImage(dimg backendbase.Image, sx, sy, sw, sh float64, gl.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh)) gl.Uniform1f(b.shd.GlobalAlpha, float32(alpha)) gl.Uniform1i(b.shd.UseAlphaTex, 0) - gl.Uniform1i(b.shd.UseLinearGradient, 0) - gl.Uniform1i(b.shd.UseRadialGradient, 0) - gl.Uniform1i(b.shd.UseImagePattern, 0) - gl.Uniform1i(b.shd.UseImage, 1) + gl.Uniform1i(b.shd.Func, shdFuncImage) gl.VertexAttribPointer(b.shd.Vertex, 2, gl.FLOAT, false, 0, nil) gl.VertexAttribPointer(b.shd.TexCoord, 2, gl.FLOAT, false, 0, gl.PtrOffset(8*4)) gl.EnableVertexAttribArray(b.shd.Vertex) diff --git a/backend/goglbackend/shaders.go b/backend/goglbackend/shaders.go index 70759cb..fa6b183 100755 --- a/backend/goglbackend/shaders.go +++ b/backend/goglbackend/shaders.go @@ -21,16 +21,15 @@ precision mediump float; varying vec2 v_cp, v_tc; +uniform int func; + uniform vec4 color; uniform float globalAlpha; -uniform bool useLinearGradient; -uniform bool useRadialGradient; uniform sampler2D gradient; uniform vec2 from, dir, to; uniform float len, radFrom, radTo; -uniform bool useImagePattern; uniform vec2 imageSize; uniform sampler2D image; uniform mat3 imageTransform; @@ -39,7 +38,9 @@ uniform vec2 repeat; uniform bool useAlphaTex; uniform sampler2D alphaTex; -uniform bool useImage; +uniform int boxSize; +uniform bool boxVertical; +uniform float boxScale; bool isNaN(float v) { return v < 0.0 || 0.0 < v || v == 0.0 ? false : true; @@ -48,12 +49,29 @@ bool isNaN(float v) { void main() { vec4 col = color; - if (useLinearGradient) { + if (func == 5) { + vec4 sum = vec4(0.0); + if (boxVertical) { + vec2 start = v_tc - vec2(0.0, (float)(boxSize) * boxScale); + for (int i=0; i 1.0) { col *= repeat.y; } - } else if (useImage) { + } else if (func == 4) { col = texture2D(image, v_tc); } @@ -94,52 +112,14 @@ void main() { } ` -var boxVS = ` -attribute vec2 vertex, texCoord; - -uniform vec2 canvasSize; - -varying vec2 v_cp, v_tc; - -void main() { - v_tc = texCoord; - v_cp = vertex; - vec2 glp = vertex * 2.0 / canvasSize - 1.0; - gl_Position = vec4(glp.x, -glp.y, 0.0, 1.0); -} -` -var boxFS = ` -#ifdef GL_ES -precision mediump float; -#endif - -varying vec2 v_cp, v_tc; - -uniform int boxSize; -uniform bool boxVertical; -uniform float boxScale; -uniform sampler2D image; - -void main() { - vec4 color = vec4(0.0, 0.0, 0.0, 0.0); - - vec4 sum = vec4(0.0); - if (boxVertical) { - vec2 start = v_tc - vec2(0.0, (float)(boxSize) * boxScale); - for (int i=0; i&dl+ZE87&W~jkwc#EcO~sf7m1qgPVFyx^dNZ*JK)M}(j|nn8X+X0~<$ zhn+Dw!?0z+bL1y+S2MBkf~+Jiw^bPnJKj4{64!!51z$!}0Y5&-?a3PM-kSzj+=3(v z+b@x?7OX`W67_LTpIoxOPY_slV<-weqhDxZBrj>NW%{~|3dS7)ZI}}CM^OSK)3=J^*s3#QgTK#n zb&qLVqhZ7Mo~e04107@vdZV!3x&3{4g40caJUGN)N@*Mf zyW4>Ia#G(%sQ7!05%w+dZ!~_MtuDovtG>mD@#(xvg0ulgFAW}bX-AGOF*?*k6_eFp zML~VJo-SF47+-X%X3X;>?0Ua%`0S z9?IN&J!Jpf5!xoA{~O%u1z;h`GPiL}D;_Z%-(=AsL&7RAw-}7$Jr-qAOMNHHdlN4n z5Uq@5hwnJIH<16uwZA(2Ywss`wSlzg__C!b$T@UMXU_n1Od@(ahHVWu+uMI{72N*I z$^Wmg_X0V$=P<1{NB9AP0mGLdh}pDbvZ(n%%E=A zZK%Cp2>Xy-hVqhgX>m|%t=Svz7n(Ib&FN~F4!=&^ly}P(M0vT&sy$hKDS|RdI{1gl zz`^efe!jzhKsatssX!To0!}$J%#Qf1m=ehMSg|0Uj_guOwILsV z;$C-P>D`s%ueFsE7D^zY`ycP_o3^xYRoFI9>xH+cg!Nij#L*8Fr7-Ip)OUSQAX(K; zs;t0KfvUdmA@!B6(q{l{cJ0|o#LoGh1Vm?(o;+gYst=sIbGlpFYG@aCYp9q0k;;e^ z<+GbZZ4MH#j(=_k$e%_wdt{rXM?Wwx6*vaxO$=Qp*&1nb-}y{d9Wp6%!Xo#Y)F}q( z?{QHz`fJyEMrlZLGyv1~50G`0{5bSaNwr&Y7{X*uZ)_N@xZ$KyEK=8D%e)je8HjvZ3S^`ht*A52B7SouuzEW7d zh62)40Cw^?+>De}+v^E!VRQQy-OQA6lP%^P~{EP8!ZbM#X zJi4y-tw_SRYdOlf#+@{%*~rU;DOX+s0uSd@6gu?y9@_GssDxv|zOj@KG9CL8~>{{k@8#P$FH delta 3394 zcmV-I4ZZT+55yXfBYzDGNklmON#zu{yCU$lnBA;=9xr?a zA7*;G^{65N2Qrb#1QKA8C8&OlYtAeur@dl}& ztbbI$yDERY@TP7OVG&i`R(jO_*}t-;Aaz^m@j6yCtS~vXQ?~IMuU919jnG=PK=5!6TNX*9B-alu+;WLr_Q5Vaf#mOQ*>l#hV(nUsV2XrYfySe315 zQu0xsDZDChM}L9UBt>YX;=${k&{DD9BUy!_5+GP=JzhtFRgwD7Z*+fA@dALL=r}gK zI#6Dg3pQfhm*2l=M#O?X1_=6OYmbM3rQNnOTBB6S>7&X>H83~8->Ie^^j`w&1Sdfg zkUnuCQW}}?5^9HlC8De^YEnLhIOkLW1*=lA%4bF1Cx5Vn)C@CK!K$$?-g&+RI0!l&}8QXwmh@x)h)OJ&rsh|~=8 z1e4~%Cx6E(K@~X!EK#MH@^1nWAcOF3AvJiTV!&yt*s^}498(Rf2__=Nr!AR;Ll>!$ z3AJ~FCCO1#vCO7dZn42c%EM6V7aiRMi#*Xi+)prhCeEkTX&3)_QQz<(G>fIqLOB#V zdbU2VC9UtliV#U(?%+<75=P|0Ja|$rrR>M^S$|dAxKb-|SWIdVSdvx9cWpEzSfW{) z6othBh5-hOkHkyg%n>tq9T2OJZc#4p@WB|LgS=fdOGiW2tU<#62~OJjv`%@Ys@Cxz z0#?-?wkf+`eEAX(eHk1jCa%K@5KeFs|24x*IyGbiFL-ISI#8a)?pcGg&fdEs;UbeP zl7D;pZq=WKF~rL=k8F}+eL|{IC|x;XbH`euc46vy^0R3-FP12p#cH*{3J?gE=v2=D zn|-iHfz|5CTZ~bPLZ%(Rz;&)jL~>ptu+T^cB2{71*?bjkj_BY^Ub1y;l3MASjq2>S z#0;4f=>9@sf-e>;vqD!YqYVmxna8cEpzh21^VX;)aWUo{1 ztfIDZt+r0tb5<^L#SH(^*!R-8C?q;&IsDT+=92@Ytnm!LQ+;fefeU@hY1L9!R(}qD z{Z6{MLM2DmtXj5e(WXnSTmT>3vb}*E#7c%U)Qo z<*zdVUz<)G%uB4^VI_GxT0;`!AukYC+H=E(EpQx1UMab4+ z*$$7Y-u&T>chgKAylEX<%~~hh^jXe)yam|`tmfZq@2Lm$s_cTbvB+g(-_x)E(`(X$ z)dOTJH~;)fQaTu3>jZsywb(+o-7>mjVf7s6y7#^6U#+ab8jY@1+SmN3Z+|0}mG~;& za9y+HLf-t1D<%M@z6+MeX3fwlzGT{ir8Nr_wUsz~3oNTvuJt$SN{`oVuspX7#||$I zZctyWeo^K!(G(A_2zsMsTWw3W*?t~PuW|(|vQ7oY z6cn$OByI;RD4#Z!KT_-eO@BnM5|9yjoJ%x}aQZi#sXo9cWrWiJBke3_0eU?##*SEvs3(eU{Y=_hFmaqcTHR%z4P8x3*VjlR-2!OPk(Dl#xx39%NRe0-JEp*uYW`#Sfp9AN+W`aNVd(iCVwjq{^x+@loGW?j2tU)~udLvImckEP0P@qO8ei&iHA}(3hDP;q$6F)w97awV%~z zX;-C5*+$5XZNr}_8hQiEmyLv8ci5Wr@~lH^7Ox(Wj5A`z@yb_vC|brleJNtN3v8&_zxnowfm~Aa06x;b# z_msrtSFSv7Pj`+0OV*NBdm|sCCv2)(-ANsnCeKngzgPb;_M##-@qZs!`lk5~qIr=; zEWW{Zr}k#wgmknXiS@NDaRphg^Y4ak;@1)LegXY?(FuOYhJTUDoZYxsnX_u8NyWzR z?t2%_`;ZbHAVZ_wY;^hwX$fU4*H0F3Ro6@uKmkkSt$zJQZ0K(xR|hP9oCv9M?rgz` z4KOOTit0?~5{2cGT5)(CCarSHYn>`;6{91rkydNoLEU0hE_`8xD@}dO@2mjqJeE1$ z+~ zI++S;P{5_+GcUj|R4f|9ti-~Ko%$OJE?{2@q?*QK;e2efkz@GuW&hY1y%2(udnSc514l_&@FS)C#vZ7f(j+B0h zf@oG?jldOeb4U%B^kDq96*@EUDqtr|R&a>rIYEPS%w?M4jO8+=GBGbvvAMF!${w4G zGi@aKp;Gy8w7bm9r^oZ+l292#$P2+euB^`kMdyU;4DcLNc>;J2@DviMXDsInJYV2~ zw(-}a{N}9s&(Ej`J)g}rvmDfSA7W)!T_TKKW8&sH0uc-03HFi z5uRe&J_dN46Se0lmGhL4olE_iG%|f-Qtz2zuSxKxvZPMz#S^j_W~bhRB8Pa@{J`({ z0EYFQVSSH?EFiTxRi5UB*M)g)RW98ItOn5U0DtZQz5x6mmR|sV1NKe{nlAK+==k*`lrb#dsY60TGp$-x7B z5AZd@UmB?;)#RctkX zrH#2vapBgS3p^+?L@hu{$kp0)f3q$0jW8z*1 zue+|NQmtflgOHjS->BxHZ8gI(^UlbK7ro^Oa_7N-JBJzHDubjv9*e{aZ)0Z7dctV< zfS&6iW@HbdTp>AoN+u>&tD2PMWmAVrvxpaw8u2E4NlFf#S~hqc7CB~)h8Y&N?|+t# zV8~VpAz2?__=`Tj-zdRCOcC#7Mp)vxp3oQ{VlK(YM+}PrH7md+WIr{pL_^85svg1$ z*2D-dcoWjiC=j2OYrlCcpjIwMw#<78==(9HaxU{CQq%{ZJ%&qLau?^A%2%ORPiTbu+9c$pxNL;eU~rFJoL}^E`9h9C+}uIbG|DuyW-seNmdnXqBGc zWr3s2j!EUA3a9>B)-OumpIN`oF(W%;rkd0$rY1pBD@;UaJKIj1_2y1gyc|Q(ni7>- zoy;t~U9P=SE>OIjLYAA#k>A7+imPYk;#ees`@)K!vw7ZLVk#3AYDIBlyMNnP(}bmS z_n^$pk`lEVug&n>B$85Aywq$r$Gr<6*%>UbLMzd^NDz;?wMdxKeyq%Kffb?OD%7x| zir+RGRu3g=HL7`wjZu@meTN-hl2zFEy~_7Qxx~*(5T6jxSw14EIi`9bQp|`t$U=|Q zN*7IN?bpedULk4I1v`6SqklLJPW7hixB7*0fw;bB`7cHNLxqV7G73y;4Nw!7Rxa(f zEUa0D$mU?&t~twv=(ik6J*rD7h>?$0vmk>p$+=`iC_`kp($^TvcPp>J0z-`?7+88b z%1dS|uOX{mucDCIqq;Xq-=xg^3#t-IQo2V7)=Al?>Wxdk#XR`y(tmsdO1Yyol(Qhy z;DV|PqflL8eUQ~YQl%i$A)%>Rlvbf$ZDeTmofWndJhn$$oDgA0pK$Nzn?uuA8NI`}tRmR#6W1gA!5rP!b-wa=7Qt0O z?=v@or8zDY&Gtr8TRS>W*6Yo$|9E80XKoUC(Lmpp2fLbKzOO=Au@vI96E?~J0ssL2 Y|3B9^bo!sR5&!@I07*qoM6N<$g46AmeE