From 42bedda0a74209cb0a4e834b3b1ad42e0f8d2de8 Mon Sep 17 00:00:00 2001
From: treeform <starplant@gmail.com>
Date: Fri, 18 Mar 2022 19:24:56 -0700
Subject: [PATCH] Fix #372 - Add negative spread.

---
 src/pixie/masks.nim            |  11 +++++++++--
 tests/masks/drawPolygon.png    | Bin 853 -> 737 bytes
 tests/masks/negativeSpread.png | Bin 0 -> 180 bytes
 tests/masks/strokePolygon.png  | Bin 1400 -> 1273 bytes
 tests/test_masks.nim           |  11 +++++++++++
 5 files changed, 20 insertions(+), 2 deletions(-)
 create mode 100644 tests/masks/negativeSpread.png

diff --git a/src/pixie/masks.nim b/src/pixie/masks.nim
index ea7b718..465dd86 100644
--- a/src/pixie/masks.nim
+++ b/src/pixie/masks.nim
@@ -234,14 +234,21 @@ proc getValueSmooth*(mask: Mask, x, y: float32): uint8 {.raises: [].} =
   else:
     topMix
 
+proc invert(mask: Mask) {.raises: [].} =
+  ## Makes inverts all values - creates a negative of the mask.
+  for i in 0 ..< mask.data.len:
+    mask.data[i] = 255 - mask.data[i]
+
 proc spread*(mask: Mask, spread: float32) {.raises: [PixieError].} =
   ## Grows the mask by spread.
   let spread = round(spread).int
   if spread == 0:
     return
   if spread < 0:
-    raise newException(PixieError, "Cannot apply negative spread")
-
+    mask.invert()
+    spread(mask, -spread.float32)
+    mask.invert()
+    return
   # Spread in the X direction. Store with dimensions swapped for reading later.
   let spreadX = newMask(mask.height, mask.width)
   for y in 0 ..< mask.height:
