parseSvg + newImage(svg)

This commit is contained in:
Ryan Oldenburg 2022-05-21 20:05:13 -05:00
parent 993fa0750e
commit a08a989a47
6 changed files with 31 additions and 19 deletions

View file

@ -28,7 +28,7 @@ proc decodeImage*(data: string): Image {.raises: [PixieError].} =
decodeBmp(data)
elif data.len > 5 and
(data.readStr(0, 5) == xmlSignature or data.readStr(0, 4) == svgSignature):
decodeSvg(data)
newImage(parseSvg(data))
elif data.len > 6 and data.readStr(0, 6) in gifSignatures:
decodeGif(data)
elif data.len > (14+8) and data.readStr(0, 4) == qoiSignature:

View file

@ -12,6 +12,7 @@ const
type
Svg* = ref object
width*, height*: int
elements: seq[(Path, SvgProperties)]
linearGradients: Table[string, LinearGradient]
@ -498,10 +499,10 @@ proc parseSvgElement(
else:
raise newException(PixieError, "Unsupported SVG tag: " & node.tag)
proc decodeSvg*(
proc parseSvg*(
data: string | XmlNode, width = 0, height = 0
): Image {.raises: [PixieError].} =
## Render SVG XML and return the image. Defaults to the SVG's view box size.
): Svg {.raises: [PixieError].} =
## Parse SVG XML. Defaults to the SVG's view box size.
try:
let root = parseXml(data)
if root.tag != "svg":
@ -518,26 +519,38 @@ proc decodeSvg*(
var rootProps = initSvgProperties()
rootProps = root.parseSvgProperties(rootProps)
if viewBoxMinX != 0 or viewBoxMinY != 0:
let viewBoxMin = vec2(-viewBoxMinX.float32, -viewBoxMinY.float32)
rootprops.transform = rootprops.transform * translate(viewBoxMin)
result = Svg()
if width == 0 and height == 0: # Default to the view box size
result = newImage(viewBoxWidth, viewBoxHeight)
result.width = viewBoxWidth
result.height = viewBoxHeight
else:
result = newImage(width, height)
result.width = width
result.height = height
let
scaleX = width.float32 / viewBoxWidth.float32
scaleY = height.float32 / viewBoxHeight.float32
rootprops.transform = rootprops.transform * scale(vec2(scaleX, scaleY))
let svg = Svg()
var propertiesStack = @[rootProps]
for node in root.items:
svg.elements.add node.parseSvgElement(svg, propertiesStack)
result.elements.add node.parseSvgElement(result, propertiesStack)
except PixieError as e:
raise e
except:
raise currentExceptionAsPixieError()
proc newImage*(svg: Svg): Image {.raises: [PixieError].} =
## Render SVG and return the image.
result = newImage(svg.width, svg.height)
try:
for (path, props) in svg.elements:
if props.display and props.opacity > 0:
if props.fill != "none":
@ -580,4 +593,4 @@ proc decodeSvg*(
except PixieError as e:
raise e
except:
raise newException(PixieError, "Unable to load SVG")
raise currentExceptionAsPixieError()

View file

@ -2,5 +2,5 @@ import benchy, pixie/fileformats/svg
let data = readFile("tests/fileformats/svg/Ghostscript_Tiger.svg")
timeIt "svg decode":
discard decodeSvg(data)
timeIt "svg parse + render":
discard newImage(parseSvg(data))

View file

@ -35,7 +35,7 @@ proc renderEmojiSet(index: int) =
let (_, name, _) = splitFile(filePath)
var image: Image
try:
image = decodeSvg(readFile(filePath), width, height)
image = newImage(parseSvg(readFile(filePath), width, height))
except PixieError:
echo &"Failed decoding {name}"
image = newImage(width, height)

View file

@ -38,7 +38,7 @@ proc renderIconSet(index: int) =
for filePath in walkFiles(iconSet.path):
let
(_, name, _) = splitFile(filePath)
image = decodeSvg(readFile(filePath), width, height)
image = newImage(parseSvg(readFile(filePath), width, height))
images.add((name, image))

View file

@ -26,9 +26,8 @@ proc doDiff(rendered: Image, name: string) =
diffImage.writeFile(&"tests/fileformats/svg/diffs/{name}.png")
for file in files:
doDiff(decodeSvg(readFile(&"tests/fileformats/svg/{file}.svg")), file)
doDiff(readImage(&"tests/fileformats/svg/{file}.svg"), file)
doDiff(
decodeSvg(readFile("tests/fileformats/svg/accessibility-outline.svg"), 512, 512),
"accessibility-outline"
)
block:
let svg = parseSvg(readFile("tests/fileformats/svg/accessibility-outline.svg"), 512, 512)
doDiff(newImage(svg), "accessibility-outline")