From d3c78f825c14b6a84f2f7a1d7b7bc6f1fae78640 Mon Sep 17 00:00:00 2001 From: Lovell Fuller Date: Sun, 4 Dec 2016 18:25:44 +0000 Subject: [PATCH] Ensure premultiply op occurs before box shrink #605 --- docs/changelog.md | 5 ++ src/operations.cc | 6 ++ src/pipeline.cc | 60 ++++++++++---------- test/fixtures/expected/crop-strategy.png | Bin 6148 -> 4487 bytes test/fixtures/expected/embed-16bit-rgba.png | Bin 988 -> 762 bytes test/fixtures/expected/embed-2channel.png | Bin 703 -> 732 bytes test/fixtures/expected/gamma-alpha.jpg | Bin 4612 -> 4338 bytes test/fixtures/expected/rotate-extract.jpg | Bin 5383 -> 5365 bytes test/fixtures/expected/sharpen-rgba.png | Bin 7268 -> 5005 bytes test/unit/extract.js | 2 +- test/unit/gamma.js | 2 +- 11 files changed, 42 insertions(+), 33 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index b68325c8e..afbb4f8f7 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -24,6 +24,11 @@ Requires libvips v8.4.2. [#601](https://github.com/lovell/sharp/issues/601) [@dynamite-ready](https://github.com/dynamite-ready) +* Ensure premultiply operation occurs before box filter shrink. + [#605](https://github.com/lovell/sharp/issues/605) + [@CmdrShepardsPie](https://github.com/CmdrShepardsPie) + [@teroparvinen](https://github.com/teroparvinen) + * Add support for PNG and WebP tile-based output formats (in addition to JPEG). [#622](https://github.com/lovell/sharp/pull/622) [@ppaskaris](https://github.com/ppaskaris) diff --git a/src/operations.cc b/src/operations.cc index 20adfd40a..3984235a3 100644 --- a/src/operations.cc +++ b/src/operations.cc @@ -300,6 +300,12 @@ namespace sharp { Calculate the intensity of edges, skin tone and saturation */ double AttentionStrategy::operator()(VImage image) { + // Flatten RGBA onto a mid-grey background + if (image.bands() == 4 && HasAlpha(image)) { + double const midgrey = sharp::Is16Bit(image.interpretation()) ? 32768.0 : 128.0; + std::vector background { midgrey, midgrey, midgrey }; + image = image.flatten(VImage::option()->set("background", background)); + } // Convert to LAB colourspace VImage lab = image.colourspace(VIPS_INTERPRETATION_LAB); VImage l = lab[0]; diff --git a/src/pipeline.cc b/src/pipeline.cc index 06093d856..617874065 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -290,9 +290,6 @@ class PipelineWorker : public Nan::AsyncWorker { ); } - // Calculate maximum alpha value based on input image pixel depth - double const maxAlpha = sharp::MaximumImageAlpha(image.interpretation()); - // Flatten image to remove alpha channel if (baton->flatten && HasAlpha(image)) { // Scale up 8-bit values to match 16-bit input image @@ -305,7 +302,6 @@ class PipelineWorker : public Nan::AsyncWorker { }; image = image.flatten(VImage::option() ->set("background", background) - ->set("max_alpha", maxAlpha) ); } @@ -324,7 +320,33 @@ class PipelineWorker : public Nan::AsyncWorker { image = image.colourspace(VIPS_INTERPRETATION_B_W); } - if (xshrink > 1 || yshrink > 1) { + // Ensure image has an alpha channel when there is an overlay + bool hasOverlay = baton->overlay != nullptr; + if (hasOverlay && !HasAlpha(image)) { + double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0; + image = image.bandjoin( + VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier) + ); + } + + bool const shouldShrink = xshrink > 1 || yshrink > 1; + bool const shouldReduce = xresidual != 1.0 || yresidual != 1.0; + bool const shouldBlur = baton->blurSigma != 0.0; + bool const shouldConv = baton->convKernelWidth * baton->convKernelHeight > 0; + bool const shouldSharpen = baton->sharpenSigma != 0.0; + bool const shouldCutout = baton->overlayCutout; + bool const shouldPremultiplyAlpha = HasAlpha(image) && + (shouldShrink || shouldReduce || shouldBlur || shouldConv || shouldSharpen || (hasOverlay && !shouldCutout)); + + // Premultiply image alpha channel before all transformations to avoid + // dark fringing around bright pixels + // See: http://entropymine.com/imageworsener/resizealpha/ + if (shouldPremultiplyAlpha) { + image = image.premultiply(); + } + + // Fast, integral box-shrink + if (shouldShrink) { if (yshrink > 1) { image = image.shrinkv(yshrink); } @@ -349,32 +371,8 @@ class PipelineWorker : public Nan::AsyncWorker { } } - // Ensure image has an alpha channel when there is an overlay - bool hasOverlay = baton->overlay != nullptr; - if (hasOverlay && !HasAlpha(image)) { - double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0; - image = image.bandjoin( - VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier) - ); - } - - bool shouldAffineTransform = xresidual != 1.0 || yresidual != 1.0; - bool shouldBlur = baton->blurSigma != 0.0; - bool shouldConv = baton->convKernelWidth * baton->convKernelHeight > 0; - bool shouldSharpen = baton->sharpenSigma != 0.0; - bool shouldCutout = baton->overlayCutout; - bool shouldPremultiplyAlpha = HasAlpha(image) && - (shouldAffineTransform || shouldBlur || shouldConv || shouldSharpen || (hasOverlay && !shouldCutout)); - - // Premultiply image alpha channel before all transformations to avoid - // dark fringing around bright pixels - // See: http://entropymine.com/imageworsener/resizealpha/ - if (shouldPremultiplyAlpha) { - image = image.premultiply(VImage::option()->set("max_alpha", maxAlpha)); - } - // Use affine increase or kernel reduce with the remaining float part - if (shouldAffineTransform) { + if (xresidual != 1.0 || yresidual != 1.0) { // Insert tile cache to prevent over-computation of previous operations if (baton->accessMethod == VIPS_ACCESS_SEQUENTIAL) { image = sharp::TileCache(image, yresidual); @@ -651,7 +649,7 @@ class PipelineWorker : public Nan::AsyncWorker { // Reverse premultiplication after all transformations: if (shouldPremultiplyAlpha) { - image = image.unpremultiply(VImage::option()->set("max_alpha", maxAlpha)); + image = image.unpremultiply(); // Cast pixel values to integer if (sharp::Is16Bit(image.interpretation())) { image = image.cast(VIPS_FORMAT_USHORT); diff --git a/test/fixtures/expected/crop-strategy.png b/test/fixtures/expected/crop-strategy.png index 1c8a3780f2a582566a4d1283e9930cd50ac2aed5..bec5122ed744418b858f784d5f74231f60614e03 100644 GIT binary patch literal 4487 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 literal 6148 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 pX)sIhBz3kk_@^; zw{VAGhmIXL>JmHT4(>28cG#g)5RC|;h(^@dU_J^O>7iD1QcKIQX^QsL=l}Wq?>4sE zzZKi}{QG~tAHIh_@Snr|&7)*8>0Vn~bL%Ocx)X_r9sT`*j(@>H?=8FinS4I)US3}I zewd%9uRI?5#%`x?IgU;=G|(3{HS}F)C;c=%O*c0;y|OI3S-DILY;A2(zuylUjpD)T zDjdZk)V4O<)#*TF89Ad7rK2MR=H@^K2Bvm)cBWQVRxms~thp6|TrLN_UJp%cDW^y&X{is;VLuBRD)HFq!fYgve_awwC26d7cbZwN;2t5kyB&YAk`f$DyUA1+}%c zxVpN+{=ODVOV4m{K(M#h2#?3Zf@WL5YPG_UG2k+FiGSezAVSp;iswc6cpnHMxVXq- zdwT)~!+QwAH&`r>(B0h~U|};YV6)j!U#SQ70MQ_VR0woL2US&3E|(FHKY=7gVXb(|)oK;8EF&I&3_RQy`N zYW)aFvSMb&A&!i=+^o!N2>^h}WC{S#ECv??!AEegPf-+<%VqTTnlV29Qgk>R*DAkJ zj*pMMv$L~wa&nSxY;63y;@?v!6x^q$r#CA92~j^Z4uX7Hr~m)}07*qoM6N<$f6UaIDZ34in>sQN9i(TysrDU67WI*ZYvbfEt zh{-aIBr_Ap8pD|}(E|_6NM@A8pkc`}CoW+|7Fg8Kn60cjBhDRAw{Ajp*bSlbQ-PI^ zA89GH^uPVS$0H3V%hzRGXK(W4&UL>B&jUyhjYa_gLo4sy zn+F+V)47zB6o06ztE1_usZ%{)U2IB)*DF~6chI=qkjLYK5nyWo<~BA&MIuo%HZ_IN z$@gD$4ZDPXo9#|zRn^Iwni}V02>`(2(qcn$HCYkz1ybdK03S1%*zAF4Wd2MRMvn(a zmzKDINa8P*RTN8a;QsH^;?w1O4(UtvOb% zWN_o~VcZ0Ri~~pM2xk9EQDh?~6KKNM*7o7|QrTQC0)JoiemSr4<9glC6jIN_z^kCv z=cDvVHHt(LiewV;WgiCm&FH^UO}B0-QC4P(Gsf1oL_lxSD-00>umaqz7{ySL3!i8^ zKu_c&JFA54CVE687e=gZM2^jYnMU7koc);9BH#~VBgfn5p zQgLuBjc`PTv9Xt7Fnoi&Jd?0vM+20~^9}&$bnkLRB$+L}2R;W{o^Q$4zf~{O?$ur)qFV2uw`(LZ@&biK zibNCHV%>_o@%2}6;>2rjtJT`>W5+spyZwVKW0I%f(Qy3e-~oV$*v$CM)!v?7YHe+$ z_V$lQiRf7XC@Oj-r{<5$z`y_iU~PHr|0`dcotu5b;c)yxLc Uy^&m*f&c&j07*qoM6N<$f?DL!1^@s6 diff --git a/test/fixtures/expected/embed-2channel.png b/test/fixtures/expected/embed-2channel.png index ca6f220aef4133bf2f5c2e7b67af6eb5d9668291..a15d715d1acd788d73622a91e67b4aeaebaee6a6 100644 GIT binary patch delta 687 zcmV;g0#N6OYIe(5xL_t(YiS1IqYZFl%|K45RH91OX5xOhggo5B&$k=w#O+ujH zB9xAbApHlrIXZW0i{O@yf-a>>!~~QOud$b;x#ph9CAl$$W~G$x`4?g$LT2WEzdtAziw_;gc^*X(2SI?o?^CDKiF&;r7mLMb z)6>)XwGG&|{eYR7h*TWMtd!y~3`r@)Qp!%N)#?O6!0mRMq?GJ=9(kS@+qNC=@9*E9 z#GknAGMmlnQ-4!acL4yRDAEuShzJl-BO)*iV^-5N2*VJFNFyR3B8{18W@bh>jx!Gc z-!Id+w4&|pZ3=?G1ppY0Mxc}er4+<*{M$rPba6O}A{Y*bpp^2i)c^oMx7&T!Y&J)h zWhD@i#|dGa_N);gA{d`@B9RbM%FoGUvN4H&;-I;47MMAql){T&K}4Ww8W@HFLI_N! z)1ujIzG*ZXdy}}YWB>qQd3o8b*XvKDlr2Ow5Rm`?bX^A_1PCF(G)=TDE75MZzj>bb zBA3f`nSWWk5d#1KD=RCXj*gBVot~ab(=>G<1evCJE}lxIj35YV^?E(Gw6s)VW=+#H zzAFDjoTbU<^NZzjxh|#TR;v|zo)`D~eLg%q{F2Y-?_GR*;~0%b`q;#M+qS>IKeki@ZzMlRk;(uawbv3oNwwAmO$4?CA V;M(w-Mfd;!002ovPDHLkV1k>#Od$XO delta 658 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 delta 4283 zcmYM1cR1VY`^P^hJsnz`L)Ebfm8wzOIa)MPN@5kEwL?i$n-Xp5sa>%uqH09UXzdYo z)JW|T;gF(6tlFb$#kW1@cb(t;$9+Bjysy{uyzlq($Gs&UCH1mhgN2QS{nQyYR<<+1 z&thR^JH^e;!6UBx2d~9_w8S+7*puYk)Zt#?1n- zvaqtVoMvTb|406R`X6$OKR6_=-A5F2 zj2W}nKeRX!5)@G;SjTlV6&^1?@)mP({H(V*e*Y47`)XiD7|+{@Z2G`0TGB)Wr}TW^ za(aXB1TCo>c1jWShxf*Y~BRV>j&x5 z&~%32aW`JDt>uedbGB|zgeQ3^>1E`LVGpwy z!q%z6L^Hl6O5zIa)+FUk^)7jZ4DU%8YZeZ?Uh)-2Xp&PLBBoA;O`(UO$A_xS2km(L zJVOZ;b0a^FA8nIfin;mVV;aMwt0P2WGyrh|_$}TT{*@f|iCmLnZ?yYNxC4^UYNPdxL5AYQlm#8|jkzYSi9^`AKKa`U7RSBy1fWGpr&uW;@S1BvsyAK2Q zia_+Vua{pb8aGc6@j?^Lbg3vgA<;|;+ zsoXN74jG-0jb--Dbe$M$F&NLHj*i~#SE=m4{e_6D& zLK>M==(8H`N{hE+EPhK1lP4@`bYJ-{)1tWAFL@9nO~=@_`dOq;pjiEp0m?{qT*+I4Kt8434z0EgtTz=WxK+OfuJ3R}K*MxLCj15se z51nMkf-BNIU{;RqTXt+(-jRGM5Vy#bR=$EM!7WY(BYj}l&E0B(XU_6B1K=;}|-nuhjPbR>7oIJ#y-pmzKv@Zk9IJ24q%-wMM03*O%zUEsXt z>L9%*oGfw60m8%>V7aQTJE8DRHoeW4A#i{cq0yB>q>@cNC-yG50n$PGz6Dx6OAGa)v=*KL+v%x0Yx+nHZymon^1TyDm)xsy=4|hze ze_K{6?qg=s+r(a@>+mu<^1o;OJg`yf0ci(|tU`-bDF)aYynK(kgEh%)q~Kb5k&K8j-KKd z&Bx=+?Vab{<-2k^#@8Y)#Uq#1M0Q;G$ic7BQH14=7Af=jxl#8P(v)$2YwHf?RZ9jg z0(0nogG`#LNRo<^^)|M$&)Bd^)u3D{^Z59pk7`3YN-j-Nhg8_#nHU~gR2}K!#|O}t zIMvoZ+6~7Arg>T9_&3Wj&5Yqxk}5s1dtL(-?^<6BW~2Q|8dVf8M()a0H#wl-;g%UE zz`c&!RlKxkafix<1F@q+qnPx~+3KU(6X4=ib~|MCh_IZG-ak&S=o9zoHQr;UWq0;| zBZuhMeOx8*%T%Jt~ z+dzG|zNWb!CUTQvwM4d)(Jm+6H#oMEP`cPc33+ZUlwnKO}*}Htwm@eKsF3 zJcef|YtxuA6yvM^V{Ld*VmM*}CBl3xQ{e}zn%NW!cq9(YkAEYtBPSM>X9_`EF!jhW z+qS@`sM0P{l*4>pL#?kQzfchga<2^=E~?9Zy?W|HZlrU-<@?p@CqUr{X^CWi*weLn zh?@5gN=wu0YDDi~ zGA=)EZ@o%mk(=I}J1YkOeBL^vtZ&W*37WBA5;NWg1IA5YmL?^}wiJt8J?UO@!6;9c z4B6Zu()B$bedZHi6uKh?M%1dbD6BN~L4qpu2FUv&UaI$vG-9{I8F>OPZup=?JW)ED zF7ZwA3O&TTYi%IhL5f#E11t%j5Q3qME*H9^;nhK*%rkA?P@{aQfY(#tSAb@c5#Tyb z*R0Vz$ex(Hz4Bfsu*=tu@HRhs9D;rv|KUaPQ;(2|&mD=%BMN}rYkP-azG4k3QNFL} zgM97OO@nP^ZZdhn+Y5x-?Z0dpgLRpWu;|OzduJ8y= z5|%YL)0HGaU0?G*m?$UQH9*)vLP$i9g4O8e@bSdI(RZ$d&k2V|^5EEDl@k24|JEhw6Tb?uVw zk~r{Ru$aNy%euJht>oP18LZ8|>#XVBk|$MP>T<8H<{S>4p^It%-tu(Db64~oROxl5 zZ(j33j%)l~6_<`p$dD*|UvqhORVms|^<6)V!FYDgWZ1u_@zt?q(025h1I_(y4F!5n z+;}sAhNR_~F7$NyEBA?@&&=<+|%U?{3N% zm5aA(#`~0-paCEY)X~TA5mSOFw7aQ8p^+DrS5z4yq+Z$%7UT1+(`kNMUIQZAwXMg* zX`cbZkoy}tBwoBO1gdxER8C3#37}WOe9*(p7o#5H*Kem0+*XKly`hP!Rpb3!Cjbr^ zKA@2e85qSA=C^FEgOg&J3)-KE7>viM&ugorX*W-RXBP@vh;ZgAG-qe2<^2UIq1B2m z-8}9-r9u+TK=W7)o3aF=4h%+^y3K!D4uW*rT)>M}L43hPUf6lTVMa@sWQetCSDg&~ zFBaabS9l3rrW?7eade8fxk>cd*X(V_!`aD(fGU(4y=mWlX;Wl$+iFEc_FF3Q*p1 zwAxv;gZ)*t@JCeBI5@7@i6E!bl7wzuKB(z0whtxOA&HCdBsr#24EELhl=17?%Y*l( zcX6#&6v#!|=4Epmg=m~CcieYX!6R2|7`o07|Q z3BwBIU#1UR$ysJ=R(OHW$DbOZ-kq$ft{Z8xDjI}XrfGGBZO(4ns#|{Ro842a-|HkE zTnd)g_^OxKDdwq>C^X*S!3@mlH6fMP`k}p|@$hy0_Rg*0$yI^;Ks>^?`{GzZT?Cm~ ti2sFRy92kN1V1m-eB_q>9-%J+qNBj|Hl6Z3<6$mo^A|`j0_BnK*+F>>9Ys`p$Z6x diff --git a/test/fixtures/expected/sharpen-rgba.png b/test/fixtures/expected/sharpen-rgba.png index 841511cac3090cb1e035d1134e3e4e9f92f53ea3..e4a29845aed9634e25eb088c68c91148ec912732 100644 GIT binary patch literal 5005 zcmds5_fr!Hw+&so!ly_P5RfQH?*c)P79g+-B7z8fYeb6F7(z?vvMbUOTnJr>ARVQ* z0P0FH5NV+XvMYwACKw@v6rS(Dc=KlNx%d2X@64SubMMUg;}>@)NihX6001BfcDDBf z0QheH8tv>00|3Nj{ug{RWE2MgkhKBZ+un{Z-yyqB$x^(hjT2&}d`F>4DQ&D}VduoTL zMCu0v(Z^xD%HOyoey31|{oV)UA|t&|@FJi%ypP3&1o8Li?HB>>BuWeHPcxP?Qs2%) z@;Yw(?2EY+El#IU4-zd~&I4#-N_I1WBU}I}@Mf{_yEU=x{ovBB1aoj$Y8xtl$2=Ms zTzjhAw9YN0KK!IyNfTh3q^wV!G?;k%nH4Goc8@2SkrwbF$b0hx!2o}Jm=>y{d_cBz zbmw3(4-GJ1ONit*4~an641W3XK1#mw@UAn$4F0}F*p%OfWEGI#Su2UVu+q$j%ION# zYsLwrKH@Jc8+FyWr4Hi|{`q$;`mrh`$QfW&b|??1OYJYX8PFn<=UcWBqGu?^5v5C@ zrPrM%<~LHS*5%^O>9WJ;hVRT&4W=-Xo!aenjRYXX5XMb7WSW{R#So@!7iiHNnx?qW~EimiV6x)%qkVXVV2}K44S6;mQ zjjkG`BJ&tHqDgO?8{vu7+j`qj5*vlq97|Qm3KZtpc{swzE{|=zLo>gnYk03s;!n8( z4pWq}NbQZBh4&FXu>3s9o}#7+g`I>zZ#%rCziZu{tr3;H8}fnbeDvd=dT5tmG`K&9 zL58gtG)x#}nqE(&9w6rdjf6`Qt5qErg1Fya{8!-$WSs`sy5FzK6+7EiN z{mEiBLIde@3IPJim50;ce%b2pVqM$0lHZh>U0`*j8|!uyRk<#qO{1x#-g_h<&5Zd zK_lHx(^>XJmr(9%e*MMNrs>KJfn#wr)vI*j!o+O9YwGS1MCVf&HRpvX#7`-oPgewg z^E)qi26+4!ou4OMDaE-=kCov}Y0B-k#4H2M&z22;@WQgRCuv{PJdEx}ym5S;$A>!K zQ)v8hXms23C8Cfz3Yz?SZbro1fuB-*f~yOA2t@O+51hTIjS}5pzZL6D;}l|@n zW#i+u82-1y((xj&Sv2OhF43+7(-c;G_1Vykz2|-pyB_ z6nzVtI5H4%p9LCL94|L38RAHxr}lHL?pPi}(D+pmei~4HxB$8A;HL;Tuq^5dHglMI zH1vbyIko&L{pZ0mauC~S9zL&;K{QKg(R^fg(&4UFQz}s=J)Q6}`cZLQF4 zl?@U#CISJBh~DDvPyBAfIZ%bFuWNsJNyLz;u(QvVC+=FI6zK8j);zV%I_5E{H{fdZ ze3Ii>*7jP)l@!O53ymP3P?XdbE$P9g_3XxPbC)K`#XJ){YJDhX1OdMY$Zm9}znG*| zJw`FbgW$U!fB;D8lN9iZP7Fv?)#SLX$jxV^1lm?R+d{4DmXFak60m2o>rzM2sw|aB zn4+`MvD(v>K)A-1z`&aXD{!9A-aVlrElyb~vmlt&IA%be9w1#Z02Re?Ly@TTrO{-8 z&nm0+wAI$7;Gvt|#0d2aK7^*pE+%yyNR|X1CI@owBdeVzF{Eu6NIBlXm&ToR*#F@9 z{Q(Vk2oyNAyBFZXG>=lFhjN z126jpbz=kgHn_N;Q_X9?NQ?0L7)vgvc<5k=#hG}si1wbYgLn#jay_RZ^vBz*qoYum zMZ?n38ToIZ0>OiR%mv{Nw%;cE+At%8J)S7wBsApZyuKVq$=TpEcT9F5SMxPC8TnY> z{zi(Q9PWT>lp#2bd@I~js|5&fFMS9!LI5%)XhRamXX(-!uzfWk*UwOa^MyK?vsF_+ zK4v49{O4-@$#Wr)S7<|TDtY;`|H9|T0$kHs;)z`;XI;zImxzv38p5hvu=hUH zkUkCVmD{+h@&(r&_HGtua?p>H?p+`DmDFFAJGP*x;~ZD_q)J@>dZsfhGQPo>P|r}# zVSgdbR6$NAY*rYTIx8XpyHXQ}UToi;WRYNiQp+~1@9BR$Ll#Wp%F+*+Ud~Ug>Jt31 zU4|e2!pW~TS6qj7uy|h15r2D(U){PQp4`%9VH=i8uKlP(s62C@mhwycAhE!La?#xNm-lJDap))c=<#-~Mj%JENq-XONdfLqss4ss+5i1;E9C4Ra+A-B&arlw!Pf??s4_ z3C=sm&vBCLLoq?POPc^s;{4zZ@u;sWP3O^xn0DRk6xZV3Z;RpXOBVvyevh70(MwX;RCmW*Vk2v_!tAPj+$0 znSZo#>_?5_7=EBJfs3CcW=($m=I(?In^|cckAt?WunSFEJXF)7ffjm!msxcu*)+lO zwE>>H;TgmY`lF7FEAY`*qXpkeS1h;8<>O@;O!+li-A{FB&TZ~sMwiz50L@K)pf5>}IDHu1anGR79$cyzQeT z!h2@iNL1t6T9yn6FE+#X!@8CtT#}2IvT9Dg=&A|rea2N+{`I{q8-}fJ<~zDDa4)3H z_0+}DrOGMt8{AM{2-KGlWrm#hh|?A2_YmTBC+uOMOY>RKUXqT+@$+_$0!0$%xYQbq z)L0tgf+J4&mzUQsZYeC;CC#Qfp%wnYb|4evFyhm`lW|%>Q6b7IYssfzDEzOBdk5Ih zdT8-&R)-0_G|YIxf+l4g5qiY--6#o|P4F0OJrU3bM27H`H9W{}Nddw)Y}_-I@i*wj zg^fq1Xp7TUMrAHedL#B^{rGLmqEEs5WBb_7@>bHXvcVp}k)6ZIxPUVpeujtOel0tl z3hTeoimWhgkMllz;?J06bJ`$i)j6VWEWr_cIZ0+qntk3g9)GhmPrrYORPoWB$aqwU z@BZq%E_Xfo`li$li*t1jt@W)QdladP9mXAZBw<<+G#tViet-JOJ=nkP+NzR$ma zVf3GmR~$!l7lKyG#+0UJ6Y(|Qsn+1LJ8=;|NblKOM~HRDKYw4#S>-%IV`m3UpB&Zm zQ+^Y+<;li}GP?PqbKMHbZLLhUtkm|Vji6hx^gAqRRog6S)c zEi(}&(A5T#^_!Xl}f=(>D$xi{^h0{4Vk)XScL{_wx9cbShjMs3e7%$GF*m0!3q+})mkw4?2`u!qpJD4V7kt-pI<~PKhvVa41Luk z3%52@gz6V_hI5|wR+$9NvnzZG6c%X4`TgwM>}ZfYc}QFII{A;o`|3DX9GfMkHd{f2 zV5-{-v97c}7R$<0(K<&E*Y}NcN9H5y=Dk}=Tdcv(1g<@$2KvvTC=_~Z|4wEDvk|E$ zO;f_`jAy;N^Ni3bh6^tM&!+N4zrO9rxsFX?UPaq#0YJ~5%cP{m>CpYx-&P1#0cUIg zB+pg>aP!|0lU>S?+Vs_r|J=b4ynm-^1vPGYLG2ZTRoYh)mGw+0heo@@K2(?W^Bc)2 z{q4v^9zK^4bsbhwR*L;%t?UIGIK4BYhlCRu^Xp01dfV1s7<-R`7e3_Tj>vky6f2Ia z&a$8#CtPU3=4)KztJ^7v*HLm?S~oJ?TGs7P5|}R|&OLs0>1zFeA97K>kS52eenHlG3E}xrKcTQtn zaBcVG3%W*i9o3#Lvs*{;I*4BkLZib7A@6%0x4nz}5;!!{M%kE{2TE)hcj*bh0@1U0a4*GFf(4 z2OFmo9Fm+$u*qcQ!=hBNdL_3ZySLwM*LSDvheCs>YBNAy+>Bs~3*$szJJefoHM=M` zEAo5e!#A?bSkK(ngs_#6I)m#SweKvKj!g|kJAEFuu`%x3(uIZVW`)zAKZCvEU#|Fq zS3-g5t#etm`0f=O@hzrh#&iFw@m=}y3;uCl5Fu|JIt>i4Dt2>tvvMjC%dZ6+$S{`7+e^SN2nXvuyw`YY@QNJx zEhig|AL=|K=H0*3qGM{l$G5Ff>0CN!qbeZFjeOWqP^HODZT*+E-?A3`S2dGwZmseA zbkg{#k8ilC4^#ieNPvAKN$Z|Dhv9+RTo6 z_i!yd${^sDuor2^CSYOzbsreyB20X&6Ve@b7JCbx$XJwUeBD4{x)}PR47xuSlL$uo zfUuZ`*KJvDC9S944F8!{&aR0wMx*)Jb~av)rJ_XC&^D+YUk-5czq0qXZ`Nh)rsjO0 zQ&OWoc)7;AdL@5#sh78h>$CwUW$pjC>SO1;tepV-&;MnUXmab6uXn#L-8FnM@Tb29 N06Vzbx7Y=x{|}4#*#ZCn literal 7268 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