Merge pull request #125 from guzba/master
basic css support, path parse bugfixes, iconset test improvements
|
@ -1,4 +1,4 @@
|
|||
## Load and Save SVG files.
|
||||
## Load SVG files.
|
||||
|
||||
import chroma, pixie/common, pixie/images, pixie/paths, strutils, vmath,
|
||||
xmlparser, xmltree
|
||||
|
@ -31,7 +31,7 @@ proc initCtx(): Ctx =
|
|||
proc decodeCtx(inherited: Ctx, node: XmlNode): Ctx =
|
||||
result = inherited
|
||||
|
||||
let
|
||||
var
|
||||
fillRule = node.attr("fill-rule")
|
||||
fill = node.attr("fill")
|
||||
stroke = node.attr("stroke")
|
||||
|
@ -39,6 +39,29 @@ proc decodeCtx(inherited: Ctx, node: XmlNode): Ctx =
|
|||
strokeLineCap = node.attr("stroke-linecap")
|
||||
strokeLineJoin = node.attr("stroke-linejoin")
|
||||
transform = node.attr("transform")
|
||||
style = node.attr("style")
|
||||
|
||||
let pairs = style.split(';')
|
||||
for pair in pairs:
|
||||
let parts = pair.split(':')
|
||||
if parts.len == 2:
|
||||
# Do not override element properties
|
||||
case parts[0].strip():
|
||||
of "fill":
|
||||
if fill.len == 0:
|
||||
fill = parts[1].strip()
|
||||
of "stroke":
|
||||
if stroke.len == 0:
|
||||
stroke = parts[1].strip()
|
||||
of "stroke-linecap":
|
||||
if strokeLineCap.len == 0:
|
||||
strokeLineCap = parts[1].strip()
|
||||
of "stroke-linejoin":
|
||||
if strokeLineJoin.len == 0:
|
||||
strokeLineJoin = parts[1].strip()
|
||||
of "stroke-width":
|
||||
if strokeWidth.len == 0:
|
||||
strokeWidth = parts[1].strip()
|
||||
|
||||
if fillRule == "":
|
||||
discard # Inherit
|
||||
|
@ -68,6 +91,8 @@ proc decodeCtx(inherited: Ctx, node: XmlNode): Ctx =
|
|||
if strokeWidth == "":
|
||||
discard # Inherit
|
||||
else:
|
||||
if strokeWidth.endsWith("px"):
|
||||
strokeWidth = strokeWidth[0 .. ^3]
|
||||
result.strokeWidth = parseFloat(strokeWidth)
|
||||
|
||||
if strokeLineCap == "":
|
||||
|
|
|
@ -124,6 +124,9 @@ proc parsePath*(path: string): Path =
|
|||
|
||||
armed = true
|
||||
|
||||
template expectsArcFlag(): bool =
|
||||
kind in {Arc, RArc} and numbers.len mod 7 in {3, 4}
|
||||
|
||||
while p < path.len:
|
||||
case path[p]:
|
||||
# Relative
|
||||
|
@ -195,7 +198,7 @@ proc parsePath*(path: string): Path =
|
|||
finishNumber()
|
||||
numberStart = p
|
||||
of '.':
|
||||
if hitDecimal:
|
||||
if hitDecimal or expectsArcFlag():
|
||||
finishNumber()
|
||||
hitDecimal = true
|
||||
if numberStart == 0:
|
||||
|
@ -203,6 +206,12 @@ proc parsePath*(path: string): Path =
|
|||
of ' ', ',', '\r', '\n', '\t':
|
||||
finishNumber()
|
||||
else:
|
||||
if numberStart > 0 and expectsArcFlag():
|
||||
finishNumber()
|
||||
if p - 1 == numberStart and path[p - 1] == '0':
|
||||
# If the number starts with 0 and we've hit another digit, finish the 0
|
||||
# .. 01.3.. -> [..0, 1.3..]
|
||||
finishNumber()
|
||||
if numberStart == 0:
|
||||
numberStart = p
|
||||
|
||||
|
@ -698,6 +707,9 @@ proc commandsToShapes*(path: Path, pixelScale: float32 = 1.0): seq[seq[Vec2]] =
|
|||
|
||||
case command.kind:
|
||||
of Move:
|
||||
if shape.len > 0:
|
||||
result.add(shape)
|
||||
shape.setLen(0)
|
||||
at.x = command.numbers[0]
|
||||
at.y = command.numbers[1]
|
||||
start = at
|
||||
|
@ -769,6 +781,9 @@ proc commandsToShapes*(path: Path, pixelScale: float32 = 1.0): seq[seq[Vec2]] =
|
|||
at = to
|
||||
|
||||
of RMove:
|
||||
if shape.len > 0:
|
||||
result.add(shape)
|
||||
shape.setLen(0)
|
||||
at.x += command.numbers[0]
|
||||
at.y += command.numbers[1]
|
||||
start = at
|
||||
|
@ -1256,7 +1271,9 @@ proc strokeShapes(
|
|||
if strokeShape.len > 0:
|
||||
result.add(strokeShape)
|
||||
|
||||
proc parseSomePath(path: SomePath, pixelScale:float32 = 1.0): seq[seq[Vec2]] {.inline.} =
|
||||
proc parseSomePath(
|
||||
path: SomePath, pixelScale: float32 = 1.0
|
||||
): seq[seq[Vec2]] {.inline.} =
|
||||
when type(path) is string:
|
||||
parsePath(path).commandsToShapes(pixelScale)
|
||||
elif type(path) is Path:
|
||||
|
|
Before Width: | Height: | Size: 280 KiB After Width: | Height: | Size: 280 KiB |
Before Width: | Height: | Size: 520 KiB After Width: | Height: | Size: 631 KiB |
BIN
tests/images/svg/simple-icons.png
Normal file
After Width: | Height: | Size: 1.4 MiB |
Before Width: | Height: | Size: 519 KiB After Width: | Height: | Size: 519 KiB |
|
@ -25,7 +25,7 @@ const
|
|||
IconSet(name: "flat-color-icons", path: "../flat-color-icons/svg/*"),
|
||||
IconSet(name: "ionicons", path: "../ionicons/src/svg/*"),
|
||||
# IconSet(name: "tabler-icons", path: "../tabler-icons/icons/*"),
|
||||
# IconSet(name: "simple-icons", path: "../simple-icons/icons/*")
|
||||
IconSet(name: "simple-icons", path: "../simple-icons/icons/*")
|
||||
]
|
||||
width = 32
|
||||
height = 32
|
||||
|
|
|
@ -202,3 +202,15 @@ block:
|
|||
path.arcTo(x, y, x + w, y, r)
|
||||
mask.fillPath(path)
|
||||
writeFile("tests/images/paths/pathRoundRectMask.png", mask.encodePng())
|
||||
|
||||
block:
|
||||
let image = newImage(200, 200)
|
||||
image.fill(rgba(255, 255, 255, 255))
|
||||
|
||||
var p = parsePath("M1 0.5C1 0.776142 0.776142 1 0.5 1C0.223858 1 0 0.776142 0 0.5C0 0.223858 0.223858 0 0.5 0C0.776142 0 1 0.223858 1 0.5Z")
|
||||
image.fillPath(p, rgba(255, 0, 0, 255), scale(vec2(200, 200)))
|
||||
|
||||
image.strokePath(p, rgba(0, 255, 0, 255), scale(vec2(200, 200)),
|
||||
strokeWidth = 0.01)
|
||||
|
||||
image.writeFile("tests/images/paths/pixelScale.png")
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
import pixie
|
||||
|
||||
let image = newImage(200, 200)
|
||||
image.fill(rgba(255, 255, 255, 255))
|
||||
|
||||
var p = parsePath("M1 0.5C1 0.776142 0.776142 1 0.5 1C0.223858 1 0 0.776142 0 0.5C0 0.223858 0.223858 0 0.5 0C0.776142 0 1 0.223858 1 0.5Z")
|
||||
image.fillPath(p, rgba(255, 0, 0, 255), scale(vec2(200, 200)))
|
||||
|
||||
image.strokePath(p, rgba(0, 255, 0, 255), scale(vec2(200, 200)), strokeWidth=0.01)
|
||||
|
||||
image.writeFile("tests/images/paths/pixelScale.png")
|