From 41cd488ae33bc99985b4c43bd7378598e93ff806 Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Sat, 22 May 2021 21:43:35 -0500 Subject: [PATCH] context clip support --- src/pixie/context.nim | 139 +++++++++++++++++++++++-------- tests/images/context/clip_1.png | Bin 0 -> 3647 bytes tests/images/context/clip_1b.png | Bin 0 -> 3655 bytes tests/images/context/clip_1c.png | Bin 0 -> 618 bytes tests/images/context/clip_2.png | Bin 0 -> 1005 bytes tests/images/context/clip_3.png | Bin 0 -> 2322 bytes tests/test_context.nim | 83 ++++++++++++++++++ 7 files changed, 189 insertions(+), 33 deletions(-) create mode 100644 tests/images/context/clip_1.png create mode 100644 tests/images/context/clip_1b.png create mode 100644 tests/images/context/clip_1c.png create mode 100644 tests/images/context/clip_2.png create mode 100644 tests/images/context/clip_3.png diff --git a/src/pixie/context.nim b/src/pixie/context.nim index 3b17500..bf6a3cc 100644 --- a/src/pixie/context.nim +++ b/src/pixie/context.nim @@ -1,5 +1,5 @@ import bumpy, chroma, pixie/blends, pixie/common, pixie/fonts, pixie/images, - pixie/paints, pixie/paths, vmath + pixie/masks, pixie/paints, pixie/paths, vmath ## This file provides a Nim version of the Canvas 2D API commonly used on the ## web. The goal is to make picking up Pixie easy for developers familiar with @@ -17,16 +17,18 @@ type textAlign*: HAlignMode path: Path mat: Mat3 + mask: Mask stateStack: seq[ContextState] ContextState = object - mat: Mat3 fillStyle, strokeStyle: Paint lineWidth: float32 lineCap: LineCap lineJoin: LineJoin font: Font textAlign: HAlignMode + mat: Mat3 + mask: Mask proc newContext*(image: Image): Context = ## Create a new Context that will draw to the parameter image. @@ -119,27 +121,69 @@ proc ellipse*(ctx: Context, x, y, rx, ry: float32) {.inline.} = proc fill*(ctx: Context, path: Path, windingRule = wrNonZero) {.inline.} = ## Fills the path with the current fillStyle. - ctx.image.fillPath( - path, - ctx.fillStyle, - ctx.mat, - windingRule = windingRule - ) + if ctx.mask != nil: + let tmp = newImage(ctx.image.width, ctx.image.height) + tmp.fillPath( + path, + ctx.fillStyle, + ctx.mat, + windingRule + ) + tmp.draw(ctx.mask) + ctx.image.draw(tmp) + else: + ctx.image.fillPath( + path, + ctx.fillStyle, + ctx.mat, + windingRule + ) proc fill*(ctx: Context, windingRule = wrNonZero) {.inline.} = ## Fills the current path with the current fillStyle. ctx.fill(ctx.path, windingRule) +proc clip*(ctx: Context, path: Path, windingRule = wrNonZero) {.inline.} = + ## Turns the path into the current clipping region. The previous clipping + ## region, if any, is intersected with the current or given path to create + ## the new clipping region. + let mask = newMask(ctx.image.width, ctx.image.height) + mask.fillPath(path, ctx.mat, windingRule) + + if ctx.mask == nil: + ctx.mask = mask + else: + ctx.mask.draw(mask, blendMode = bmMask) + +proc clip*(ctx: Context, windingRule = wrNonZero) {.inline.} = + ## Turns the current path into the current clipping region. The previous + ## clipping region, if any, is intersected with the current or given path + ## to create the new clipping region. + ctx.clip(ctx.path, windingRule) + proc stroke*(ctx: Context, path: Path) {.inline.} = ## Strokes (outlines) the current or given path with the current strokeStyle. - ctx.image.strokePath( - path, - ctx.strokeStyle, - ctx.mat, - ctx.lineWidth, - ctx.lineCap, - ctx.lineJoin - ) + if ctx.mask != nil: + let tmp = newImage(ctx.image.width, ctx.image.height) + tmp.strokePath( + path, + ctx.strokeStyle, + ctx.mat, + ctx.lineWidth, + ctx.lineCap, + ctx.lineJoin + ) + tmp.draw(ctx.mask) + ctx.image.draw(tmp) + else: + ctx.image.strokePath( + path, + ctx.strokeStyle, + ctx.mat, + ctx.lineWidth, + ctx.lineCap, + ctx.lineJoin + ) proc stroke*(ctx: Context) {.inline.} = ## Strokes (outlines) the current or given path with the current strokeStyle. @@ -189,12 +233,24 @@ proc fillText*(ctx: Context, text: string, at: Vec2) = at.y -= round(ctx.font.typeface.ascent * ctx.font.scale) ctx.font.paint = ctx.fillStyle - ctx.image.fillText( - ctx.font, - text, - ctx.mat * translate(at), - hAlign = ctx.textAlign - ) + + if ctx.mask != nil: + let tmp = newImage(ctx.image.width, ctx.image.height) + tmp.fillText( + ctx.font, + text, + ctx.mat * translate(at), + hAlign = ctx.textAlign + ) + tmp.draw(ctx.mask) + ctx.image.draw(tmp) + else: + ctx.image.fillText( + ctx.font, + text, + ctx.mat * translate(at), + hAlign = ctx.textAlign + ) proc fillText*(ctx: Context, text: string, x, y: float32) {.inline.} = ## Draws the outlines of the characters of a text string at the specified @@ -213,15 +269,30 @@ proc strokeText*(ctx: Context, text: string, at: Vec2) = at.y -= round(ctx.font.typeface.ascent * ctx.font.scale) ctx.font.paint = ctx.strokeStyle - ctx.image.strokeText( - ctx.font, - text, - ctx.mat * translate(at), - ctx.lineWidth, - hAlign = ctx.textAlign, - lineCap = ctx.lineCap, - lineJoin = ctx.lineJoin - ) + + if ctx.mask != nil: + let tmp = newImage(ctx.image.width, ctx.image.height) + tmp.strokeText( + ctx.font, + text, + ctx.mat * translate(at), + ctx.lineWidth, + hAlign = ctx.textAlign, + lineCap = ctx.lineCap, + lineJoin = ctx.lineJoin + ) + tmp.draw(ctx.mask) + ctx.image.draw(tmp) + else: + ctx.image.strokeText( + ctx.font, + text, + ctx.mat * translate(at), + ctx.lineWidth, + hAlign = ctx.textAlign, + lineCap = ctx.lineCap, + lineJoin = ctx.lineJoin + ) proc strokeText*(ctx: Context, text: string, x, y: float32) {.inline.} = ## Draws the outlines of the characters of a text string at the specified @@ -280,7 +351,6 @@ proc save*(ctx: Context) = ## Saves the entire state of the canvas by pushing the current state onto ## a stack. var state: ContextState - state.mat = ctx.mat state.fillStyle = ctx.fillStyle state.strokeStyle = ctx.strokeStyle state.lineWidth = ctx.lineWidth @@ -288,6 +358,8 @@ proc save*(ctx: Context) = state.lineJoin = ctx.lineJoin state.font = ctx.font state.textAlign = ctx.textAlign + state.mat = ctx.mat + state.mask = if ctx.mask != nil: ctx.mask.copy() else: nil ctx.stateStack.add(state) proc restore*(ctx: Context) = @@ -296,7 +368,6 @@ proc restore*(ctx: Context) = ## nothing. if ctx.stateStack.len > 0: let state = ctx.stateStack.pop() - ctx.mat = state.mat ctx.fillStyle = state.fillStyle ctx.strokeStyle = state.strokeStyle ctx.lineWidth = state.lineWidth @@ -304,6 +375,8 @@ proc restore*(ctx: Context) = ctx.lineJoin = state.lineJoin ctx.font = state.font ctx.textAlign = state.textAlign + ctx.mat = state.mat + ctx.mask = state.mask # Additional procs that are not part of the JS API diff --git a/tests/images/context/clip_1.png b/tests/images/context/clip_1.png new file mode 100644 index 0000000000000000000000000000000000000000..a9924d615749f1a89daca2f4626dab3bec1751af GIT binary patch literal 3647 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 literal 0 HcmV?d00001 diff --git a/tests/images/context/clip_1c.png b/tests/images/context/clip_1c.png new file mode 100644 index 0000000000000000000000000000000000000000..c7b514bfb5d35a76698a926f166e57f75428088d GIT binary patch literal 618 zcmeAS@N?(olHy`uVBq!ia0y~yVAKJ!r*W_W$&~1=mlznB3_V>OLn;{GUI|>r;>hE0 zF+lqKx#x!Y+?T(EJ`ea3-rZ51W;cIk?0LRB4bm0AdFT9RWW49Up@HWhlMxH|2>~4i zXR2_j4lwjv81L@?Bl+MsHO+;(Qi?6@+?jQ*46IG8DI8)G6eAqk5*Q_^!->f*I(H^7 zFYAF9b<9I`ogl|GhMKuE@4gp5aGI0KE(h9FE~Q~*y!+_E#@|E+%OA#j4u>zOxEukc OAqG!ZKbLh*2~7a=hPXQb literal 0 HcmV?d00001 diff --git a/tests/images/context/clip_2.png b/tests/images/context/clip_2.png new file mode 100644 index 0000000000000000000000000000000000000000..9bbe6dbcb70d00f40c0b208b98fa08003cf2a432 GIT binary patch literal 1005 zcmeAS@N?(olHy`uVBq!ia0y~yVAKJ!r*W_W$&~1=mlznBw|KfZhEy=Vy?b!gCoh54 zi(>IMl|}VM%f53R%KPS7d96uqRtRU^1C>g>21OQb3Fi$BhPbJu_1B}0Z+t%|-A1TJ z^I%u(##^2n9>^Z-;uPD$5$3aDf$N5(CRW`hQDL17!8J1y9J>>avT#SSXsbnBP>L`- z*d#`z(vAPwvR%&^|FWpr_+0#bSdmP1zDmT0J~B-s!D3XUsSX?B)_)al)#fR;=|BFf z^77fQ+h)r?fA>4i`p|m`fi>De<=cL<|GECH^*kqv_t$i>X4}61uPSqVZ@SI-y`TSX zt$kQbd9a|mj{Lwjyejp)@_%Rl<9o*Qp5OcV@7BH#mPE!3IZoi=xF*_SJ^y<4UmMjg zRY5QMK3A-Xe$Tx>-C@JKcU@Qf!sj&pS0*ZXu(4#{y;Jx`_Jhs$IR1UdKUatS)TK&- zBgb2qDgMTZ1l<`gEDK*$TlVe!Jm>kn*9X4~Am@X!zpb|`8_IEKFmUF3EZ^}jLU-!% Sy`8{p%;4$j=d#Wzp$PzfLWL9n literal 0 HcmV?d00001 diff --git a/tests/images/context/clip_3.png b/tests/images/context/clip_3.png new file mode 100644 index 0000000000000000000000000000000000000000..510800a7fd6432524d120b0001a00f6ba56bcfc2 GIT binary patch literal 2322 zcmcguX;hQf77ZW?g%7D9lE_eeR4WogXksB>$MBrIUAml#N!KPF z3CR2c@_(VW+wuUi9`6RriExH43+}k=Y7U#Zd3Vo_NmG4Y9o;?uw2;PT-*_?Reqiq2 zzLgo#kwEQ|oT^F4Hnc+M4P2?oSjN4*xGcX^L>UwQ{PtLEo9wsVR5HSIKAT@Oq?xt% zu0Gt`&l)KG7X^v-oWv4}nn}>tv<}d7p7CG6aQ*NlWJR_(iPZF7lvBWu(*pWClA9&z zYX`|tGq+it;Zd2U@vIf|^2yQ82M2Cty*9)e=8z{8;S722zS6H}wZvp%LG zrI5_my2uK8d{mS}c^Q9RFz@xs`a~-rE}{r1c!LfqtP#aDtEZyk%mF?AyX&mH#cv6HLAR8*3RaIy``s}7 zzCSONNXeXY!y2sZeBc9TdkXX0EsiT$R}n8Rv1-LTkRcPD+w7dOhHnn%(!Z%9KA(sP zHt}s^wInSYw?dY&Eh=!fDZ3wb+&RVjWN6GURMR*;fCZ){nrXRY0eBoZU_ zlBB|GpDJI6<0cz6xrj1{-d?beLhD$vGb_U+L|>GxoWwearw3C|oNYHn3UZOfX1{G} z({2;h)3h1gBnRQsfywc?EyIM+A3{F{?-$W<4>6U*?TtQfcNVR8n(2FoMQ!(wE&#e_inXeiH^LY8{ppJj&;LS@U#ldYgoZMNdhr zI|7s=uZGLs1fS35d+Ca5YeL+b9BI~3&P_m#J}AfC4;Nby?lSJX$i3zVY?b&v>N-0~Q-ULyGMrpT%N|CqPV=yi6Fm?QOE4{c={?G^(|!pF9#qp?K*$eW zX39Jj4&Ymr$HTQdRrM%3|j+ z%+PHF7~j~%UsKL1iAqtf(Q<9FQjA?^!nS80vA+2L_brm+FU_%cP&LS=2F}-UNkS+3 zM`*<#`;%gVD{@|c9j8@X)M_8o&Q)H@=z3`l{ZV@sB0p%2PPQm!l1IybUo%D<{hZ1g z|2No2P629|RYcr4#xiTp{6y<38aoBa8Rgs$@0zSmts**(Q%WK>5GxjhPCs~GJJ=V+ z8+zUEoR-kN8hDyHJ$-=~<}|!IK6{maV{-i;z@M3F;-Sr@Q~dYpac1a>I>>^j1dlcm z=z;Q2*vYx_j;T&VK2;07H8B1rmChf#F11CT$)xrOjt(&6ozR!wk+*JdB`KU#po2wM zPCR~Y@!rlS7nGKZkHxXbq(i)scZF9b8#4?C0fizUsw+fYLDj9^my^;L^g3elAba9BsB4qF5xS;RRKtganPLUz!q#>INfbrFg#cH>FJ}J|IhA_{|RPV8&-2Ju+dnt P>%csZ_`27*f|>sTHXA_B literal 0 HcmV?d00001 diff --git a/tests/test_context.nim b/tests/test_context.nim index 6777b97..605564c 100644 --- a/tests/test_context.nim +++ b/tests/test_context.nim @@ -310,3 +310,86 @@ block: ctx.fillRect(150, 40, 100, 100) ctx.image.writeFile("tests/images/context/save_1.png") + +block: + let ctx = newContext(newImage(300, 150)) + + ctx.beginPath() + ctx.circle(100, 75, 50) + ctx.clip() + + ctx.fillStyle = "blue" + ctx.fillRect(0, 0, ctx.image.width.float32, ctx.image.height.float32) + ctx.fillStyle = "orange" + ctx.fillRect(0, 0, 100, 100) + + ctx.image.writeFile("tests/images/context/clip_1.png") + +block: + let ctx = newContext(newImage(300, 150)) + + ctx.fillStyle = "blue" + ctx.fillRect(0, 0, ctx.image.width.float32, ctx.image.height.float32) + + ctx.beginPath() + ctx.circle(100, 75, 50) + ctx.clip() + + ctx.fillStyle = "red" + ctx.fillRect(0, 0, ctx.image.width.float32, ctx.image.height.float32) + ctx.fillStyle = "orange" + ctx.fillRect(0, 0, 100, 100) + + ctx.image.writeFile("tests/images/context/clip_1b.png") + +block: + let ctx = newContext(newImage(300, 150)) + + ctx.save() + + ctx.beginPath() + ctx.circle(100, 75, 50) + ctx.clip() + + ctx.fillStyle = "red" + ctx.fillRect(0, 0, ctx.image.width.float32, ctx.image.height.float32) + ctx.fillStyle = "orange" + ctx.fillRect(0, 0, 100, 100) + + ctx.restore() + + ctx.fillStyle = "blue" + ctx.fillRect(0, 0, ctx.image.width.float32, ctx.image.height.float32) + + ctx.image.writeFile("tests/images/context/clip_1c.png") + +block: + let ctx = newContext(newImage(300, 150)) + + var region: Path + region.rect(80, 10, 20, 130) + region.rect(40, 50, 100, 50) + ctx.clip(region, wrEvenOdd) + + ctx.fillStyle = "blue" + ctx.fillRect(0, 0, ctx.image.width.float32, ctx.image.height.float32) + + ctx.image.writeFile("tests/images/context/clip_2.png") + +block: + let image = newImage(300, 150) + + let ctx = newContext(image) + + var circlePath: Path + circlePath.circle(150, 75, 75) + var squarePath: Path + squarePath.rect(85, 10, 130, 130) + + ctx.clip(circlePath) + ctx.clip(squarePath) + + ctx.fillStyle = "blue" + ctx.fillRect(0, 0, ctx.image.width.float32, ctx.image.height.float32) + + image.writeFile("tests/images/context/clip_3.png")