diff --git a/src/pixie/context.nim b/src/pixie/context.nim
index bf6a3cc..a081e57 100644
--- a/src/pixie/context.nim
+++ b/src/pixie/context.nim
@@ -193,7 +193,11 @@ proc clearRect*(ctx: Context, rect: Rect) =
   ## Erases the pixels in a rectangular area.
   var path: Path
   path.rect(rect)
-  ctx.image.fillPath(path, rgbx(0, 0, 0, 0), ctx.mat, blendMode = bmOverwrite)
+  ctx.image.fillPath(
+    path,
+    Paint(kind: pkSolid, color:rgbx(0, 0, 0, 0), blendMode: bmOverwrite),
+    ctx.mat
+  )
 
 proc clearRect*(ctx: Context, x, y, width, height: float32) {.inline.} =
   ## Erases the pixels in a rectangular area.
diff --git a/src/pixie/fileformats/svg.nim b/src/pixie/fileformats/svg.nim
index 666b6e8..c460ed5 100644
--- a/src/pixie/fileformats/svg.nim
+++ b/src/pixie/fileformats/svg.nim
@@ -1,6 +1,6 @@
 ## Load SVG files.
 
-import chroma, pixie/common, pixie/images, pixie/paths, strutils, vmath,
+import chroma, pixie/common, pixie/images, pixie/paths, pixie/paints, strutils, vmath,
     xmlparser, xmltree
 
 const
diff --git a/src/pixie/paints.nim b/src/pixie/paints.nim
index f3bc2a5..93ed8a7 100644
--- a/src/pixie/paints.nim
+++ b/src/pixie/paints.nim
@@ -34,7 +34,10 @@ converter parseSomePaint*(paint: SomePaint): Paint {.inline.} =
   when type(paint) is string:
     Paint(kind: pkSolid, color: parseHtmlColor(paint).rgbx())
   elif type(paint) is SomeColor:
-    Paint(kind: pkSolid, color: paint.rgbx())
+    when type(paint) is ColorRGBX:
+      Paint(kind: pkSolid, color: paint)
+    else:
+      Paint(kind: pkSolid, color: paint.rgbx())
   elif type(paint) is Paint:
     paint
 
diff --git a/src/pixie/paths.nim b/src/pixie/paths.nim
index 49b09c0..af95fd6 100644
--- a/src/pixie/paths.nim
+++ b/src/pixie/paths.nim
@@ -41,12 +41,15 @@ const epsilon = 0.0001 * PI ## Tiny value used for some computations.
 when defined(release):
   {.push checks: off.}
 
