From acc6822d0e506cccceac5e4db07f33c7ef5e09f5 Mon Sep 17 00:00:00 2001 From: treeform Date: Sat, 21 Nov 2020 16:51:29 -0800 Subject: [PATCH] Add drawFast4 integer-matrix. --- src/pixie/images.nim | 65 +++++++--- tests/benchmark_draw.nim | 119 ++++++++++++++++++ ...hmark_images.nim => benchmark_inplace.nim} | 8 +- tests/images/benchDrawFast1Copy.png | Bin 0 -> 328 bytes tests/images/benchDrawFast2Copy.png | Bin 0 -> 328 bytes tests/images/benchDrawFast2Normal.png | Bin 0 -> 328 bytes tests/images/benchDrawFast2Rotation.png | Bin 0 -> 339 bytes tests/images/benchDrawFast2Saturation.png | Bin 0 -> 337 bytes tests/images/benchDrawFast3Copy.png | Bin 0 -> 339 bytes tests/images/benchDrawFast3Normal.png | Bin 0 -> 328 bytes tests/images/benchDrawFast3Rotation.png | Bin 0 -> 329 bytes tests/images/benchDrawFast3Saturation.png | Bin 0 -> 337 bytes tests/images/drawFast1.master.png | Bin 0 -> 328 bytes tests/images/drawFast1.png | Bin 0 -> 328 bytes tests/images/drawFast2.master.png | Bin 0 -> 328 bytes tests/images/drawFast2.png | Bin 0 -> 328 bytes tests/images/drawFast3.master.png | Bin 0 -> 358 bytes tests/images/drawFast3.png | Bin 0 -> 358 bytes tests/images/drawFast3Rot.master.png | Bin 0 -> 375 bytes tests/images/drawFast3Rot.png | Bin 0 -> 375 bytes tests/images/drawFast4.master.png | Bin 0 -> 336 bytes tests/images/drawFast4.png | Bin 0 -> 336 bytes tests/images/drawFast4Rot.master.png | Bin 0 -> 336 bytes tests/images/drawFast4Rot.png | Bin 0 -> 336 bytes tests/images/drawSmooth.png | Bin 356 -> 358 bytes tests/images/inplaceDraw.bmp | Bin 40122 -> 40122 bytes tests/test_images.nim | 26 ++-- 27 files changed, 187 insertions(+), 31 deletions(-) create mode 100644 tests/benchmark_draw.nim rename tests/{benchmark_images.nim => benchmark_inplace.nim} (90%) create mode 100644 tests/images/benchDrawFast1Copy.png create mode 100644 tests/images/benchDrawFast2Copy.png create mode 100644 tests/images/benchDrawFast2Normal.png create mode 100644 tests/images/benchDrawFast2Rotation.png create mode 100644 tests/images/benchDrawFast2Saturation.png create mode 100644 tests/images/benchDrawFast3Copy.png create mode 100644 tests/images/benchDrawFast3Normal.png create mode 100644 tests/images/benchDrawFast3Rotation.png create mode 100644 tests/images/benchDrawFast3Saturation.png create mode 100644 tests/images/drawFast1.master.png create mode 100644 tests/images/drawFast1.png create mode 100644 tests/images/drawFast2.master.png create mode 100644 tests/images/drawFast2.png create mode 100644 tests/images/drawFast3.master.png create mode 100644 tests/images/drawFast3.png create mode 100644 tests/images/drawFast3Rot.master.png create mode 100644 tests/images/drawFast3Rot.png create mode 100644 tests/images/drawFast4.master.png create mode 100644 tests/images/drawFast4.png create mode 100644 tests/images/drawFast4Rot.master.png create mode 100644 tests/images/drawFast4Rot.png diff --git a/src/pixie/images.nim b/src/pixie/images.nim index e82cbdc..9fa307d 100644 --- a/src/pixie/images.nim +++ b/src/pixie/images.nim @@ -136,9 +136,9 @@ proc getRgbaSmooth*(image: Image, x, y: float64): ColorRGBA {.inline.} = proc hasEffect*(blendMode: BlendMode, rgba: ColorRGBA): bool = ## Returns true if applying rgba with current blend mode has effect. case blendMode - of Mask: + of bmMask: rgba.a != 255 - of COPY: + of bmCopy: true else: rgba.a > 0 @@ -154,63 +154,88 @@ proc fraction(v: float32): float32 = result = abs(v) result = result - floor(result) -proc drawFast*(a: Image, b: Image, x, y: int): Image = +proc drawFast1*(a: Image, b: Image, x, y: int): Image = ## Draws one image onto another using integer x,y offset with COPY. result = newImage(a.width, a.height) for yd in 0 ..< a.width: for xd in 0 ..< a.height: var rgba = a.getRgbaUnsafe(xd, yd) - if b.inside(xd + x, yd + y): - rgba = b.getRgbaUnsafe(xd + x, yd + y) + if b.inside(xd - x, yd - y): + rgba = b.getRgbaUnsafe(xd - x, yd - y) result.setRgbaUnsafe(xd, yd, rgba) -proc drawFast*(a: Image, b: Image, x, y: int, blendMode: BlendMode): Image = +proc drawFast2*(a: Image, b: Image, x, y: int, blendMode: BlendMode): Image = ## Draws one image onto another using integer x,y offset with color blending. result = newImage(a.width, a.height) for yd in 0 ..< a.width: for xd in 0 ..< a.height: var rgba = a.getRgbaUnsafe(xd, yd) - if b.inside(xd + x, yd + y): - var rgba2 = b.getRgbaUnsafe(xd + x, yd + y) + if b.inside(xd - x, yd - y): + var rgba2 = b.getRgbaUnsafe(xd - x, yd - y) if blendMode.hasEffect(rgba2): rgba = blendMode.mix(rgba, rgba2) result.setRgbaUnsafe(xd, yd, rgba) -proc drawFast*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode): Image = +proc drawFast3*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode): Image = ## Draws one image onto another using matrix with color blending. result = newImage(a.width, a.height) + var matInv = mat.inverse() for y in 0 ..< a.width: for x in 0 ..< a.height: var rgba = a.getRgbaUnsafe(x, y) - let srcPos = mat * vec2(x.float32, y.float32) + let srcPos = matInv * vec2(x.float32, y.float32) if b.inside1px(srcPos.x, srcPos.y): let rgba2 = b.getRgbaSmooth(srcPos.x, srcPos.y) if blendMode.hasEffect(rgba2): rgba = blendMode.mix(rgba, rgba2) result.setRgbaUnsafe(x, y, rgba) -proc draw*(a: Image, b: Image, mat: Mat3, blendMode = Normal): Image = +proc drawFast4*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode): Image = + ## Draws one image onto another using matrix with color blending. + result = newImage(a.width, a.height) + var matInv = mat.inverse() + for y in 0 ..< a.width: + for x in 0 ..< a.height: + var rgba = a.getRgbaUnsafe(x, y) + let srcPos = matInv * vec2(x.float32, y.float32) + if b.inside(srcPos.x.floor.int, srcPos.y.floor.int): + let rgba2 = b.getRgbaUnsafe(srcPos.x.floor.int, srcPos.y.floor.int) + if blendMode.hasEffect(rgba2): + rgba = blendMode.mix(rgba, rgba2) + result.setRgbaUnsafe(x, y, rgba) + +proc draw*(a: Image, b: Image, mat: Mat3, blendMode = bmNormal): Image = ## Draws one image onto another using matrix with color blending. if mat[0, 0] == 1 and mat[0, 1] == 0 and mat[1, 0] == 0 and mat[1, 1] == 1 and mat[2, 0].fraction == 0.0 and mat[2, 1].fraction == 0.0: # Matrix is simple integer translation fast path: - if blendMode == Copy: - echo "use 1" - return drawFast( + if blendMode == bmCopy: + #echo "use 1" + return drawFast1( a, b, mat[2, 0].int, mat[2, 1].int ) else: - echo "use 2" - return drawFast( + #echo "use 2" + return drawFast2( a, b, mat[2, 0].int, mat[2, 1].int, blendMode ) + let ns = [-1.float32, 0, 1] + if mat[0, 0] in ns and mat[0, 1] in ns and + mat[1, 0] in ns and mat[1, 1] in ns and + mat[2, 0].fraction == 0.0 and mat[2, 1].fraction == 0.0: + echo "use 4" + return drawFast4( + a, b, mat, blendMode + ) + # Todo: if matrix is a simple flip -> fast path - echo "use 3" - return drawFast(a, b, mat, blendMode) + # 4 rotation x 3 flips = 12 combo + #echo "use 3" + return drawFast3(a, b, mat, blendMode) -proc draw*(a: Image, b: Image, pos = vec2(0, 0), blendMode = Normal): Image = - a.draw(b, translate(-pos), blendMode) +proc draw*(a: Image, b: Image, pos = vec2(0, 0), blendMode = bmNormal): Image = + a.draw(b, translate(pos), blendMode) diff --git a/tests/benchmark_draw.nim b/tests/benchmark_draw.nim new file mode 100644 index 0000000..2fa19f1 --- /dev/null +++ b/tests/benchmark_draw.nim @@ -0,0 +1,119 @@ +import pixie, chroma, vmath, fidget/opengl/perf, pixie/fileformats/bmp + +timeIt "benchDrawFast1 COPY": + var tmp = 0 + var c: Image + for i in 0 ..< 1000: + var a = newImage(100, 100) + a.fill(rgba(255, 0, 0, 255)) + var b = newImage(100, 100) + b.fill(rgba(0, 255, 0, 255)) + c = a.drawFast1(b, x = 25, y = 25) # Copy + tmp += c.width * c.height + c.writeFile("tests/images/benchDrawFast1Copy.png") + echo tmp + +timeIt "benchDrawFast2 COPY": + var tmp = 0 + var c: Image + for i in 0 ..< 1000: + var a = newImage(100, 100) + a.fill(rgba(255, 0, 0, 255)) + var b = newImage(100, 100) + b.fill(rgba(0, 255, 0, 255)) + c = a.drawFast2(b, x = 25, y = 25, bmCopy) + tmp += c.width * c.height + c.writeFile("tests/images/benchDrawFast2Copy.png") + echo tmp + +timeIt "benchDrawFast3 COPY": + var tmp = 0 + var c: Image + for i in 0 ..< 1000: + var a = newImage(100, 100) + a.fill(rgba(255, 0, 0, 255)) + var b = newImage(100, 100) + b.fill(rgba(0, 255, 0, 255)) + c = a.drawFast3(b, translate(vec2(25, 25)), bmCopy) + tmp += c.width * c.height + c.writeFile("tests/images/benchDrawFast3Copy.png") + echo tmp + +timeIt "benchDrawFast2 Normal": + var tmp = 0 + var c: Image + for i in 0 ..< 1000: + var a = newImage(100, 100) + a.fill(rgba(255, 0, 0, 255)) + var b = newImage(100, 100) + b.fill(rgba(0, 255, 0, 255)) + c = a.drawFast2(b, x = 25, y = 25, bmNormal) + tmp += c.width * c.height + c.writeFile("tests/images/benchDrawFast2Normal.png") + echo tmp + +timeIt "benchDrawFast3 Normal": + var tmp = 0 + var c: Image + for i in 0 ..< 1000: + var a = newImage(100, 100) + a.fill(rgba(255, 0, 0, 255)) + var b = newImage(100, 100) + b.fill(rgba(0, 255, 0, 255)) + c = a.drawFast3(b, translate(vec2(25, 25)), bmNormal) + tmp += c.width * c.height + c.writeFile("tests/images/benchDrawFast3Normal.png") + echo tmp + +timeIt "benchDrawFast2 Saturation": + var tmp = 0 + var c: Image + for i in 0 ..< 1000: + var a = newImage(100, 100) + a.fill(rgba(255, 0, 0, 255)) + var b = newImage(100, 100) + b.fill(rgba(0, 0, 0, 255)) + c = a.drawFast2(b, x = 25, y = 25, bmSaturation) + tmp += c.width * c.height + c.writeFile("tests/images/benchDrawFast2Saturation.png") + echo tmp + +timeIt "benchDrawFast3 Saturation": + var tmp = 0 + var c: Image + for i in 0 ..< 1000: + var a = newImage(100, 100) + a.fill(rgba(255, 0, 0, 255)) + var b = newImage(100, 100) + b.fill(rgba(0, 0, 0, 255)) + c = a.drawFast3(b, translate(vec2(25, 25)), bmSaturation) + tmp += c.width * c.height + c.writeFile("tests/images/benchDrawFast3Saturation.png") + echo tmp + + +timeIt "benchDrawFast4 Rotation": + var tmp = 0 + var c: Image + for i in 0 ..< 1000: + var a = newImage(100, 100) + a.fill(rgba(255, 0, 0, 255)) + var b = newImage(100, 100) + b.fill(rgba(0, 0, 0, 255)) + c = a.drawFast4(b, translate(vec2(25, 25)) * rotationMat3(PI/2), bmNormal) + tmp += c.width * c.height + c.writeFile("tests/images/benchDrawFast2Rotation.png") + echo tmp + +timeIt "benchDrawFast3 Rotation": + var tmp = 0 + var c: Image + for i in 0 ..< 1000: + var a = newImage(100, 100) + a.fill(rgba(255, 0, 0, 255)) + var b = newImage(100, 100) + b.fill(rgba(0, 0, 0, 255)) + c = a.drawFast3(b, translate(vec2(25, 25)) * rotationMat3(PI/2), bmNormal) + tmp += c.width * c.height + c.writeFile("tests/images/benchDrawFast3Rotation.png") + echo tmp diff --git a/tests/benchmark_images.nim b/tests/benchmark_inplace.nim similarity index 90% rename from tests/benchmark_images.nim rename to tests/benchmark_inplace.nim index 9912797..4bb6f9f 100644 --- a/tests/benchmark_images.nim +++ b/tests/benchmark_inplace.nim @@ -1,6 +1,6 @@ import pixie, chroma, vmath, fidget/opengl/perf, pixie/fileformats/bmp -proc inPlaceDraw*(destImage: Image, srcImage: Image, mat: Mat3, blendMode = Normal) = +proc inPlaceDraw*(destImage: Image, srcImage: Image, mat: Mat3, blendMode = bmNormal) = ## Draws one image onto another using matrix with color blending. for y in 0 ..< destImage.width: for x in 0 ..< destImage.height: @@ -14,7 +14,7 @@ proc inPlaceDraw*(destImage: Image, srcImage: Image, mat: Mat3, blendMode = Norm rgba = blendMode.mix(destRgba, srcRgba) destImage.setRgbaUnsafe(x, y, rgba) -proc inPlaceDraw*(destImage: Image, srcImage: Image, pos = vec2(0, 0), blendMode = Normal) = +proc inPlaceDraw*(destImage: Image, srcImage: Image, pos = vec2(0, 0), blendMode = bmNormal) = destImage.inPlaceDraw(srcImage, translate(-pos), blendMode) block: @@ -30,7 +30,7 @@ block: a.fill(rgba(255, 0, 0, 255)) var b = newImage(100, 100) b.fill(rgba(0, 255, 0, 255)) - var c = a.draw(b, pos=vec2(25, 25)) + var c = a.drawFast3(b, translate(vec2(25, 25)), bmNormal) writeFile("tests/images/copyDraw.bmp", c.encodeBmp()) timeIt "inPlaceDraw": @@ -51,6 +51,6 @@ timeIt "copyDraw": a.fill(rgba(255, 0, 0, 255)) var b = newImage(100, 100) b.fill(rgba(0, 255, 0, 255)) - var c = a.draw(b, pos=vec2(25, 25)) + var c = a.drawFast3(b, translate(vec2(25, 25)), bmNormal) tmp += c.width * c.height echo tmp diff --git a/tests/images/benchDrawFast1Copy.png b/tests/images/benchDrawFast1Copy.png new file mode 100644 index 0000000000000000000000000000000000000000..ef62fc50f96ab6b5a60e3c0a77a6f30d92b90a53 GIT binary patch literal 328 zcmeAS@N?(olHy`uVBq!ia0vp^DIm2lMBcR{!O1$Zz`$bgboFyt=akR{0K(;P AN&o-= literal 0 HcmV?d00001 diff --git a/tests/images/benchDrawFast2Saturation.png b/tests/images/benchDrawFast2Saturation.png new file mode 100644 index 0000000000000000000000000000000000000000..340186df3a41fb03f4ecc7c5539b4dcec13d332f GIT binary patch literal 337 zcmeAS@N?(olHy`uVBq!ia0vp^DIm%R&Da ztyv4ycP;eUpwXnHoi<5$dHK$?b;-{*9GjN>Ou~qzQ&7cif`nrd@r)VqO>b_SJ>PY! u{jboInCr(M_bM1R9pMmC_UIYJ4D&MPu-PuU8tuTKV(@hJb6Mw<&;$S#u4!EW literal 0 HcmV?d00001 diff --git a/tests/images/benchDrawFast3Copy.png b/tests/images/benchDrawFast3Copy.png new file mode 100644 index 0000000000000000000000000000000000000000..a41ca36289af08e14a2f7b8a8c366b0e2872e606 GIT binary patch literal 339 zcmeAS@N?(olHy`uVBq!ia0vp^DImS;!;Sf?LmNB1wXWBa3&2R78@o!51 zHpg=Fo!P(DPka))FQno&LBcVKrBiV5GM+Im{_-ttVtBxAU|=zLy85}Sb4q9e0B@mZ A#{d8T literal 0 HcmV?d00001 diff --git a/tests/images/benchDrawFast3Normal.png b/tests/images/benchDrawFast3Normal.png new file mode 100644 index 0000000000000000000000000000000000000000..ef62fc50f96ab6b5a60e3c0a77a6f30d92b90a53 GIT binary patch literal 328 zcmeAS@N?(olHy`uVBq!ia0vp^DImF!PC{xWt~$(69BY}WuE{5 literal 0 HcmV?d00001 diff --git a/tests/images/benchDrawFast3Saturation.png b/tests/images/benchDrawFast3Saturation.png new file mode 100644 index 0000000000000000000000000000000000000000..340186df3a41fb03f4ecc7c5539b4dcec13d332f GIT binary patch literal 337 zcmeAS@N?(olHy`uVBq!ia0vp^DIm%R&Da ztyv4ycP;eUpwXnHoi<5$dHK$?b;-{*9GjN>Ou~qzQ&7cif`nrd@r)VqO>b_SJ>PY! u{jboInCr(M_bM1R9pMmC_UIYJ4D&MPu-PuU8tuTKV(@hJb6Mw<&;$S#u4!EW literal 0 HcmV?d00001 diff --git a/tests/images/drawFast1.master.png b/tests/images/drawFast1.master.png new file mode 100644 index 0000000000000000000000000000000000000000..ef62fc50f96ab6b5a60e3c0a77a6f30d92b90a53 GIT binary patch literal 328 zcmeAS@N?(olHy`uVBq!ia0vp^DImqY-8 zMt;+FmbPo69jjQFIGGZ%-xZuYKl^{d^zUb?XWv}2F?IIMHXg?$mQFzxw+Rx&GoDRR zn10;m`|my9Zp(bDUe0p!{pVSw*LLozFZY{pTjqZGA61VY1;eHz974*2mGPPJS^pQI Uh@UE7fZ@mB>FVdQ&MBb@0K`Ukc>n+a literal 0 HcmV?d00001 diff --git a/tests/images/drawFast3.png b/tests/images/drawFast3.png new file mode 100644 index 0000000000000000000000000000000000000000..bef6ff3065e9f5f415fa8ffbba15d064adcd70a1 GIT binary patch literal 358 zcmeAS@N?(olHy`uVBq!ia0vp^DImqY-8 zMt;+FmbPo69jjQFIGGZ%-xZuYKl^{d^zUb?XWv}2F?IIMHXg?$mQFzxw+Rx&GoDRR zn10;m`|my9Zp(bDUe0p!{pVSw*LLozFZY{pTjqZGA61VY1;eHz974*2mGPPJS^pQI Uh@UE7fZ@mB>FVdQ&MBb@0K`Ukc>n+a literal 0 HcmV?d00001 diff --git a/tests/images/drawFast3Rot.master.png b/tests/images/drawFast3Rot.master.png new file mode 100644 index 0000000000000000000000000000000000000000..2a78fb8888c15285a259643d4a8956ef988a332e GIT binary patch literal 375 zcmeAS@N?(olHy`uVBq!ia0vp^DImPl{QV$d4hyv5=*C` ziW~8aNZI_&w(p+(`y89T+2+}Yvf2D$#j`H2`Ts$$IP%@*`rT*QS4aN)k~;Uf&WZA- kBOF4?9z6<%gO;JMG4YFVdQ&MBb@05Us#?f?J) literal 0 HcmV?d00001 diff --git a/tests/images/drawFast3Rot.png b/tests/images/drawFast3Rot.png new file mode 100644 index 0000000000000000000000000000000000000000..2a78fb8888c15285a259643d4a8956ef988a332e GIT binary patch literal 375 zcmeAS@N?(olHy`uVBq!ia0vp^DImPl{QV$d4hyv5=*C` ziW~8aNZI_&w(p+(`y89T+2+}Yvf2D$#j`H2`Ts$$IP%@*`rT*QS4aN)k~;Uf&WZA- kBOF4?9z6<%gO;JMG4YFVdQ&MBb@05Us#?f?J) literal 0 HcmV?d00001 diff --git a/tests/images/drawFast4.master.png b/tests/images/drawFast4.master.png new file mode 100644 index 0000000000000000000000000000000000000000..3a098ca12ddc8a2bd2ac1f30f4aa6e6da0443e80 GIT binary patch literal 336 zcmeAS@N?(olHy`uVBq!ia0vp^DIm$_Vd?og}YBJ pHvK)HU+{@6Df&sWj9Q9kaLe`T&b4Ffm;nqa22WQ%mvv4FO#p{EV%h)z literal 0 HcmV?d00001 diff --git a/tests/images/drawFast4.png b/tests/images/drawFast4.png new file mode 100644 index 0000000000000000000000000000000000000000..3a098ca12ddc8a2bd2ac1f30f4aa6e6da0443e80 GIT binary patch literal 336 zcmeAS@N?(olHy`uVBq!ia0vp^DIm$_Vd?og}YBJ pHvK)HU+{@6Df&sWj9Q9kaLe`T&b4Ffm;nqa22WQ%mvv4FO#p{EV%h)z literal 0 HcmV?d00001 diff --git a/tests/images/drawFast4Rot.master.png b/tests/images/drawFast4Rot.master.png new file mode 100644 index 0000000000000000000000000000000000000000..3a098ca12ddc8a2bd2ac1f30f4aa6e6da0443e80 GIT binary patch literal 336 zcmeAS@N?(olHy`uVBq!ia0vp^DIm$_Vd?og}YBJ pHvK)HU+{@6Df&sWj9Q9kaLe`T&b4Ffm;nqa22WQ%mvv4FO#p{EV%h)z literal 0 HcmV?d00001 diff --git a/tests/images/drawFast4Rot.png b/tests/images/drawFast4Rot.png new file mode 100644 index 0000000000000000000000000000000000000000..3a098ca12ddc8a2bd2ac1f30f4aa6e6da0443e80 GIT binary patch literal 336 zcmeAS@N?(olHy`uVBq!ia0vp^DIm$_Vd?og}YBJ pHvK)HU+{@6Df&sWj9Q9kaLe`T&b4Ffm;nqa22WQ%mvv4FO#p{EV%h)z literal 0 HcmV?d00001 diff --git a/tests/images/drawSmooth.png b/tests/images/drawSmooth.png index e695b85194cf77e19897e43cb8adf8481d4fc1a3..bef6ff3065e9f5f415fa8ffbba15d064adcd70a1 100644 GIT binary patch literal 358 zcmeAS@N?(olHy`uVBq!ia0vp^DImqY-8 zMt;+FmbPo69jjQFIGGZ%-xZuYKl^{d^zUb?XWv}2F?IIMHXg?$mQFzxw+Rx&GoDRR zn10;m`|my9Zp(bDUe0p!{pVSw*LLozFZY{pTjqZGA61VY1;eHz974*2mGPPJS^pQI Uh@UE7fZ@mB>FVdQ&MBb@0K`Ukc>n+a delta 126 zcmV-^0D=GJ0^|aaBseQcL_t(|0qxN-4TAs_MbV$2rCOS5s0{=#AOUstPz`jb05)=m zbCNf|;V$RQxy@|W*s{%RLxz#DGahqGkmo+LuQSKnu69e1eVzHdX1))*+ATqj_y3G* glQ97wk-&o$KdVgLXD diff --git a/tests/images/inplaceDraw.bmp b/tests/images/inplaceDraw.bmp index bed31be741a0047d5f0cf9687aa4c742429414f0..f5ff7a750f351b0b75d04743bdee194d0e3dd7e4 100644 GIT binary patch literal 40122 zcmeI5F-`(e00oy=T3C32y@`oEwHKf)DV&hz8-jNA2fUao&GZ||S;`oRsC@%8i8=kzlEWp6}a zYZuG!=Yy{?ADoqu%!fglcjSY!GLrc)DD#eda8^b#9|mRKkq^$wNan+!%scYISsBTE z7?gQOJ~%5QnGb_9@5l#dWhC=qQ05)^;H->fJ`BpdBOjcVk<5odnRn!avoezTFevkm zd~jAqG9LzI-jNT^%1GwJpv*h+!C4u}d>E8@M?N?!Bbg6_GVjO-XJsVwVNm8B`QWUK zWIhbaydxi+m66PcL78{tgR?S{`7kK+j(l)dMlv4;W!{kw&dNyU!=TJN^1)dd$$S`; zc}G4tD-95DtMSVX)4!#KNMP&DkGr*J*iO z#{D$T-|K9qdD@QWd7RzP^Nh#kb${I7ZokL#yu9(jZG8N`_8Gsi3ldn{#pCfJ`BpdBOjcVk<5odnRn!avoezTFevkmd~jAq zG9LzI-jNT^%1GwJpv*h+!C4u}d>E8@M?N?!Bbg6_GVjO-XJsVwVNm8B`QWUKWIhba zydxi+m66PcL78{tgR?S{`7kK+j(l)dMlv4;W!{kw&dNyU!=TJN^1)dd$$S`;c}G4t zD