From fadc1ed639b97abd6c75498dc5931f1a05b518ac Mon Sep 17 00:00:00 2001 From: Rezmason Date: Mon, 6 Jun 2022 12:49:54 -0700 Subject: [PATCH] C project now uses drawing contexts to draw faded glyph variants to a cache. Switched to an opaque fade gradient and white-transparent drawing mode to avoid a bug discovered in the C SDK. --- .../matrix_c/Source/images/fade-gradient.png | Bin 2853 -> 3237 bytes playdate/matrix_c/main.c | 170 ++++-------------- playdate/matrix_lua/images/fade-gradient.png | Bin 2853 -> 3237 bytes playdate/matrix_lua/main.lua | 1 + 4 files changed, 37 insertions(+), 134 deletions(-) diff --git a/playdate/matrix_c/Source/images/fade-gradient.png b/playdate/matrix_c/Source/images/fade-gradient.png index 7c8f97fd9f38a1dc6431889f3e84f8fe6553ba9d..3571ae816dc9cdf9070173a611245d1d980d7102 100644 GIT binary patch delta 1981 zcmbW2X;7015{8p-hCw9Ej39>t;neYygoGoXT#|4k$Q7fE1p+xGDhZHqgewF=Ma3k5 zD8gVI5(fw#h|F*ZBH$>9q7KSA7zA|`Fyt`*r3=b z*{BVJKp^}!1Gv<}5T+@s0i`gw69tU|Q5Xx{wC(O4QCg)~o&i46z<30!(Y43onr65Nnf2A9SJ8C*J>0mepi`CMK! zHx^{koE@kboHO9$;y?o!EG!yuLA$U3hO-j`#iF6sDr{UM)YVZ zXFrqShO|f8yPLcIe`NpLQvWsX7c#`Z(Y_w5NZkYRUsMIX>Jh@el`W4T5Dg`fu!E93 z_^$L;e4D2(er468xf%a7BflA{V+sORB>|x2K>p6^XA_u61l9Dn~pE zm#CQPTKb7=$UWci>it@ZOG8z8emQA+m5TvVf7|3fwxKFr`e%~o`GV`B1_`b!>3xiC za+}n4)vsCSR(ij`bo&aTbwbI7@m4f;FW%0mQWse8gjNcy-0krxBQwZb(Y6^W7R*ji z1(C(=q(t9F9A%$-f$4(W%V?8C25f!9^LvL9kvyDa;^7BkNZP`7Xua87_#2a`A*nys z8p|tld9*ZceO2eVJoEIMxUi2Q$|-V!sOyPdX1&*wLuRrGmFQZd&3>B%WodYGLjBnL z@70B;rNg?@~rhEq$?J-}R-d*IhmBcHo3F2!|L999LKS2cw6iwwts$1(b* zh4PP|jDAt*hZlWpY*4y#7__dllvMas>AAEdj`fSEltwfWnqRhD_`Re${MSjTc z`e*N+R=jxU1J&_8a>}Z>J-pofGM5~!)XY>DUd$inTK#;(TX!I!`F`K4YInr&>|lYF zv0_E3a7Jg0o+HmfsrwvtyWD-cGE-gvJ8Si`ib+>1fShqtKe9M9&c7{vZxx>K@^am) zNt=4zrtCQFj~i}?;UlD_n|sZT`Nq0V4&!E=5u0G`SsB6Rd3{fvWmz||P$x)_99Jr& zbgeIG=ZVtIOq;Ct=I7elK4iHr#UO;dr8KfJZ(JKe(S6qy6}-3?QU zDe$NGQorDKFZi2Vo*{deO&Zq~#>HCxNZoU0OMVpD;%~rXW*YN}QM|9)#K&UN4 zDV%_oJ{4SajcDmHy|dUQVQZ6lc^YNHNh0xrE?A8Xu-~WWlb)}ympfc|#(aZyM-u#s zpx0qwrxw<*%mM5D@$VjNz}H%GV&!8^!5yJ}uWVvB&S!6{K>z@E z@0Mh^W#A6iLy>z@AE@T49)(e7-fpJm%!oC!dv~2XrNKa4@eP^aM7+mOxKvG1j$y{4 z)K5lz>}(xS5T+D&*Rk!Ftb~tV9g!Lf(uv)xhrvuE<2fCtd*F)>(q|NuHe{upBny4x zWHqBR86(+8TFvwnc49{Zi)N~Bj_2R|6a=qKtjNuDzu{{qo{pv5`K`GiVMOZ3IXqQ9 zdj4DAqPkC%RK!!6J;R)MGK&MuI|*QpodKk(M^!Cnq30SE4<3p>3mvM7iLmbl1})F6 zIxLd+Ed7++(LJ3VN_RgPKH5-X@Y%Q6BU;U<(Y5n;KldHSc10bS%vn}mWr_xZi6V=U z1Qqva#M_}o|I7JV@`ta1v9ca%nc^hE??DWP49|<~w`kt&Gb=%?cv#DLo*qL7x{u_X zOEQ~0sN6J-_K`CEWtXwtFYrAt$Jxrjg zYe&w1L;CB-41BNcVEZ2z)EC#~-7R{1VQV0tmmDdwO{QuVe$Xk6*Pf8vfo;z2d*7tx zoyH3E@IePYCPjSqRdlj6V*2}bNz%hYXHJMfQK_T!M5OtzWcx*h8L^A^oxzzWcIVgi zeFwWvIz-MctXNepfm%a-*80O}j|Puh)~RO+`3AoEgDt~x&o)Q7T^hO(Y`CN*Cd};2 U4K=A=d(;r3cOc=iS5($N0JfcTP5=M^ delta 1600 zcmV-G2EX~G8KoAG83+ad008@-g%7Xk$Iu{x0glb!+>li~stlb!++lL!MalVAiU z26%07O-(wJx&u11tOGj%lZ6IZe@#qDQ~&@4*GWV{RCwC$T?vk>FbwtmFYP%&CvS5I zZIG&8QE(Q%<}G$Y)jjT!J34gF>RglUclbMcZgs5_@6G=r-;=Q#ymxiqr%wfcTeWpR z_mlkG<6yuO(g3uIt%9BtKMnjWNnys-g#+D_WT1)uHaRvFpc&v-fBMznbNP>8 zAmoENkN&?%--{5V0-wu&&+`(WO8|}r<~DxsaQqc{U=*yYx|P^1&V2{x!cpxgFrstA z_7MT4w0oe;4;ym@@F-zSfT%(ynDgkEkw4(-AAa;2w;yGmM~Gq7B@;hS09Zxc!B|dP zuBG0a<}gJj5H+k2Db~G6e>eb&v(g06v*Svu#1fRYqA*v44+V}QfeTTo41g)uBH*d0 zcoZ1ZWWXgVNdTXmlS=>&1^W)hddg)`Nrmf9pcXs>yeetf?WUk=)dg%;RXR#LY5BW~ znz7N9w8shn)U?Kgo1oMtqyX1#PbD+%7^`LpL{;A9xCCHO`Re}nC)miVO2&f4%5 zh{=Gbs*n|ev$E?G^%V`Y$)*s}U_DVY(BS+s%Ge<@Y+)hm2%zI?aeiHUoFQ2bU?z$o ztL8!Y+ns=~vG-5ulWHlmDt6L!P*V>I;5$m)vH#J+GpBEEettF1gmO>121+U+kzR0$ z1UuX(Wwn8-$ko(^f13uFST&J~ok;n(bEXoSpCSPLaJ#Cap8#W5O|wORN_&n=(c)MX z$3k&JoF#xM^~u=ds+%%hRqnbCPA!3B@uw8DO3+v}-bM_ydfz>dfUe>g>;%@T?{gJi zbtDSwb`p}QT7(37Nm%-7ksbnE%921fWuSt-nodxw?h)HKf0Yt&L7P>$^?~dlH3BMd zBjZ70DZtFkk(z&JK{{2ve+tm19F?~aimxO9eMpQ1DVcB^IuJiavQkSJ9Uyo2FK|~5 zAmD_$4J(IXA|>Eh_9$stkr|dqtlCK1@GQWp>R*|66~vROpHcf0*7>fK$E^W4+}=-J zpH2gypxvSVe@=k0YL_c9Qz-^#&jbX>+&(E4{$1I;KHCQ*9-^q3xk+rKqpTuG*xRhd zueb{8{gX=^cjwSkED24;Z({q~2_p7T1R((^6P=Zu3 zmcE(>P@IMWTo5K7kEr~FdS{&zhvX0oA!&<5!C+$9Av1@L zDrL#mG_Wh~N7XzSpPoi|HOTIYky>Lvl`=q7q8nqP){9>E zj&lk*e?n6f&N4rw&x-Bux(`!?VTvS7D%+~}sp`8Az)Q#g3zqKMe7C@k{oMgrroT?r zJvz5-jLF$QKp{tndQXuLrlvsM0vI79$xVk;#m{bKjHE6Y%iVD<6u?*JOR=;WqU0)V z)$urS6~ZL|qP6`5G1z?{qui(6ht%$ViMJave}uz%(GOdA#-;&~^GZRSqLfRR3&q|| z1Al=^yM%tDmN80p)FpcAb4V4`CGKI}6K5Pu?5eYD{S%47RriDaPAPsSuB&np^~u`q zYBf31cjZlbPWnT&;MElg?kj>(g<6I0Dc}$)1Vj-sgQ`;&I2|xUZFI-}b`&_{Rm6)*Pgq?GRSg6WiM{WvAA~v;B06 y)L6I1&C8cKc6VQYMMW#)dupBh)As!F>4854P~R+eQ*{IY0000system->realloc(NULL, sizeof(BitmapView)); - pd->graphics->getBitmapData( - bitmap, - &(bv->width), - &(bv->height), - &(bv->rowBytes), - &(bv->hasMask), - &(bv->data) - ); - - return bv; -} - -static void freeBitmapView(BitmapView *bv) -{ - pd->system->realloc(bv, 0); -} - -static void composite(BitmapView *src, BitmapView *dest, int srcX, int srcY, int width, int height, int destX, int destY) -{ - if (height < 0) { srcY -= height; destY -= height; height = -height; } - if (srcY < 0) { height += srcY; destY += srcY; srcY = 0; } - if (destY < 0) { height += destY; destY += destY; destY = 0; } - if (height > src->height - srcY) { height = src->height - srcY; } - if (height > dest->height - destY) { height = dest->height - destY; } - if (height == 0) return; - - if (width < 0) { srcX -= width; destX -= width; width = -width; } - if (srcX < 0) { width += srcX; destX += srcX; srcX = 0; } - if (destX < 0) { width += destX; destX += destX; destX = 0; } - if (width > src->width - srcX) { width = src->width - srcX; } - if (width > dest->width - destX) { width = dest->width - destX; } - if (width == 0) return; -} - -static void logBitmapViewToConsole(BitmapView *bv) -{ - pd->system->logToConsole("bitmap: %i x %i %s", bv->width, bv->height, bv->hasMask ? "transparent" : ""); - - int dataSize = bv->rowBytes * bv->height; - - { - char line[bv->rowBytes * 8 + 1]; - for (int l = 0; l < bv->width; l++) { - line[l] = (l % 8 == 0) ? '|' : '_'; - } - for (int l = bv->width; l < bv->rowBytes * 8; l++) { - line[l] = 'x'; - } - line[bv->rowBytes * 8] = '\0'; - - pd->system->logToConsole("+%s+", line); - } - - int i = 0; - for (int y = 0; y < bv->height; y++) { - - char line[bv->rowBytes * 8 + 1]; - for (int l = 0; l < bv->rowBytes * 8; l++) { - line[l] = ' '; - } - line[bv->rowBytes * 8] = '\0'; - - int l = 0; - for (int x = 0; x < bv->rowBytes; x++) { - int byte = bv->data[i]; - int maskByte = bv->hasMask ? bv->data[i + dataSize] : 0xFF; - for (int j = 0; j < 8; j++) { - int bit = (byte >> (7 - j)) & 1; - int maskBit = (maskByte >> (7 - j)) & 1; - if (maskBit) { - line[l] = bit ? '#' : '.'; - } else { - line[l] = bit ? '?' : ' '; - } - l++; - } - i++; - } - - pd->system->logToConsole("[%s]", line); - } -} - static void init() { srand(pd->system->getSecondsSinceEpoch(NULL)); @@ -160,65 +66,67 @@ static void init() sineTable[i] = sin(M_PI / 180 * i); } - const char *outErr = NULL; - LCDBitmap *glyphSpritesheet = pd->graphics->loadBitmap("images/matrix-glyphs", &outErr); - BitmapView *glyphSpritesheetBV = getBitmapView(glyphSpritesheet); + LCDBitmap *glyphSpritesheet = pd->graphics->loadBitmap("images/matrix-glyphs", NULL); + int glyphSpritesheetWidth; + pd->graphics->getBitmapData(glyphSpritesheet, &glyphSpritesheetWidth, NULL, NULL, NULL, NULL); + int spritesheetColumns = floor(glyphSpritesheetWidth / glyphWidth); - int spritesheetColumns = floor(glyphSpritesheetBV->width / glyphWidth); + LCDBitmap *fadeGradient = pd->graphics->loadBitmap("images/fade-gradient", NULL); + int fadeGradientWidth; + pd->graphics->getBitmapData(fadeGradient, &fadeGradientWidth, NULL, NULL, NULL, NULL); + + + LCDBitmap *fadeGradientTransparent = pd->graphics->loadBitmap("images/fade-gradient-transparent", NULL); + int wrongWidth; + pd->graphics->getBitmapData(fadeGradientTransparent, &wrongWidth, NULL, NULL, NULL, NULL); + pd->system->logToConsole("%i should be 512", wrongWidth); - LCDBitmap *fadeGradient = pd->graphics->loadBitmap("images/fade-gradient", &outErr); - BitmapView *fadeGradientBV = getBitmapView(fadeGradient); glyphs = pd->system->realloc(NULL, sizeof(LCDBitmap *) * numGlyphs * numFades); LCDBitmap *glyph = pd->graphics->newBitmap(glyphWidth, glyphWidth, kColorBlack); + pd->graphics->pushContext(glyph); for (int i = 0; i < numGlyphs; i++) { int column = i % spritesheetColumns; int row = i / spritesheetColumns; - /* - pd->graphics.lockFocus(glyph) - glyphSpritesheet:draw(-column * glyphWidth, -row * glyphWidth) - pd->graphics.unlockFocus() - glyphs[i] = {} - for j = 1, numFades do - local fade = (j - 1) / (numFades - 1) - local variant = glyph:copy() - glyphs[i][j] = variant - pd->graphics.lockFocus(variant) - fadeGradient:draw(fade * (glyphWidth - fadeGradient.width), 0) - pd->graphics.unlockFocus() - end - */ + pd->graphics->drawBitmap(glyphSpritesheet, -column * glyphWidth, -row * glyphWidth, kBitmapUnflipped); + for (int j = 0; j < numFades; j++) { + float fade = j / (numFades - 1.0); + LCDBitmap *variant = pd->graphics->copyBitmap(glyph); + glyphs[i * numFades + j] = variant; + pd->graphics->pushContext(variant); + pd->graphics->setDrawMode(kDrawModeWhiteTransparent); + pd->graphics->drawBitmap(fadeGradient, fade * (glyphWidth - fadeGradientWidth), 0, kBitmapUnflipped); + pd->graphics->popContext(); + } } - - pd->graphics->drawBitmap(glyphSpritesheet, 0, 20, kBitmapUnflipped); - pd->graphics->drawBitmap(fadeGradient, 0, 0, kBitmapUnflipped); + pd->graphics->popContext(); pd->graphics->freeBitmap(glyphSpritesheet); - freeBitmapView(glyphSpritesheetBV); pd->graphics->freeBitmap(fadeGradient); - freeBitmapView(fadeGradientBV); cells = pd->system->realloc(NULL, sizeof(Cell) * numCells); int i = 0; for (int x = 0; x < numColumns; x++) { float columnTimeOffset = randf() * 1000; - float columnSpeedOffset = random() * 0.5 + 0.5; + float columnSpeedOffset = randf() * 0.5 + 0.5; for (int y = 0; y < numRows; y++) { Cell *cell = &cells[i]; i++; cell->x = x; cell->y = y; - cell->glyphCycle = random(); + cell->glyphCycle = randf(); cell->columnTimeOffset = columnTimeOffset; cell->columnSpeedOffset = columnSpeedOffset; cell->glyphIndex = rand() % numGlyphs; cell->fadeIndex = -1; } } + + pd->graphics->clear(kColorBlack); } static int update(void* ud) @@ -251,10 +159,10 @@ static int update(void* ud) + 0.2 * sineTable[(int)(fmod(wobbleB * cellTime, 360))], 1 ); - int fadeIndex = brightness * numFades; - if (fadeIndex < 1) fadeIndex = 1; - if (fadeIndex > numFades) fadeIndex = numFades; + int fadeIndex = brightness * numFades; + if (fadeIndex < 0) fadeIndex = 0; + if (fadeIndex >= numFades - 1) fadeIndex = numFades - 1; if (cell->fadeIndex != fadeIndex) { cell->fadeIndex = fadeIndex; mustDraw = 1; @@ -266,21 +174,15 @@ static int update(void* ud) int glyphIndex = (cell->glyphIndex + (rand() % 20)) % numGlyphs; if (cell->glyphIndex != glyphIndex) { cell->glyphIndex = glyphIndex; - if (fadeIndex < numFades) { + if (fadeIndex < numFades - 1) { mustDraw = 1; } } } if (mustDraw) { - /* - pd->graphics->drawBitmap( - glyphs[cell->glyphIndex * numFades + cell->fadeIndex], - (cell->x - 1) * glyphWidth, - (cell->y - 1) * glyphWidth, - kBitmapUnflipped - ); - */ + LCDBitmap *glyph = glyphs[cell->glyphIndex * numFades + cell->fadeIndex]; + pd->graphics->drawBitmap(glyph, cell->x * glyphWidth, cell->y * glyphWidth, kBitmapUnflipped); } } @@ -295,7 +197,7 @@ int eventHandler(PlaydateAPI* playdate, PDSystemEvent event, uint32_t arg) if ( event == kEventInit ) { pd = playdate; - pd->display->setRefreshRate(0); + pd->display->setRefreshRate(30); pd->system->setUpdateCallback(update, NULL); init(); pd->system->resetElapsedTime(); diff --git a/playdate/matrix_lua/images/fade-gradient.png b/playdate/matrix_lua/images/fade-gradient.png index 7c8f97fd9f38a1dc6431889f3e84f8fe6553ba9d..3571ae816dc9cdf9070173a611245d1d980d7102 100644 GIT binary patch delta 1981 zcmbW2X;7015{8p-hCw9Ej39>t;neYygoGoXT#|4k$Q7fE1p+xGDhZHqgewF=Ma3k5 zD8gVI5(fw#h|F*ZBH$>9q7KSA7zA|`Fyt`*r3=b z*{BVJKp^}!1Gv<}5T+@s0i`gw69tU|Q5Xx{wC(O4QCg)~o&i46z<30!(Y43onr65Nnf2A9SJ8C*J>0mepi`CMK! zHx^{koE@kboHO9$;y?o!EG!yuLA$U3hO-j`#iF6sDr{UM)YVZ zXFrqShO|f8yPLcIe`NpLQvWsX7c#`Z(Y_w5NZkYRUsMIX>Jh@el`W4T5Dg`fu!E93 z_^$L;e4D2(er468xf%a7BflA{V+sORB>|x2K>p6^XA_u61l9Dn~pE zm#CQPTKb7=$UWci>it@ZOG8z8emQA+m5TvVf7|3fwxKFr`e%~o`GV`B1_`b!>3xiC za+}n4)vsCSR(ij`bo&aTbwbI7@m4f;FW%0mQWse8gjNcy-0krxBQwZb(Y6^W7R*ji z1(C(=q(t9F9A%$-f$4(W%V?8C25f!9^LvL9kvyDa;^7BkNZP`7Xua87_#2a`A*nys z8p|tld9*ZceO2eVJoEIMxUi2Q$|-V!sOyPdX1&*wLuRrGmFQZd&3>B%WodYGLjBnL z@70B;rNg?@~rhEq$?J-}R-d*IhmBcHo3F2!|L999LKS2cw6iwwts$1(b* zh4PP|jDAt*hZlWpY*4y#7__dllvMas>AAEdj`fSEltwfWnqRhD_`Re${MSjTc z`e*N+R=jxU1J&_8a>}Z>J-pofGM5~!)XY>DUd$inTK#;(TX!I!`F`K4YInr&>|lYF zv0_E3a7Jg0o+HmfsrwvtyWD-cGE-gvJ8Si`ib+>1fShqtKe9M9&c7{vZxx>K@^am) zNt=4zrtCQFj~i}?;UlD_n|sZT`Nq0V4&!E=5u0G`SsB6Rd3{fvWmz||P$x)_99Jr& zbgeIG=ZVtIOq;Ct=I7elK4iHr#UO;dr8KfJZ(JKe(S6qy6}-3?QU zDe$NGQorDKFZi2Vo*{deO&Zq~#>HCxNZoU0OMVpD;%~rXW*YN}QM|9)#K&UN4 zDV%_oJ{4SajcDmHy|dUQVQZ6lc^YNHNh0xrE?A8Xu-~WWlb)}ympfc|#(aZyM-u#s zpx0qwrxw<*%mM5D@$VjNz}H%GV&!8^!5yJ}uWVvB&S!6{K>z@E z@0Mh^W#A6iLy>z@AE@T49)(e7-fpJm%!oC!dv~2XrNKa4@eP^aM7+mOxKvG1j$y{4 z)K5lz>}(xS5T+D&*Rk!Ftb~tV9g!Lf(uv)xhrvuE<2fCtd*F)>(q|NuHe{upBny4x zWHqBR86(+8TFvwnc49{Zi)N~Bj_2R|6a=qKtjNuDzu{{qo{pv5`K`GiVMOZ3IXqQ9 zdj4DAqPkC%RK!!6J;R)MGK&MuI|*QpodKk(M^!Cnq30SE4<3p>3mvM7iLmbl1})F6 zIxLd+Ed7++(LJ3VN_RgPKH5-X@Y%Q6BU;U<(Y5n;KldHSc10bS%vn}mWr_xZi6V=U z1Qqva#M_}o|I7JV@`ta1v9ca%nc^hE??DWP49|<~w`kt&Gb=%?cv#DLo*qL7x{u_X zOEQ~0sN6J-_K`CEWtXwtFYrAt$Jxrjg zYe&w1L;CB-41BNcVEZ2z)EC#~-7R{1VQV0tmmDdwO{QuVe$Xk6*Pf8vfo;z2d*7tx zoyH3E@IePYCPjSqRdlj6V*2}bNz%hYXHJMfQK_T!M5OtzWcx*h8L^A^oxzzWcIVgi zeFwWvIz-MctXNepfm%a-*80O}j|Puh)~RO+`3AoEgDt~x&o)Q7T^hO(Y`CN*Cd};2 U4K=A=d(;r3cOc=iS5($N0JfcTP5=M^ delta 1600 zcmV-G2EX~G8KoAG83+ad008@-g%7Xk$Iu{x0glb!+>li~stlb!++lL!MalVAiU z26%07O-(wJx&u11tOGj%lZ6IZe@#qDQ~&@4*GWV{RCwC$T?vk>FbwtmFYP%&CvS5I zZIG&8QE(Q%<}G$Y)jjT!J34gF>RglUclbMcZgs5_@6G=r-;=Q#ymxiqr%wfcTeWpR z_mlkG<6yuO(g3uIt%9BtKMnjWNnys-g#+D_WT1)uHaRvFpc&v-fBMznbNP>8 zAmoENkN&?%--{5V0-wu&&+`(WO8|}r<~DxsaQqc{U=*yYx|P^1&V2{x!cpxgFrstA z_7MT4w0oe;4;ym@@F-zSfT%(ynDgkEkw4(-AAa;2w;yGmM~Gq7B@;hS09Zxc!B|dP zuBG0a<}gJj5H+k2Db~G6e>eb&v(g06v*Svu#1fRYqA*v44+V}QfeTTo41g)uBH*d0 zcoZ1ZWWXgVNdTXmlS=>&1^W)hddg)`Nrmf9pcXs>yeetf?WUk=)dg%;RXR#LY5BW~ znz7N9w8shn)U?Kgo1oMtqyX1#PbD+%7^`LpL{;A9xCCHO`Re}nC)miVO2&f4%5 zh{=Gbs*n|ev$E?G^%V`Y$)*s}U_DVY(BS+s%Ge<@Y+)hm2%zI?aeiHUoFQ2bU?z$o ztL8!Y+ns=~vG-5ulWHlmDt6L!P*V>I;5$m)vH#J+GpBEEettF1gmO>121+U+kzR0$ z1UuX(Wwn8-$ko(^f13uFST&J~ok;n(bEXoSpCSPLaJ#Cap8#W5O|wORN_&n=(c)MX z$3k&JoF#xM^~u=ds+%%hRqnbCPA!3B@uw8DO3+v}-bM_ydfz>dfUe>g>;%@T?{gJi zbtDSwb`p}QT7(37Nm%-7ksbnE%921fWuSt-nodxw?h)HKf0Yt&L7P>$^?~dlH3BMd zBjZ70DZtFkk(z&JK{{2ve+tm19F?~aimxO9eMpQ1DVcB^IuJiavQkSJ9Uyo2FK|~5 zAmD_$4J(IXA|>Eh_9$stkr|dqtlCK1@GQWp>R*|66~vROpHcf0*7>fK$E^W4+}=-J zpH2gypxvSVe@=k0YL_c9Qz-^#&jbX>+&(E4{$1I;KHCQ*9-^q3xk+rKqpTuG*xRhd zueb{8{gX=^cjwSkED24;Z({q~2_p7T1R((^6P=Zu3 zmcE(>P@IMWTo5K7kEr~FdS{&zhvX0oA!&<5!C+$9Av1@L zDrL#mG_Wh~N7XzSpPoi|HOTIYky>Lvl`=q7q8nqP){9>E zj&lk*e?n6f&N4rw&x-Bux(`!?VTvS7D%+~}sp`8Az)Q#g3zqKMe7C@k{oMgrroT?r zJvz5-jLF$QKp{tndQXuLrlvsM0vI79$xVk;#m{bKjHE6Y%iVD<6u?*JOR=;WqU0)V z)$urS6~ZL|qP6`5G1z?{qui(6ht%$ViMJave}uz%(GOdA#-;&~^GZRSqLfRR3&q|| z1Al=^yM%tDmN80p)FpcAb4V4`CGKI}6K5Pu?5eYD{S%47RriDaPAPsSuB&np^~u`q zYBf31cjZlbPWnT&;MElg?kj>(g<6I0Dc}$)1Vj-sgQ`;&I2|xUZFI-}b`&_{Rm6)*Pgq?GRSg6WiM{WvAA~v;B06 y)L6I1&C8cKc6VQYMMW#)dupBh)As!F>4854P~R+eQ*{IY0000