-proc maxScale(m: Mat3): float32 =
-  ## What is the largest scale factor of this matrix?
-  max(
-    vec2(m[0, 0], m[0, 1]).length,
-    vec2(m[1, 0], m[1, 1]).length
-  )
+proc pixelScale(transform: Vec2 | Mat3): float32 =
+  ## What is the largest scale factor of this transform?
+  when type(transform) is Vec2:
+    return 1.0
+  else:
+    max(
+      vec2(transform[0, 0], transform[0, 1]).length,
+      vec2(transform[1, 0], transform[1, 1]).length
+    )
 
 proc isRelative(kind: PathCommandKind): bool =
   kind in {
@@ -1317,16 +1320,27 @@ proc fillShapes(
         mask.setValueUnsafe(x, y, blended)
       inc x
 
+proc miterLimitToAngle*(limit: float32): float32 =
+  ## Converts milter-limit-ratio to miter-limit-angle.
+  arcsin(1 / limit) * 2
+
+proc angleToMiterLimit*(angle: float32): float32 =
+  ## Converts miter-limit-angle to milter-limit-ratio.
+  1 / sin(angle / 2)
+
 proc strokeShapes(
   shapes: seq[seq[Vec2]],
   strokeWidth: float32,
   lineCap: LineCap,
   lineJoin: LineJoin,
-  miterAngleLimit = degToRad(28.96)
+  miterLimit: float32,
+  dashes: seq[float32]
 ): seq[seq[Vec2]] =
   if strokeWidth == 0:
     return
 
+  let miterAngleLimit = miterLimitToAngle(miterLimit)
+
   let halfStroke = strokeWidth / 2
 
   proc makeCircle(at: Vec2): seq[Vec2] =
@@ -1413,7 +1427,25 @@ proc strokeShapes(
         pos = shape[i]
         prevPos = shape[i - 1]
 
-      shapeStroke.add(makeRect(prevPos, pos))
+      if dashes.len > 0:
+        var dashes = dashes
+        if dashes.len mod 2 != 0:
+          dashes.add(dashes[^1])
+        var distance = dist(prevPos, pos)
+        let dir = dir(pos, prevPos)
+        var currPos = prevPos
+        block dashLoop:
+          while true:
+            for i, d in dashes:
+              if i mod 2 == 0:
+                let d = min(distance, d)
+                shapeStroke.add(makeRect(currPos, currPos + dir * d))
+              currPos += dir * d
+              distance -= d
+              if distance <= 0:
+                break dashLoop
+      else:
+        shapeStroke.add(makeRect(prevPos, pos))
 
       # If we need a line join
       if i < shape.len - 1:
@@ -1459,53 +1491,14 @@ proc transform(shapes: var seq[seq[Vec2]], transform: Vec2 | Mat3) =
         for segment in shape.mitems:
           segment = transform * segment
 
-proc fillPath*(
-  image: Image,
-  path: SomePath,
-  color: SomeColor,
-  windingRule = wrNonZero,
-  blendMode = bmNormal
-) {.inline.} =
-  ## Fills a path.
-  image.fillShapes(parseSomePath(path), color, windingRule, blendMode)
-
-proc fillPath*(
-  image: Image,
-  path: SomePath,
-  color: SomeColor,
-  transform: Vec2 | Mat3,
-  windingRule = wrNonZero,
-  blendMode = bmNormal
-) =
-  ## Fills a path.
-  when type(transform) is Mat3:
-    let pixelScale = transform.maxScale()
-  else:
-    let pixelScale = 1.0
-  var shapes = parseSomePath(path, pixelScale)
-  shapes.transform(transform)
-  image.fillShapes(shapes, color, windingRule, blendMode)
-
 proc fillPath*(
   mask: Mask,
   path: SomePath,
-  windingRule = wrNonZero
-) {.inline.} =
-  ## Fills a path.
-  mask.fillShapes(parseSomePath(path), windingRule)
-
-proc fillPath*(
-  mask: Mask,
-  path: SomePath,
-  transform: Vec2 | Mat3,
+  transform: Vec2 | Mat3 = vec2(),
   windingRule = wrNonZero
 ) =
   ## Fills a path.
-  when type(transform) is Mat3:
-    let pixelScale = transform.maxScale()
-  else:
-    let pixelScale = 1.0
-  var shapes = parseSomePath(path, pixelScale)
+  var shapes = parseSomePath(path, transform.pixelScale())
   shapes.transform(transform)
   mask.fillShapes(shapes, windingRule)
 
@@ -1518,7 +1511,9 @@ proc fillPath*(
 ) =
   ## Fills a path.
   if paint.kind == pkSolid:
-    image.fillPath(path, paint.color, transform, windingRule)
+    var shapes = parseSomePath(path, transform.pixelScale())
+    shapes.transform(transform)
+    image.fillShapes(shapes, paint.color, windingRule, paint.blendMode)
     return
 
   let
@@ -1545,69 +1540,23 @@ proc fillPath*(
   image.draw(fill, blendMode = paint.blendMode)
 
 proc strokePath*(
-  image: Image,
+  mask: Mask,
   path: SomePath,
-  color: SomeColor,
+  transform: Vec2 | Mat3 = vec2(),
   strokeWidth = 1.0,
   lineCap = lcButt,
   lineJoin = ljMiter,
-  blendMode = bmNormal
+  miterLimit: float32 = 4,
+  dashes: seq[float32] = @[],
 ) =
   ## Strokes a path.
-  let strokeShapes = strokeShapes(
-    parseSomePath(path), strokeWidth, lineCap, lineJoin
-  )
-  image.fillShapes(strokeShapes, color, wrNonZero, blendMode)
-
-proc strokePath*(
-  image: Image,
-  path: SomePath,
-  color: SomeColor,
-  transform: Vec2 | Mat3,
-  strokeWidth = 1.0,
-  lineCap = lcButt,
-  lineJoin = ljMiter,
-  blendMode = bmNormal
-) =
-  ## Strokes a path.
-  when type(transform) is Mat3:
-    let pixelScale = transform.maxScale()
-  else:
-    let pixelScale = 1.0
   var strokeShapes = strokeShapes(
-    parseSomePath(path, pixelScale), strokeWidth, lineCap, lineJoin
-  )
-  strokeShapes.transform(transform)
-  image.fillShapes(strokeShapes, color, wrNonZero, blendMode)
-
-proc strokePath*(
-  mask: Mask,
-  path: SomePath,
-  strokeWidth = 1.0,
-  lineCap = lcButt,
-  lineJoin = ljMiter
-) =
-  ## Strokes a path.
-  let strokeShapes = strokeShapes(
-    parseSomePath(path), strokeWidth, lineCap, lineJoin
-  )
-  mask.fillShapes(strokeShapes, wrNonZero)
-
-proc strokePath*(
-  mask: Mask,
-  path: SomePath,
-  transform: Vec2 | Mat3,
-  strokeWidth = 1.0,
-  lineCap = lcButt,
-  lineJoin = ljMiter
-) =
-  ## Strokes a path.
-  when type(transform) is Mat3:
-    let pixelScale = transform.maxScale()
-  else:
-    let pixelScale = 1.0
-  var strokeShapes = strokeShapes(
-    parseSomePath(path, pixelScale), strokeWidth, lineCap, lineJoin
+    parseSomePath(path, transform.pixelScale()),
+    strokeWidth,
+    lineCap,
+    lineJoin,
+    miterLimit,
+    dashes
   )
   strokeShapes.transform(transform)
   mask.fillShapes(strokeShapes, wrNonZero)
@@ -1619,20 +1568,38 @@ proc strokePath*(
   transform: Vec2 | Mat3 = vec2(),
   strokeWidth = 1.0,
   lineCap = lcButt,
-  lineJoin = ljMiter
+  lineJoin = ljMiter,
+  miterLimit: float32 = 4,
+  dashes: seq[float32] = @[]
 ) =
-  ## Fills a path.
+  ## Strokes a path.
   if paint.kind == pkSolid:
-    image.strokePath(
-      path, paint.color, transform, strokeWidth, lineCap, lineJoin
+    var strokeShapes = strokeShapes(
+      parseSomePath(path, transform.pixelScale()),
+      strokeWidth,
+      lineCap,
+      lineJoin,
+      miterLimit,
+      dashes
     )
+    strokeShapes.transform(transform)
+    image.fillShapes(
+      strokeShapes, paint.color, wrNonZero, blendMode = paint.blendMode)
     return
 
   let
     mask = newMask(image.width, image.height)
     fill = newImage(image.width, image.height)
 
-  mask.strokePath(parseSomePath(path), transform)
+  mask.strokePath(
+    parseSomePath(path),
+    transform,
+    strokeWidth,
+    lineCap,
+    lineJoin,
+    miterLimit,
+    dashes
+  )
 
   case paint.kind:
     of pkSolid:
diff --git a/tests/images/paths/dashes.png b/tests/images/paths/dashes.png
new file mode 100644
index 0000000..5389bd8
Binary files /dev/null and b/tests/images/paths/dashes.png differ
diff --git a/tests/images/paths/miterLimit_10deg_2.00num.png b/tests/images/paths/miterLimit_10deg_2.00num.png
new file mode 100644
index 0000000..2359b83
Binary files /dev/null and b/tests/images/paths/miterLimit_10deg_2.00num.png differ
diff --git a/tests/images/paths/miterLimit_145deg_2.00num.png b/tests/images/paths/miterLimit_145deg_2.00num.png
new file mode 100644
index 0000000..5938bc2
Binary files /dev/null and b/tests/images/paths/miterLimit_145deg_2.00num.png differ
diff --git a/tests/images/paths/miterLimit_145deg_3.32num.png b/tests/images/paths/miterLimit_145deg_3.32num.png
new file mode 100644
index 0000000..5938bc2
Binary files /dev/null and b/tests/images/paths/miterLimit_145deg_3.32num.png differ
diff --git a/tests/images/paths/miterLimit_145deg_3.33num.png b/tests/images/paths/miterLimit_145deg_3.33num.png
new file mode 100644
index 0000000..237608a
Binary files /dev/null and b/tests/images/paths/miterLimit_145deg_3.33num.png differ
diff --git a/tests/images/paths/miterLimit_155deg_2.00num.png b/tests/images/paths/miterLimit_155deg_2.00num.png
new file mode 100644
index 0000000..76493bb
Binary files /dev/null and b/tests/images/paths/miterLimit_155deg_2.00num.png differ
diff --git a/tests/images/paths/miterLimit_165deg_10.00num.png b/tests/images/paths/miterLimit_165deg_10.00num.png
new file mode 100644
index 0000000..c65f635
Binary files /dev/null and b/tests/images/paths/miterLimit_165deg_10.00num.png differ
diff --git a/tests/images/paths/miterLimit_165deg_2.00num.png b/tests/images/paths/miterLimit_165deg_2.00num.png
new file mode 100644
index 0000000..1a96656
Binary files /dev/null and b/tests/images/paths/miterLimit_165deg_2.00num.png differ
diff --git a/tests/test_paths.nim b/tests/test_paths.nim
index 1dfa970..93d2d8e 100644
--- a/tests/test_paths.nim
+++ b/tests/test_paths.nim
@@ -1,4 +1,4 @@
-import chroma, pixie, pixie/fileformats/png
+import chroma, pixie, pixie/fileformats/png, strformat
 
 block:
   let pathStr = """
@@ -47,7 +47,7 @@ block:
     image = newImage(100, 100)
     pathStr = "M 10 10 L 90 90"
     color = rgba(255, 0, 0, 255)
-  image.strokePath(pathStr, color, 10)
+  image.strokePath(pathStr, color, strokeWidth=10)
   image.writeFile("tests/images/paths/pathStroke1.png")
 
 block:
@@ -55,7 +55,7 @@ block:
     image = newImage(100, 100)
     pathStr = "M 10 10 L 50 60 90 90"
     color = rgba(255, 0, 0, 255)
-  image.strokePath(pathStr, color, 10)
+  image.strokePath(pathStr, color, strokeWidth=10)
   image.writeFile("tests/images/paths/pathStroke2.png")
 
 block:
@@ -256,6 +256,68 @@ block:
 
   image.writeFile("tests/images/paths/lcSquare.png")
 
+block:
+  let
+    image = newImage(60, 120)
+    path = parsePath("M 0 0 L 50 0")
+  image.fill(rgba(255, 255, 255, 255))
+
+  image.strokePath(
+    path, rgba(0, 0, 0, 255), vec2(5, 5), 10, lcButt, ljBevel,
+  )
+
+  image.strokePath(
+    path, rgba(0, 0, 0, 255), vec2(5, 25), 10, lcButt, ljBevel,
+    dashes = @[2.float32,2]
+  )
+
+  image.strokePath(
+    path, rgba(0, 0, 0, 255), vec2(5, 45), 10, lcButt, ljBevel,
+    dashes = @[4.float32,4]
+  )
+
+  image.strokePath(
+    path, rgba(0, 0, 0, 255), vec2(5, 65), 10, lcButt, ljBevel,
+    dashes = @[2.float32, 4, 6, 2]
+  )
+
+  image.strokePath(
+    path, rgba(0, 0, 0, 255), vec2(5, 85), 10, lcButt, ljBevel,
+    dashes = @[1.float32]
+  )
+
+  image.strokePath(
+    path, rgba(0, 0, 0, 255), vec2(5, 105), 10, lcButt, ljBevel,
+    dashes = @[1.float32, 2, 3, 4, 5, 6, 7, 8, 9]
+  )
+
+  image.writeFile("tests/images/paths/dashes.png")
+
+block:
+  proc miterTest(angle, limit: float32) =
+    let
+      image = newImage(60, 60)
+    image.fill(rgba(255, 255, 255, 255))
+    var path: Path
+    path.moveTo(-20, 0)
+    path.lineTo(0, 0)
+    let th = angle.float32.degToRad() + PI/2
+    path.lineTo(sin(th)*20, cos(th)*20)
+
+    image.strokePath(
+      path, rgba(0, 0, 0, 255), vec2(30, 30), 8, lcButt, ljMiter,
+      miterLimit = limit
+    )
+    image.writeFile(&"tests/images/paths/miterLimit_{angle.int}deg_{limit:0.2f}num.png")
+
+  miterTest(10, 2)
+  miterTest(145, 2)
+  miterTest(155, 2)
+  miterTest(165, 2)
+  miterTest(165, 10)
+  miterTest(145, 3.32)
+  miterTest(145, 3.33)
+
 # Potential error cases, ensure they do not crash
 
 block: