Merge pull request #73 from guzba/master

start+stop bounds = faster fill
This commit is contained in:
treeform 2021-01-24 20:40:41 -08:00 committed by GitHub
commit 53703a4a7f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -20,6 +20,11 @@ type
commands*: seq[PathCommand] commands*: seq[PathCommand]
start, at: Vec2 # Maintained by moveTo, lineTo, etc. Used by arcTo. start, at: Vec2 # Maintained by moveTo, lineTo, etc. Used by arcTo.
SomePath* = Path | string | seq[seq[Segment]]
when defined(release):
{.push checks: off.}
proc parameterCount(kind: PathCommandKind): int = proc parameterCount(kind: PathCommandKind): int =
case kind: case kind:
of Close: 0 of Close: 0
@ -773,14 +778,11 @@ proc computeBounds(shape: seq[Segment]): Rect =
result.w = xMax - xMin result.w = xMax - xMin
result.h = yMax - yMin result.h = yMax - yMin
{.push checks: off, stacktrace: off.}
proc fillShapes*( proc fillShapes*(
image: Image, image: Image,
shapes: seq[seq[Segment]], shapes: seq[seq[Segment]],
color: ColorRGBA, color: ColorRGBA,
windingRule: WindingRule, windingRule: WindingRule
quality = 4
) = ) =
var sortedShapes = newSeq[seq[(Segment, bool)]](shapes.len) var sortedShapes = newSeq[seq[(Segment, bool)]](shapes.len)
for i, sorted in sortedShapes.mpairs: for i, sorted in sortedShapes.mpairs:
@ -800,7 +802,25 @@ proc fillShapes*(
for i, shape in shapes: for i, shape in shapes:
bounds[i] = computeBounds(shape) bounds[i] = computeBounds(shape)
const ep = 0.0001 * PI # Figure out the total bounds of all the shapes
var
minX = float32.high
minY = float32.high
maxY = float32.low
for bounds in bounds:
minX = min(minX, bounds.x)
minY = min(minY, bounds.y)
maxY = max(maxY, bounds.y + bounds.h)
# Rasterize only within the total bounds
let
startX = max(0, minX.int)
startY = max(0, miny.int)
stopY = min(image.height, maxY.int)
const
quality = 4
ep = 0.0001 * PI
proc scanLineHits( proc scanLineHits(
shapes: seq[seq[(Segment, bool)]], shapes: seq[seq[(Segment, bool)]],
@ -831,7 +851,7 @@ proc fillShapes*(
var var
hits = newSeq[(float32, bool)]() hits = newSeq[(float32, bool)]()
alphas = newSeq[float32](image.width) alphas = newSeq[float32](image.width)
for y in 0 ..< image.height: for y in startY ..< stopY:
# Reset alphas for this row. # Reset alphas for this row.
zeroMem(alphas[0].addr, alphas.len * 4) zeroMem(alphas[0].addr, alphas.len * 4)
@ -843,7 +863,7 @@ proc fillShapes*(
var var
penFill = 0 penFill = 0
curHit = 0 curHit = 0
for x in 0 ..< image.width: for x in startX ..< image.width:
var penEdge: float32 var penEdge: float32
case windingRule case windingRule
of wrNonZero: of wrNonZero:
@ -877,16 +897,11 @@ proc fillShapes*(
let rgba = image.getRgbaUnsafe(x, y) let rgba = image.getRgbaUnsafe(x, y)
image.setRgbaUnsafe(x, y, blendNormal(rgba, colorWithAlpha)) image.setRgbaUnsafe(x, y, blendNormal(rgba, colorWithAlpha))
{.pop.}
type SomePath = seq[seq[Segment]] | string | Path
proc parseSomePath(path: SomePath): seq[seq[Segment]] = proc parseSomePath(path: SomePath): seq[seq[Segment]] =
## Given some path, turns it into polys.
when type(path) is string: when type(path) is string:
commandsToShapes(parsePath(path)) parsePath(path).commandsToShapes()
elif type(path) is Path: elif type(path) is Path:
commandsToShapes(path) path.commandsToShapes()
elif type(path) is seq[seq[Segment]]: elif type(path) is seq[seq[Segment]]:
path path
@ -895,9 +910,8 @@ proc fillPath*(
path: SomePath, path: SomePath,
color: ColorRGBA, color: ColorRGBA,
windingRule = wrNonZero windingRule = wrNonZero
) = ) {.inline.} =
let polys = parseSomePath(path) image.fillShapes(parseSomePath(path), color, windingRule)
image.fillShapes(polys, color, windingRule)
proc fillPath*( proc fillPath*(
image: Image, image: Image,
@ -906,11 +920,11 @@ proc fillPath*(
pos: Vec2, pos: Vec2,
windingRule = wrNonZero windingRule = wrNonZero
) = ) =
var polys = parseSomePath(path) var shapes = parseSomePath(path)
for poly in polys.mitems: for shape in shapes.mitems:
for i, p in poly.mpairs: for segment in shape.mitems:
poly[i] = p + pos segment += pos
image.fillShapes(polys, color, windingRule) image.fillShapes(shapes, color, windingRule)
proc fillPath*( proc fillPath*(
image: Image, image: Image,
@ -919,11 +933,11 @@ proc fillPath*(
mat: Mat3, mat: Mat3,
windingRule = wrNonZero windingRule = wrNonZero
) = ) =
var polys = parseSomePath(path) var shapes = parseSomePath(path)
for poly in polys.mitems: for shape in shapes.mitems:
for i, p in poly.mpairs: for segment in shape.mitems:
poly[i] = mat * p segment = mat * segment
image.fillShapes(polys, color, windingRule) image.fillShapes(shapes, color, windingRule)
proc strokePath*( proc strokePath*(
image: Image, image: Image,
@ -977,3 +991,6 @@ proc strokePath*(
for segment in shape.mitems: for segment in shape.mitems:
segment = mat * segment segment = mat * segment
image.fillShapes(strokeShapes, color, windingRule) image.fillShapes(strokeShapes, color, windingRule)
when defined(release):
{.pop.}