From 34b3f09e431687773cdb5f4fec204c79309305d0 Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Fri, 19 Feb 2021 12:04:27 -0600 Subject: [PATCH] fill and stroke helpers --- README.md | 16 ++- examples/blur.nim | 2 +- examples/line.nim | 2 +- examples/rounded_rectangle.nim | 2 +- examples/shadow.nim | 8 +- examples/square.nim | 2 +- src/pixie.nim | 165 +++++++++++++++++++---- tests/images/masks/strokeEllipse.png | Bin 0 -> 905 bytes tests/images/masks/strokePolygon.png | Bin 0 -> 1486 bytes tests/images/masks/strokeRect.png | Bin 0 -> 345 bytes tests/images/masks/strokeRoundedRect.png | Bin 0 -> 831 bytes tests/images/strokeEllipse.png | Bin 0 -> 2607 bytes tests/images/strokePolygon.png | Bin 0 -> 2621 bytes tests/images/strokeRect.png | Bin 0 -> 565 bytes tests/images/strokeRoundedRect.png | Bin 0 -> 1497 bytes tests/test_images_draw.nim | 51 ++++++- tests/test_masks.nim | 30 ++++- 17 files changed, 225 insertions(+), 53 deletions(-) create mode 100644 tests/images/masks/strokeEllipse.png create mode 100644 tests/images/masks/strokePolygon.png create mode 100644 tests/images/masks/strokeRect.png create mode 100644 tests/images/masks/strokeRoundedRect.png create mode 100644 tests/images/strokeEllipse.png create mode 100644 tests/images/strokePolygon.png create mode 100644 tests/images/strokeRect.png create mode 100644 tests/images/strokeRoundedRect.png diff --git a/README.md b/README.md index 53be7ab..cd591af 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ let pos = vec2(50, 50) wh = vec2(100, 100) -image.drawRect(rect(pos, wh), rgba(255, 0, 0, 255)) +image.fillRect(rect(pos, wh), rgba(255, 0, 0, 255)) ``` ![example output](examples/square.png) @@ -42,7 +42,7 @@ let stop = vec2(175, 175) color = parseHtmlColor("#FF5C00").rgba -image.drawSegment(segment(start, stop), color, strokeWidth = 10) +image.strokeSegment(segment(start, stop), color, strokeWidth = 10) ``` ![example output](examples/line.png) @@ -54,7 +54,7 @@ let wh = vec2(100, 100) r = 25.0 -image.drawRoundedRect(rect(pos, wh), r, rgba(0, 255, 0, 255)) +image.fillRoundedRect(rect(pos, wh), r, rgba(0, 255, 0, 255)) ``` ![example output](examples/rounded_rectangle.png) @@ -79,19 +79,21 @@ image.fillPath( [examples/shadow.nim](examples/shadow.nim) ```nim let polygonImage = newImage(200, 200) -polygonImage.drawPolygon( +polygonImage.fillPolygon( vec2(100, 100), 70, sides = 8, rgba(255, 255, 255, 255) ) -image.draw(polygonImage.shadow( +let shadow = polygonImage.shadow( offset = vec2(2, 2), spread = 2, blur = 10, color = rgba(0, 0, 0, 200) -)) +) + +image.draw(shadow) image.draw(polygonImage) ``` ![example output](examples/shadow.png) @@ -100,7 +102,7 @@ image.draw(polygonImage) [examples/blur.nim](examples/blur.nim) ```nim let mask = newMask(200, 200) -mask.drawPolygon(vec2(100, 100), 70, sides = 6) +mask.fillPolygon(vec2(100, 100), 70, sides = 6) blur.blur(20) blur.draw(mask, blendMode = bmMask) diff --git a/examples/blur.nim b/examples/blur.nim index e9889fd..16d34b5 100644 --- a/examples/blur.nim +++ b/examples/blur.nim @@ -8,7 +8,7 @@ let image.fill(rgba(255, 255, 255, 255)) let mask = newMask(200, 200) -mask.drawPolygon(vec2(100, 100), 70, sides = 6) +mask.fillPolygon(vec2(100, 100), 70, sides = 6) blur.blur(20) blur.draw(mask, blendMode = bmMask) diff --git a/examples/line.nim b/examples/line.nim index 3a03e23..24f9b76 100644 --- a/examples/line.nim +++ b/examples/line.nim @@ -8,6 +8,6 @@ let stop = vec2(175, 175) color = parseHtmlColor("#FF5C00").rgba -image.drawSegment(segment(start, stop), color, strokeWidth = 10) +image.strokeSegment(segment(start, stop), color, strokeWidth = 10) image.writeFile("examples/line.png") diff --git a/examples/rounded_rectangle.nim b/examples/rounded_rectangle.nim index 776fb6d..79bc160 100644 --- a/examples/rounded_rectangle.nim +++ b/examples/rounded_rectangle.nim @@ -8,6 +8,6 @@ let wh = vec2(100, 100) r = 25.0 -image.drawRoundedRect(rect(pos, wh), r, rgba(0, 255, 0, 255)) +image.fillRoundedRect(rect(pos, wh), r, rgba(0, 255, 0, 255)) image.writeFile("examples/rounded_rectangle.png") diff --git a/examples/shadow.nim b/examples/shadow.nim index 52c0d33..399ab9b 100644 --- a/examples/shadow.nim +++ b/examples/shadow.nim @@ -4,19 +4,21 @@ let image = newImage(200, 200) image.fill(rgba(255, 255, 255, 255)) let polygonImage = newImage(200, 200) -polygonImage.drawPolygon( +polygonImage.fillPolygon( vec2(100, 100), 70, sides = 8, rgba(255, 255, 255, 255) ) -image.draw(polygonImage.shadow( +let shadow = polygonImage.shadow( offset = vec2(2, 2), spread = 2, blur = 10, color = rgba(0, 0, 0, 200) -)) +) + +image.draw(shadow) image.draw(polygonImage) image.writeFile("examples/shadow.png") diff --git a/examples/square.nim b/examples/square.nim index 578007c..1942f17 100644 --- a/examples/square.nim +++ b/examples/square.nim @@ -7,6 +7,6 @@ let pos = vec2(50, 50) wh = vec2(100, 100) -image.drawRect(rect(pos, wh), rgba(255, 0, 0, 255)) +image.fillRect(rect(pos, wh), rgba(255, 0, 0, 255)) image.writeFile("examples/square.png") diff --git a/src/pixie.nim b/src/pixie.nim index f8b4caf..35d1971 100644 --- a/src/pixie.nim +++ b/src/pixie.nim @@ -50,51 +50,95 @@ proc writeFile*(image: Image, filePath: string) = raise newException(PixieError, "Unsupported image file extension") image.writeFile(filePath, fileformat) -proc drawRect*( - image: Image, rect: Rect, color: ColorRGBA, blendMode = bmNormal -) = +proc fillRect*(image: Image, rect: Rect, color: ColorRGBA) = var path: Path path.rect(rect) - image.fillPath(path, color, wrNonZero, blendMode) + image.fillPath(path, color) -proc drawRect*(mask: Mask, rect: Rect) = +proc fillRect*(mask: Mask, rect: Rect) = var path: Path path.rect(rect) mask.fillPath(path) -proc drawRoundedRect*( +proc strokeRect*( + image: Image, rect: Rect, color: ColorRGBA, strokeWidth = 1.0 +) = + var path: Path + path.rect(rect) + image.strokePath(path, color, strokeWidth) + +proc strokeRect*(mask: Mask, rect: Rect, strokeWidth = 1.0) = + var path: Path + path.rect(rect) + mask.strokePath(path, strokeWidth) + +proc fillRoundedRect*( + image: Image, + rect: Rect, + nw, ne, se, sw: float32, + color: ColorRGBA +) = + var path: Path + path.roundedRect(rect, nw, ne, se, sw) + image.fillPath(path, color) + +proc fillRoundedRect*( + image: Image, + rect: Rect, + radius: float32, + color: ColorRGBA +) = + var path: Path + path.roundedRect(rect, radius, radius, radius, radius) + image.fillPath(path, color) + +proc fillRoundedRect*(mask: Mask, rect: Rect, nw, ne, se, sw: float32) = + var path: Path + path.roundedRect(rect, nw, ne, se, sw) + mask.fillPath(path) + +proc fillRoundedRect*(mask: Mask, rect: Rect, radius: float32) = + var path: Path + path.roundedRect(rect, radius, radius, radius, radius) + mask.fillPath(path) + +proc strokeRoundedRect*( image: Image, rect: Rect, nw, ne, se, sw: float32, color: ColorRGBA, - blendMode = bmNormal + strokeWidth = 1.0 ) = var path: Path path.roundedRect(rect, nw, ne, se, sw) - image.fillPath(path, color, wrNonZero, blendMode) + image.strokePath(path, color, strokeWidth) -proc drawRoundedRect*( +proc strokeRoundedRect*( image: Image, rect: Rect, radius: float32, color: ColorRGBA, - blendMode = bmNormal + strokeWidth = 1.0 ) = var path: Path path.roundedRect(rect, radius, radius, radius, radius) - image.fillPath(path, color, wrNonZero, blendMode) + image.strokePath(path, color, strokeWidth) -proc drawRoundedRect*(mask: Mask, rect: Rect, nw, ne, se, sw: float32) = +proc strokeRoundedRect*( + mask: Mask, rect: Rect, nw, ne, se, sw: float32, strokeWidth = 1.0 +) = var path: Path path.roundedRect(rect, nw, ne, se, sw) - mask.fillPath(path) + mask.strokePath(path, strokeWidth) -proc drawRoundedRect*(mask: Mask, rect: Rect, radius: float32) = +proc strokeRoundedRect*( + mask: Mask, rect: Rect, radius: float32, strokeWidth = 1.0 +) = var path: Path path.roundedRect(rect, radius, radius, radius, radius) - mask.fillPath(path) + mask.strokePath(path, strokeWidth) -proc drawSegment*( +proc strokeSegment*( image: Image, segment: Segment, color: ColorRGBA, @@ -106,13 +150,13 @@ proc drawSegment*( path.lineTo(segment.to) image.strokePath(path, color, strokeWidth, wrNonZero, blendMode) -proc drawSegment*(mask: Mask, segment: Segment, strokeWidth: float32) = +proc strokeSegment*(mask: Mask, segment: Segment, strokeWidth: float32) = var path: Path path.moveTo(segment.at) path.lineTo(segment.to) mask.strokePath(path, strokeWidth) -proc drawEllipse*( +proc fillEllipse*( image: Image, center: Vec2, rx, ry: float32, @@ -123,7 +167,7 @@ proc drawEllipse*( path.ellipse(center, rx, ry) image.fillPath(path, color, wrNonZero, blendMode) -proc drawEllipse*( +proc fillEllipse*( mask: Mask, center: Vec2, rx, ry: float32 @@ -132,18 +176,38 @@ proc drawEllipse*( path.ellipse(center, rx, ry) mask.fillPath(path) -proc drawCircle*( +proc strokeEllipse*( + image: Image, + center: Vec2, + rx, ry: float32, + color: ColorRGBA, + strokeWidth = 1.0 +) = + var path: Path + path.ellipse(center, rx, ry) + image.strokePath(path, color, strokeWidth) + +proc strokeEllipse*( + mask: Mask, + center: Vec2, + rx, ry: float32, + strokeWidth = 1.0 +) = + var path: Path + path.ellipse(center, rx, ry) + mask.strokePath(path, strokeWidth) + +proc fillCircle*( image: Image, center: Vec2, radius: float32, - color: ColorRGBA, - blendMode = bmNormal + color: ColorRGBA ) = var path: Path path.ellipse(center, radius, radius) - image.fillPath(path, color, wrNonZero, blendMode) + image.fillPath(path, color) -proc drawCircle*( +proc fillCircle*( mask: Mask, center: Vec2, radius: float32 @@ -152,19 +216,39 @@ proc drawCircle*( path.ellipse(center, radius, radius) mask.fillPath(path) -proc drawPolygon*( +proc strokeCircle*( + image: Image, + center: Vec2, + radius: float32, + color: ColorRGBA, + strokeWidth = 1.0 +) = + var path: Path + path.ellipse(center, radius, radius) + image.fillPath(path, color) + +proc strokeCircle*( + mask: Mask, + center: Vec2, + radius: float32, + strokeWidth = 1.0 +) = + var path: Path + path.ellipse(center, radius, radius) + mask.fillPath(path) + +proc fillPolygon*( image: Image, pos: Vec2, size: float32, sides: int, - color: ColorRGBA, - blendMode = bmNormal + color: ColorRGBA ) = var path: Path path.polygon(pos, size, sides) - image.fillPath(path, color, wrNonZero, blendMode) + image.fillPath(path, color) -proc drawPolygon*( +proc fillPolygon*( mask: Mask, pos: Vec2, size: float32, @@ -173,3 +257,26 @@ proc drawPolygon*( var path: Path path.polygon(pos, size, sides) mask.fillPath(path) + +proc strokePolygon*( + image: Image, + pos: Vec2, + size: float32, + sides: int, + color: ColorRGBA, + strokeWidth = 1.0 +) = + var path: Path + path.polygon(pos, size, sides) + image.strokePath(path, color, strokeWidth) + +proc strokePolygon*( + mask: Mask, + pos: Vec2, + size: float32, + sides: int, + strokeWidth = 1.0 +) = + var path: Path + path.polygon(pos, size, sides) + mask.strokePath(path, strokeWidth) diff --git a/tests/images/masks/strokeEllipse.png b/tests/images/masks/strokeEllipse.png new file mode 100644 index 0000000000000000000000000000000000000000..872ae6fb6fe1a8519ae23fb0c46c066b166d3752 GIT binary patch literal 905 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 literal 0 HcmV?d00001 diff --git a/tests/images/masks/strokeRect.png b/tests/images/masks/strokeRect.png new file mode 100644 index 0000000000000000000000000000000000000000..c44294a140756a669d05855ef594c69f0a00c3be GIT binary patch literal 345 zcmeAS@N?(olHy`uVBq!ia0vp^DIm-NBp5ET3k9ZsDG^pHNB((e4@z8>vs!1NVcP9%}?Tt^!n6q?I%GN7WEWATcCTXfveoa5q z_41bJq!{01l~Y$USgP6|W*%BB7{GKeM&Jx9nD}@irmVbl>qhg;HwlgB-|uR-Hs1Gk zBh&qg{i$VBCRFHlFYY*!T-axG^2)6zM*AH>p*F4@^#op(9}cit*c4f9lKV=<9a z3As>VDCNqja^%P4Z)#c5w`LjrKI`*(+lRMod(ISR?4LO||K7RXck|P8Ht=c)3jIH- z_kHJwtG|{f`jwY2-sbV^CY#8bBiHQT5b&eC~Xu zjrRXFSA3J1-`TAu>-pr<4Y_IaOE%^o%!@Ay+j4?q;=={8=kGndz}$E@)>tztWXFl6 z0{*2-D%UUgn(kxL@t}lV?tvA9Lib*yw}F?xyUssnw=sOyg@7aXjAl(e7I48;Z_dQY zJgy7u?DYAS4!Gn5E}xUE!{l4;Yp3V4Wbgd!l>76)w6jfEDBd0;6<2H*e{2Gq@XPGO zYjT!+cIJ|1Sb2WpuC#TNW(b9A7)(51cc!vjsZ%jIch%-;t_&{&dom0rKG3t(e*Slf z`O`A$x9AW>zJq@y6S@TROTv&md+N# z)rT^qE~+aDxhl0?{xCnGqEr|de5dq;T+34PBvU@OU%VCngKOHC;x{RjqY6YWV1w z7eBx5-Yr4P3zF9E%zimr)nWE#=MBCzw3VdaC2JymF>qoJDCsHK9wGr-aXAs zXy+F8=|OD|P8*4zJ~&Uf;&Nbo-W99Qb=|Xfzc~|`IXlZ^)8qEU|7sV)nLgfs$Lkh9 z`$mF8W&lf)>Ds04j0g74{^?&_AUGq&HtNeflRw7cLiughDH$rs!Wb$`dnB vAOBJBvaRLO)ErK)mX=N3B%veQZhRFFczkGXNKC66C}()O`njxgN@xNAPOOOw literal 0 HcmV?d00001 diff --git a/tests/images/strokeEllipse.png b/tests/images/strokeEllipse.png new file mode 100644 index 0000000000000000000000000000000000000000..828dd587db6ee5df53aa6216e09019661918061c GIT binary patch literal 2607 zcmbW3=RX^W1ID#AQ!BC6h@eKzNbOeRh*>2@iBTi59a2Hj6{11Xs}Zq6WAEKl>QwEs zcPY-9tyLwht$zLe3-{uAzVDuw&*z(9ZDqm*5Ct$XF>#rh8X)EqQ&!p0&^R1PTG=krZ|48DU32k!t6-sa?^~+Y&WMD`gD8#X=}L6X zt+(WR@}2Ouk)_~j%bFjVZMGtmoes%Ir2A#vtb7=QHSJFO#j_r*R{lZ>$yrx}gxoMs z!KoQbsW$V|_Y=W568GSe0s@&z-d?|cV5_;~g&^F=+j+8iZS)Rsz6Q+6w-ZKdoO>th zqBW8?1J@WpX7$^8NNcs44dYnL4TDF7l+lzV#@0YFN6aY=0!I{Q}Pugr30ZV z#2(@rtVpV2`z!g{n+)B`BB9h7%bVfW3SSjtl1PAk&dvaF7RU>!LSlviZSK98}PV9>C|r!;5{lkoeru1Zx-**L2nm`*?t*DM?n)W zk(O+7(K4Np@yCr`*J;Pk^k$7&`s)@+17XSX0y9HfjTVQz3Vhk%*vRBw5gL|-upLJg z?qhQE21`3yT1?w!-%9%o#%K@NAB+Y(F5b0691KD}g%G#OK3q>MRDWcBh>JI@%zs`E z(y3z~&oU;J@Hnj&hrvtaJrDlAGKk?&);8Kmd!qV1TL7jR%U54I6ji;N(}peP*sV*> z3M{E-1dq#N;pp$mMLNbh6r{M&Sk}4{M(Xgza&*dt`N1%MRLIplWaiHBk1=u~{E6ZM zL`y@)0NgG4}Ep{HLhBL0lx`wy}=CkUR znMSYc(2!aVe%w$hx7FH&6cmmQ@J(Oow`OH1{?RPRLmsj#0e|-GJGJUf`)xnyYr-H- zG826Au%dQq1A;N=+bVamz8?9<^>s}3rsHCYml-erj6p{U!3<973adEf43 zQ`V4(`pCR^{VvIzo>aiLC+vI(D59CnJ|0h4*TKOX)I@?a5e=$Cg`zrPt}SC|Y9Jl; zW$LBTs}GzFZ^L=>Z2MCL(9%cksLj#TzxmhD?zq+Br?fwxuOVf_;AoaVN>me7nfD;E zj);cWo^|wV1-w3kP@ZMB81J^mt7tZXy9uK#HmkeKOyYxlB&b2^J+T7qb73Jz*@ck$ zvqR+V+CarP6;rnLaT4Y^Zf1bI9`c<_2l7sX5^xNy^>;4y&s|gFaDDZ1aEbdiXJtW* zIac_jk88W&cEy#7KPiez#rE#acI>XNBrNHo&X>2oiSjVin^@8Zm_P2Z1Ud0WZa$uG zcadOMq5|kw^OfKW9`L@Z`qb>$L`>Y4j**ZzbMC3UY-Q04voCo40c-ORSnbPBkuIS$ zfD)C1-~Xp<4NLBc*!?UMh^3*>Dobv|(L#~-J^;|D2pH_$Fy3d%)HC zyCn|FNqyLK5l`RGb8(T|OcJpD2CSOKhSKC~4=zQ-M{&?2oDv24g!toBBBc(qcl+eh z`o6(BY2}3qZ82C6Pz#t%zEhf{9_-7THBwV(V_oOC(QPDmcxuzuTNBy+^zhyC74!F# z9RuLTLd!b7ZmF0%NtQRoBRH%T79-sHo+tsMt^!dVl^emlEg>97DtGaTJspg*9`6K| z)^>*G4p+7*^K36!FlIawWF$U4>*A)qW0@aQk*V(s905<}Q=H1wexEUd5EW6WTvmQ} z0LSS*BOe)$W;^x@=3SLTkEA5^Xf9?VC@m06GCSlTBvtuVIQ-bU|BbyVjE{58=|t_H zs4eAJ3WCb+zoH3)Ok{MzwMvpj?Crj;)Abj!t4Yke$tO#iEcHW!4?DDZWKugj`Ql{FQ?e|AJ?BqxHI& zNu$5!ep|(VxtvBDOP&q)X~`F`>fde2O0A?<-@VC*CAB@!h%?yJu&A^mT;;Ewbi{qI z;cEtavQd?}01f*{eLK zxH+&Dg^4Zidd?_qq=xEZ{b*$hL-ghBIFkqkaBW~B=Ej^~k6su?m+y;LUFf+(ujvZ| z#_Ot|@+7Wo(LbUh}BpFHTW7dhf7$Hp%WoBfL?rZ)e* z;`VEFo)48)gZuYW?4jUKk*Fk_BR2oi_hOVYMB52t@?B}4Ayj{yE)Ta@0v!* z<-S-)%?%=|`9!D_#{^oZNh`k$55c?%IhDfE_+Wa`w>`?=i5PjU3HuT+R`T}Eb^1M3bWTfun ziRA#r9)bCB?fd|{^viZMc=cviS6929OvhbVK!`pv_(;Tm!I_W^VzX*&=*0Y&2r`)& LTNzawy5s)`@{rbg literal 0 HcmV?d00001 diff --git a/tests/images/strokePolygon.png b/tests/images/strokePolygon.png new file mode 100644 index 0000000000000000000000000000000000000000..f4483df04bf0cd0006e065803b563b83c8579c6e GIT binary patch literal 2621 zcmbW3`9Bj30LBq=EsQZ&%`o?zbHCHp93kf2);s=A3J!%-ogt zbuS@Aaz@P2X;O;n?fnqOmlx|2`ll9b0RRcHxe7oN6G+7nlz8I?gdHyB*o>7| z(_*rfaYAez9k$pLJSK&doJVmuE|lY}E(Dj?gA`+q7d&6;JtJ=V;V5sRN;|fj6?pN@ zmxwRlyDr9+Mt7cri-?G{fl97g8k*Oz*cs$#7!M3rtk?FY@VclV`vrIF^?TChyma^0QKl9A8msPc2 z@N+#+VzO-aG#(Yog`;{_ai96Rxo6z>jgzJ^Z^`sQIPc?$Cy{-$f0(5H@M;t432Zt- zvPqH;X`4v(ZBk%s*7jgSjU+o7HP0EDG)YY4AncwRu6oK8EDJ?hq2L#!!p4MmC9Plm z1%Y2UG&Y&SpdX`|o8v)5_#+`(K2$wsd30sxnM43Z_y@h2p^56LT_IUDT6^RAV9w`4 zOw$jW=D9+}g3QHzQsu5H*@)28^X1;FYU{in^;2z$&ospKI4UdVYhv-TOrcx;$1Pb= z5CTt&623-E?Q}=5toOn1d5M=h18YZ>F#2ahp}khTfYY2l=n!e(l3@}emw0SyV>I!I z&p7Qdv!McTqQEU4DS067tGOHM+5t8GvlRoU!jjB324AY0t?jxG@J;=SCPHos+dKN( zH1&Nl%Wq26-W!ZX*gSEg7p-AYKD`fD^v}ts4hfHog&`NdDtEal zC6v^2wU=lXZsug~nrkM3ZuWB-yKW&=m2w9Zku1gYN;T!uLhqdIY`(GZ+_*|w#Xs(r z2^WxZ(^Dj&bhe=~-g5#dn+EZswr^iLyJ6d z(SoAxcE-M(NpxwtUY{WA5@3w+Z9Ht;_=>`{85Q=NWeiF)6<47!JxN=(TX9oDJ}3!^ zS@U0qilM9Xzdh`A`zEUOn+6)h)NpOj>>5$p=rw5d8lxk_iLYnfPKff`H?jE-Qa~Jn z{_d$wLkf&)&^$IVWzGe%4}K(r=y&w^-%oVXcJU z#y$!vhPb>}vi^f9Zn<4sk4}N}RM)5M)N-v?(e%$g?_!P>54p(rBX_Roy-r&d&RO%@ z?n+wbWrSVBTss@oI!?I*H%1$xLiSa^uw;BY4}%?~d0Gj>-f|<7G8n_x5Z?{1nJP2t zyts=}ec0MtTsmM#P&*x#U6kvzRV!`i~vdiRj)82s1r1tXi87R z5{~JI702pxU9tiaH|85rt5Nd~f{%}f#(BU`=?wUhodsIN)RdXd1<+5OhWg+h8qs!F z?fG)l*(&(lZT@F1>}`s~vjR&GN5QS!pE{0iC_szHk%Zj2BvG}# z2Oq)bUZc#?X4W?T9 zx5G^uW)x}CWAn_Ch%7wnN2WcW!r{Ht>Gc)n)$vR`6p{;%E8_Ik|lmUCfc=y{gaM^0f~r#ofy@qgSse)>(Fl<7i9 zzs^7tx%H~DO*TiSycQ!~LE458>GgvamX;@lI)YT#FJrbWNdsaqx_S344Qr-8#9hWPU>jnM2f?F?<1a1cw@`W`^A%`y4m7T&csIX!SFph_A`2Hgg0!`MU_-o zqyU;SHVBi8-Wd5l-&m6^TL-}-?k(WKAf#9j_+?5(r+sPq^vD+iukK=LUN81iQzp1^-rpV@fDz}?Mi>ga29RFf*>P=lGn3k zO#kiB>sz1u*&mO#a0#u&QjVt#$fVQz>Ma}GhZ$?xcb(u93Jq)U>+f2B-o{H0bb;So zj?CgZTC_vfXPseIPB_(H)tPZ7*zhge;+!MpgTO(WK?Z5JsfF_lV*) zm;ygNgBSZ=eSNOST+}pKHjh~@TMt32J#1jjHjRXG1hBW$e|%`$^AVh~$A#f681hAr zjCiaWk7EgvvH?<+4z{!3C!OS3&r3HOyVHs>!d`>lV0RW~B~#}1`rxC{;{}0XSsE8biqtSN%a-KV|LyBYWG0;1bsL|A*38Owc`o<`-YEBlV!r&TZ3!A3J5Y(eL z-|=ahzs?NhNA4?K{(N{t?Pvd|pSo(lm7*D^uSgC literal 0 HcmV?d00001 diff --git a/tests/images/strokeRect.png b/tests/images/strokeRect.png new file mode 100644 index 0000000000000000000000000000000000000000..3d8128a172324da11bc2943f2770e5abbd078406 GIT binary patch literal 565 zcmeAS@N?(olHy`uVBq!ia0vp^DImJzdA|noZ`c~U{;a}5i$%w)-gB|?PpWeIDXy?md`dm@q$-D*tTT5v zZhx`)UG|FgR|6eSE088c#F%d_pa!%I?%u&vQDGPVc@;iO9>6^v5 z_py#MwKwN&wZD7id)yhz6G7GIRaH5391}R1ydiFwrm%sf5tmRd+sxl_TI*xJ+nk#{ zduB~*bm^}L37L-9H8#I{UHt7*)1N8p9X73)vpzCSCsD<*PP9iy=Xv&&?Das-IW9M` z>7VC4oi*Jcs9fP^uTK2T^;^x|ZWWzp7#BUnn3n+e6A(VPiU%6WL@2}@K|Av k-BmGR7QBH0*Lcmm`@6rx>4nMCz~sQ->FVdQ&MBb@0GSWnqyPW_ literal 0 HcmV?d00001 diff --git a/tests/images/strokeRoundedRect.png b/tests/images/strokeRoundedRect.png new file mode 100644 index 0000000000000000000000000000000000000000..d1745c6fb2f2f991dfafcde360a32e0331cc1ce6 GIT binary patch literal 1497 zcmcIkYcvxI07k`(F^bK4upvg4Z5AoAS=cIhtSir$5vP)kJSr#eN5(aVc|^OeSGu>X z!epjm$drhRD9kh0@hGgUZuj1E&*|6wchC7=KffQ}`7&Ib9YH`CP)<$`L~wbJqjM-Ucno##+`F1v4Ti1I_kH`k}~=cyWgLOMe18;BUM@!L?pSK#pZ#OVVNz z8Bxo&9!<{~s0C=x2j&#+^MI55685a=hq8sdh8(CF#npay1xI{OH3)0-hJ;jljlZb4 zR2gk*@UtWzBU;7e#gqkGFC!tWn|%*UjadabU$1tK_}Cjhh@5&)mxaeKehlcO&&07X zjw>jSRke~PrmS?j1c!-HDH0KWVwegxa6ywL#$2`xDO{gYjdJg1c#H+7eh$vu>PIM> z@C!CCL}V)-Xx`q-?%Tl;L2By~KSj={525=lN0d+2>2 z5(Q~|�WmEdwn~3M`dpc)u4>QINr;g0enN0y?;{+3?V76T1q9Oo1w`q>#OZ?RK)G zo{DgEgAaDFKlyaFYbv|J5p+S3B&Tb)7=4L`X4%*@`(#6A!9>31eV@Hi7&q?2&H;;nDpA zDU7GEC@7**Nr=t%czZeZIQa7}-WojC;0}BI3F;}oyh93+=&@!r6RdJC*~-{Bnrtc% z)$J9>*PF8<}`eeTEdLtN5tu~0H ztZAxAR2q%U^A@;b!LaiLza=K#NZ$;e$aJ+00AzCJ%k~TD&85@2&Qb2jxh0!8-}VV| z7#(gGy~;H8p6~ML*Iv0sJHr#uUa yLH|2)?7FPc6U#9}=WuS+f3NPx<^6YF*400D#~82%J^KB!