diff --git a/tests/masks/drawPolygon.png b/tests/masks/drawPolygon.png
index abd187e6caf02634e38bf9f260ef5d7adab20646..c16c400c321c54864ad69538edd9ebfd4b4e45d3 100644
GIT binary patch
delta 714
zcmV;*0yX{B2H^#eBYy&@Nkl<ZcmeI0Ur19?9LIlok%`eLLmD9n3oJ6CWhw@vhYBK)
zT_i*c79nd<P(9Q`f%FhW5BX3pxlx!Ai~b;6L{Lyc5H3?QBpGNzqYzet(jsL{W8BTP
zyYoBu{Co(B-{<L^d+#})d%hPg{C*nz16V?W<08Rvk>I#UaDQARI4%+^Gp?H6!-LbL
za{%=u_A9+xYEG<;&XY8xxK{a|?9~9YCg02liiIL^hkfP7-sR1nvA8i*L0wWEyynR7
z=s33Z{cL_JM^&=AGqj^{r&_;lAXRt?Ls4=h++1qjqI8cpF>1ajL`hQDciJvmZEra+
zG9|iULP6rVaeuSQv}%c}<JIqyOEU~hiS68@Mr?5gmw#a}of$~5lIW3En`Aiv*!H!@
zW0?VtT(T{6p#oo<qoU0{oXAZ?$Rz8!Zfz2+DaPsF&_7pvIR`G$PDR`$xyP(1srUaH
zTEw10CHo^s%dkZv$_^eHpA_AY{e@$H{X^8KY`cs+h<~1>6TO47z!C}^7YUAw1jj{!
z<08Rvk>I#Ua9kufE)pv=u4r8)=@<ZGUUKO$8Th`NQlQM7@0h^e3NH{A2ZC#Ms+L;m
zZqFOE(2__1=AQL!c4BW=z)Ss(q-2yz01|gU)?1gqpF1u}i(d-~KrE1Klo#?pFL^%|
z9IF`on}1|UzwKG?%8o_!=+CVh64nxcWaQ~~tho}^yRBO*DiVOH+Y=3<HqqCUXH=R*
zY7&6aV7kHF@cMnVeMl5wF_zyXOX~H#jkVda!9zW)*tk=c0L(sq<;ph@Mq0a+TaW|^
zKs@-Z$!MqkIP3pnoJR@OxD18^wp|*euDA3Xb3;XF5}1GSz&Y-@Pit^fB>>Z*5VciL
wmn;WjT+4$cBseY-92W_Wiv-6-;{S>34~sR%jC|_ZMgRZ+07*qoM6N<$f_8LH3;+NC

delta 831
zcmV-F1Hk;@1=R+SBYy)NNkl<ZcmeI0Ur5tY6vwZcnh6aSj3ku6j7XstIz|P(WDkK>
zGsGk#S{nLf5Iq!8Jp>UH5mESuFta~cwDnR{MD&m~1FcLDTtXT`3r2=1vYhdAu-|rn
z=bU@*7QJ*oFS>KS=l8wm?8kn;d$ZU-K$|F`$EAcGmlAqhN`L5aDWS)ugdUd?|JArE
zKcAuK8xxLBJgy}^2YD4|N0iJnCSlTY(9q#HzW3wF7{X5M5A$M56nEz=nu{Scxv(uJ
z#L3Hlf+rUdnUr;AF6t;Y`9f4${GIfa(DeqHCAW+}ID$@gpTJ8^ia(5)9{^~R9KRi?
zu~ObPZw;EzCV$6jO(li>*J~<Gnyl>2rD_v!bxkLH;vr40xOq87?utiPN^p0TU(GNi
zQfzoUiR-9N?ib;8JQ-y-_>c)<Qr~LI#1xJWp(5O5ncs$1LAcr?P}l%BIaOuk<*`Ly
zVq(;!=yo<%gHX{kg=A2Z_KipdO7^M;NDedEdJ!ogD1Uu14rdXQOkV-4CvkQxfO*8k
z+XSiw6nq^36|l*2e?F)d;clOUB(TZ3N=PdvKj#@F0!`L*gJB6O?uG#sc(v|zfKrOA
z;yZv2G^t+;DCHQHHtBB$y(NfLNslXR+bnTGh|Bk&D7@si-qdd4&|ZaGc<x6vv?3v`
zZ$Js$34eQEoyyP3V6)QC%EIs=a7{F|U*J5_2^(Ku3KtOcUP6+X3F|+i`p}x$rToyE
zS}NQ#aGdvZfjDR<EO0>WMPi~Zg%^p)x0?7;WN3k(B?fpWY-mTJp^&8SC@v&S*vG^2
zuc^_R6kk&(S3B|5adchh0*O$aut1aS*5T)#lz+Dl(+k@Z7esLSDUqZ*Vej_IZRdgw
zX>8}_C+esb#_<lv|AipG6UIii6hl=U&rfmz4uJ_9Y!V+9{Pv`ISP*i!xMf+ncGDV5
zLevQhG>Q&z!k)AbaAs$ACaucK@mJPZGQv*SfQLR*`_%Xs4%MQ;hU9f|><@AQE8<Sr
z*f?1d@e!nqN%}~1l0Mo*2|X?)^thDJ<5EJ8O9?$LCG@zI_yb?yt$<oF6wCks002ov
JPDHLkV1l;|f7bv2

diff --git a/tests/masks/negativeSpread.png b/tests/masks/negativeSpread.png
new file mode 100644
index 0000000000000000000000000000000000000000..0dcc9f36b354a808cc52af568fca2cd7e18f746c
GIT binary patch
literal 180
zcmeAS@N?(olHy`uVBq!ia0vp^DIm-NBp5<FPjvvPYEKu(kP61P7Zmvp8wj`_^x9cb
z^GW3DvN}e!xd&JOy|mkoUBdH;7cThE^m_6Co=dBr?s}fBEdAtG$CeXYqjDrJANzR8
jEzW4tiK)1t%*n5;O-iB;QjsZzAm@0x`njxgN@xNANZU~@

literal 0
HcmV?d00001

diff --git a/tests/masks/strokePolygon.png b/tests/masks/strokePolygon.png
index c6475856183e447759a5f80954848c634b1747d8..3ba071255d77bcbbd23487eb73ad8d58e15ea1e2 100644
GIT binary patch
delta 1254
zcmV<C1R4AI3i%0;BYy<INkl<ZcmeI1drTZf9LIlqg&v?FFH=%T+7iS{m6mc>N)555
zCK9o7&;<HGq#?%<ZM8<@t7)-njBPZ&FnZTEgo>!GZT4z4)kLgmDusgrRxN2!DyfhX
z+JIbo^13s#4|aCg*PKaX%zpl0e=|EX-<==3bMyPnJh%y%B7anv?xMnU7Zs+vsQ7QX
zE9$ud-!#=cfO;kyyc^bSxpIL{h0a~spFNYnKz#YyuB$XMG$M&_dzM&&764|kYvGRp
z<1)rYBDS;lG5}17L4R$vfzF^&8N(ul-ijzRC=+7P1)>Vee(U2beGH0ZHttCl2IDmV
zAi2WcaSa)|7=IFpd%ba~(ny)L@n<KLZV(s{VVuX;sWh9H?;PzSMpQthH%0;LjKPWo
zG3$$e9U$uFpeM4(^JdDNPBK!qX8FcsHFMAwxn)=L3bKoTaz0VD%w=t#BI;ylifm!m
zN6K0Qz#&Kd?aPsY5qV$b@Oi9Eq9Kxd?8!K?xv%7d@qcml>lLZQjkwan?jfR1LRBQ?
zy${o*W;}!2whba{aDRVZEHW_CY8JKmu`-T|i1pbcd3ZC#-}}ma=!)p^fIR{k7&)$B
z#|&1MP!@4`%dAoxo}-SVle~qPY-vu*LI#HQflULwSa}vDk-M8;Oq^{X@YBY9qk<_g
zdiaNx3x5P#BynTGxnZ#&p(HYJhjgFk`qMT2yoGbwGqffeuK<Ab>etXkj*`f3qH_BE
zk~1NBH=a<ZJKAR9ZIRHcq9s9$$FWOZd9t`kbrqaAc_br)>m(8kN95vTbtB>cN{oEc
zVN1epgdy_ROI3>R3(h6?Xuk9=(u?db{?5Q}Y=5xzKtir4OD|%(A@<eg#+NUP4d_Mo
zh+TCtMF329k;qpN9%)%I-!1~vT_onRVlFGl1#-;qgv9(#Zq|2_+G=d$mHf`X*xcpT
zPIl;TAtQGjTZ87!LBF{J>x1R}m$=UA6nQsGTj6j-0FpMZ?$p}WFa3D;?4~}*ged}$
zQGc7%surN!wznZunq!b*49{dI5!YH)*Nk;3g<6)H<M-o%mL<f%w83zWy)#{UI*#av
zOL{MJ7S39>ng|&~p_WpEf$8v}9y=NSE~vW4Cl7y@9N0?+zwmqYP6Z9cRT!Yx?a#yB
z7>+DgOuH1w-&)s{%~fNJ*H!+46(y9GdVc_zIn$7ogA9y_f~t`op<Ky6MEv6b8XVs!
zCnVHF0Q}w)*<^Ahwxp;VC0Fe16J%-y&N@Ew3r{cym6;%h4)(1g(?qGx^!C{LdNN4_
zR~~;#JxQb`0&w0lvx-c3Eq86Vqes9Ays@bhFOz7@1OTSKZ(6tv85r})#2p-Vuz#9~
zI~@^#u`gP3w3CqCWpC-EAaz9m20VY}tEW>(cDVk)rm2j!yLisItv2O$Q}33x@@+Ix
z?o2T8?dR#(IylQ$#h<FJoH7iF09<Qsvx&)ik={pNF_x+~Dgw~wzMjt=I|z)uu=S!~
z>SAyv01#~R#IF>O9D<%lPU-(Hi(eQQ0SKI6?^?`tJmok@aS%i$0&unQXL|zlJD;aI
z(xMarxZn=7RA{Blm7sAK$Mosbw8xC}BH=(ilW;Ias4(3{h3PIT{y*;e2bD8=?_(F&
Q?*IS*07*qoM6N<$f(oB!761SM

delta 1382
zcmV-s1)2K!3HS<-BYy=yNkl<ZcmeI0eN0zX7{{N>>u;GLN*kKGkZ>$?_yRvPt=uwQ
zE6JY{6Nxe~$lgZF)heAPtGSxh)Ykand?7I>Ovt6J8nd=aKtzISG;|UMTV(F1As{cm
zU%QvzdCtA(o^$TK<oxUR`Gfo1bH300-g|!BbDn#`;WiL5aeqW;xEv80E=Poh%MqdB
zaztpj9APzFbGmnIw_;08uiVqh<Z!Y)UnMO0GxZ?G!esSZ;#5k~&+YVtgWM#lb}}c2
zC7`62jk1k;<R+VQ^jZ-LOuq4>s8dC1l5i|q)!4v&uur0rn(SUA$utl><qN~P5t&K)
zUd1Sukans^+<z-GshTOyV^QYpXPYPzll3K3X3dO_Hlavtaz||}W_i=!;WKR^YTRYR
z7&C}Xwyr{{&(n{wk%s47q#@RSnuIJulbMx~sC~N*Lx%q0mDrMT_!`DWgeKm3C~SY$
zk1<Qrlkub}^6qL9GKfr;?xMmCl9|chxgLe7bzK-65Pz72)l5a%sqGkY=y20{dVYC;
zqX3afVG*i_Jy$U1(Oir-I`z+Ho<d*}Q#%3GyDKr|awTptwlDJD#8?O3g@Jb;!PCF9
z2QlW;P>6RlA?h2RhHG+f1#Ny!I*F?cUVmIYXk>hTRXZA?SJP3Ef+sD{^M6Y{Em;ZV
zG(71D34e9)Ojhimg4|0q77_S;19r_ma}i^NV-j%!-!G{80F@FtQ&X`wqs;u~a!g*z
z$BaRb4`Ufk#Sx@C{+|XCB4(2*H8|(N&f_SQ;&VNS{fqYvU>wA3vg?6h7eIIJ5XN%O
zJ`+O(u8Ai}NHCeqEe|7o@M05&77X>SLB#@7L4Rp7IXVl~^$wQCww2RKURcT@5(Z&S
zJg;N%5XkNhitNy~5fc+znQ5Cbah-?@Ku}%riJglRx)bRF<SrW_%^Hl!=4?d?__bf9
z&-1UQuuEfc6p_Z=_<@8PjLFjF00GcUhW0u$nfNlAK%La2DQ}WLO!I)K_*(5Y?6Sv=
z%707w(%~BE$_WqtB#`!%lONu_hvWei{(^oQ%y2>bvP7*N0F=3sqL4+Yo`FrL-^6eX
z)&!c1#zqSOreA1P54L#f6_kyxX20%CCcOz6D9`lTomTFw#c2h~4&ViXm`tF)2q(xG
zaZ$*yl`f7xkBtd(VBk@_g8PWQsXnn1r+<2Q<2y_zn4bjB<=~<fF|CRSrFhe@yS>2H
zqUG<Pt5)jwotQ3W+55CFuY<+}&Xd5!jM=0c=IygCB*KqlAps{#F_pqIfyPbPoX~TQ
ziUkyH#E#)rwo9U`vG-$IX1P^yH>n1TJNWo0ChEXjHc+oC3ZuU^beVbYVQlNoc7IT&
z-t=W*VtSRl;7&O}Z?xnLrs<&q11(wj=@34PR}@kX(Q@<rCjNhkz$EZ%0bVA#iSu8H
zJhlx($oYfDdIToWbssGX7kJI@7a_6+-&{EMA!%h0nLuMPewfDl_(QEylBfRF=zqfT
zhv_xfLt3gz=6=y9_FdIDo5D`vN`ESR5@>iDHw#>`U#ealK3al}m)DJBvtanIwRnMr
zqHRdJAptM&zZX@+PXcW#=!$C1Pp^tK5)){pMWH4Bwd351>bYmW<;4jHKP3?d*^@v+
zk=^aV>z;9M5B3F?bMA{Jpa5)>&Z5#MfwQ@Gwrmx+kpDlnY)3DzWAEFH0$)(@o4Hws
z{7ImFNs>-1;@d5vo-r&;;5;oTg^a9WOGXAOlipJP=A)ic3lB?^TY}Y-+!8`2jtC8x
oBSORFh|q93A~al%xGlr=ADUKO`h0S~wEzGB07*qoM6N<$g1n@r5&!@I

diff --git a/tests/test_masks.nim b/tests/test_masks.nim
index f0ae841..0b9d105 100644
--- a/tests/test_masks.nim
+++ b/tests/test_masks.nim
@@ -92,6 +92,17 @@ block:
 
   a.writeFile("tests/masks/spread.png")
 
+block:
+  let path = newPath()
+  path.rect(40, 40, 20, 20)
+
+  let a = newMask(100, 100)
+  a.fillPath(path)
+
+  a.spread(-5)
+
+  a.writeFile("tests/masks/negativeSpread.png")
+
 block:
   let mask = newMask(100, 100)