From 4d148701501d2f099e78d9a770eff867a928b287 Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Mon, 14 Dec 2020 13:11:14 -0600 Subject: [PATCH 1/3] case back --- src/pixie/fileformats/svg.nim | 99 ++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/src/pixie/fileformats/svg.nim b/src/pixie/fileformats/svg.nim index 13c8b65..6819ee3 100644 --- a/src/pixie/fileformats/svg.nim +++ b/src/pixie/fileformats/svg.nim @@ -9,57 +9,58 @@ template failInvalid() = raise newException(PixieError, "Invalid SVG data") proc draw(img: Image, matStack: var seq[Mat3], xml: XmlNode) = - if xml.tag != "g": + case xml.tag: + of "g": + let + fill = xml.attr("fill") + stroke = xml.attr("stroke") + strokeWidth = xml.attr("stroke-width") + transform = xml.attr("transform") + + if transform != "": + if transform.startsWith("matrix("): + let arr = transform[7..^2].split(",") + if arr.len != 6: + failInvalid() + var m = mat3() + m[0] = parseFloat(arr[0]) + m[1] = parseFloat(arr[1]) + m[3] = parseFloat(arr[2]) + m[4] = parseFloat(arr[3]) + m[6] = parseFloat(arr[4]) + m[7] = parseFloat(arr[5]) + matStack.add(matStack[^1] * m) + else: + raise newException( + PixieError, "Unsupported SVG transform: " & transform & ".") + + for child in xml: + if child.tag == "path": + let d = child.attr("d") + + if fill != "none" and fill != "": + let + fillColor = parseHtmlColor(fill).rgba + (bounds, fillImg) = fillPathBounds(d, fillColor, matStack[^1]) + img.draw(fillImg, bounds.xy) + + if stroke != "none" and stroke != "": + let + strokeColor = parseHtmlColor(stroke).rgba + strokeWidth = + if strokeWidth == "": 1.0 # Default stroke width is 1px + else: parseFloat(strokeWidth) + (bounds, strokeImg) = + strokePathBounds(d, strokeColor, strokeWidth, matStack[^1]) + img.draw(strokeImg, bounds.xy) + else: + img.draw(matStack, child) + + if transform != "": + discard matStack.pop() + else: raise newException(PixieError, "Unsupported SVG tag: " & xml.tag & ".") - let - fill = xml.attr("fill") - stroke = xml.attr("stroke") - strokeWidth = xml.attr("stroke-width") - transform = xml.attr("transform") - - if transform != "": - if transform.startsWith("matrix("): - let arr = transform[7..^2].split(",") - if arr.len != 6: - failInvalid() - var m = mat3() - m[0] = parseFloat(arr[0]) - m[1] = parseFloat(arr[1]) - m[3] = parseFloat(arr[2]) - m[4] = parseFloat(arr[3]) - m[6] = parseFloat(arr[4]) - m[7] = parseFloat(arr[5]) - matStack.add(matStack[^1] * m) - else: - raise newException( - PixieError, "Unsupported SVG transform: " & transform & ".") - - for child in xml: - if child.tag == "path": - let d = child.attr("d") - - if fill != "none" and fill != "": - let - fillColor = parseHtmlColor(fill).rgba - (bounds, fillImg) = fillPathBounds(d, fillColor, matStack[^1]) - img.draw(fillImg, bounds.xy) - - if stroke != "none" and stroke != "": - let - strokeColor = parseHtmlColor(stroke).rgba - strokeWidth = - if strokeWidth == "": 1.0 # Default stroke width is 1px - else: parseFloat(strokeWidth) - (bounds, strokeImg) = - strokePathBounds(d, strokeColor, strokeWidth, matStack[^1]) - img.draw(strokeImg, bounds.xy) - else: - img.draw(matStack, child) - - if transform != "": - discard matStack.pop() - proc decodeSvg*(data: string): Image = ## Render SVG file and return the image. try: From 79189a473eaecde4ac773771306aa53bc0940fa1 Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Sat, 19 Dec 2020 18:01:14 -0600 Subject: [PATCH 2/3] svg decode more support --- src/pixie/fileformats/svg.nim | 169 +++++++++++++++++++++----------- tests/images/svg/triangle01.png | Bin 0 -> 4048 bytes tests/images/svg/triangle01.svg | 11 +++ tests/test_svg.nim | 20 ++-- 4 files changed, 136 insertions(+), 64 deletions(-) create mode 100644 tests/images/svg/triangle01.png create mode 100644 tests/images/svg/triangle01.svg diff --git a/src/pixie/fileformats/svg.nim b/src/pixie/fileformats/svg.nim index 6819ee3..61518b2 100644 --- a/src/pixie/fileformats/svg.nim +++ b/src/pixie/fileformats/svg.nim @@ -1,75 +1,130 @@ ## Load and Save SVG files. -import chroma, pixie/images, pixie/common, pixie/paths, vmath, xmlparser, xmltree, - strutils, strutils, bumpy +import chroma, pixie/images, pixie/common, pixie/paths, vmath, xmlparser, + xmltree, strutils, strutils, bumpy const svgSignature* = " + return + + case node.tag: + of "title": + discard + of "desc": + discard + of "g": + let ctx = decodeCtx(ctxStack[^1], node) + ctxStack.add(ctx) + for child in node: + img.draw(child, ctxStack) + discard ctxStack.pop() + of "path": + let + d = node.attr("d") + ctx = decodeCtx(ctxStack[^1], node) + if ctx.fill != ColorRGBA(): + let (bounds, fillImg) = fillPathBounds(d, ctx.fill, ctx.transform) + img.draw(fillImg, bounds.xy) + if ctx.stroke != ColorRGBA(): + let (bounds, strokeImg) = strokePathBounds( + d, ctx.stroke, ctx.strokeWidth, ctx.transform + ) + img.draw(strokeImg, bounds.xy) + of "rect": + let + ctx = decodeCtx(ctxStack[^1], node) + x = parseFloat(node.attr("x")) + y = parseFloat(node.attr("y")) + width = parseFloat(node.attr("width")) + height = parseFloat(node.attr("height")) + path = newPath() + path.moveTo(x, y) + path.lineTo(x + width, x) + path.lineTo(x + width, y + height) + path.lineTo(x, y + height) + path.closePath() + if ctx.fill != ColorRGBA(): + img.fillPath(path, ctx.fill, ctx.transform) + if ctx.stroke != ColorRGBA(): + img.strokePath(path, ctx.stroke, ctx.strokeWidth, ctx.transform) + else: + raise newException(PixieError, "Unsupported SVG tag: " & node.tag & ".") proc decodeSvg*(data: string): Image = ## Render SVG file and return the image. try: - let xml = parseXml(data) - if xml.tag != "svg": + let root = parseXml(data) + if root.tag != "svg": failInvalid() let - viewBox = xml.attr("viewBox") + viewBox = root.attr("viewBox") box = viewBox.split(" ") if parseInt(box[0]) != 0 or parseInt(box[1]) != 0: failInvalid() @@ -77,11 +132,11 @@ proc decodeSvg*(data: string): Image = let width = parseInt(box[2]) height = parseInt(box[3]) + var ctxStack = @[initCtx()] result = newImage(width, height) - var matStack = @[mat3()] - for n in xml: - result.draw(matStack, n) + for node in root: + result.draw(node, ctxStack) except PixieError as e: raise e except: - raise newException(PixieError, "Unable to load SVG") + raise newException(PixieError, "Unable to load SVG") diff --git a/tests/images/svg/triangle01.png b/tests/images/svg/triangle01.png new file mode 100644 index 0000000000000000000000000000000000000000..603107fdb2538ad2228050b3d8b2059ef4a3f233 GIT binary patch literal 4048 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 + + Example triangle01- simple example of a 'path' + A path that draws a triangle + + + + diff --git a/tests/test_svg.nim b/tests/test_svg.nim index d9b2361..1abc0bd 100644 --- a/tests/test_svg.nim +++ b/tests/test_svg.nim @@ -1,9 +1,15 @@ -import pixie/fileformats/svg, pixie +import pixie/fileformats/svg, pixie, strformat -let - original = readFile("tests/images/svg/Ghostscript_Tiger.svg") - image = decodeSvg(original) - gold = readImage("tests/images/svg/Ghostscript_Tiger.png") +const files = [ + "triangle01", + "Ghostscript_Tiger" +] -doAssert image.data == gold.data -# image.writeFile("tests/images/svg/Ghostscript_Tiger.png") +for file in files: + let + original = readFile(&"tests/images/svg/{file}.svg") + image = decodeSvg(original) + gold = readImage(&"tests/images/svg/{file}.png") + + doAssert image.data == gold.data + # image.writeFile(&"{file}.png") From 76036c614b9e843381cdc966dcd05fb141e90634 Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Sat, 19 Dec 2020 18:16:03 -0600 Subject: [PATCH 3/3] chroma dep --- pixie.nimble | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pixie.nimble b/pixie.nimble index 12a8466..b86b493 100644 --- a/pixie.nimble +++ b/pixie.nimble @@ -7,7 +7,7 @@ srcDir = "src" requires "nim >= 1.2.6" requires "vmath >= 0.4.0" -requires "chroma >= 0.1.5" +requires "chroma >= 0.2.1" requires "zippy >= 0.3.5" requires "flatty >= 0.1.2" requires "nimsimd >= 0.4.6"