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) decodeBmp(data)
elif data.len > 5 and elif data.len > 5 and
(data.readStr(0, 5) == xmlSignature or data.readStr(0, 4) == svgSignature): (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: elif data.len > 6 and data.readStr(0, 6) in gifSignatures:
decodeGif(data) decodeGif(data)
elif data.len > (14+8) and data.readStr(0, 4) == qoiSignature: elif data.len > (14+8) and data.readStr(0, 4) == qoiSignature:

View file

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

View file

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

View file

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

View file

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