Merge pull request #323 from guzba/master

simpler + faster curves to segments, leak fix
This commit is contained in:
treeform 2021-11-22 16:02:11 -08:00 committed by GitHub
commit 8ba0ce3fb9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
182 changed files with 79 additions and 62 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View file

@ -1,6 +1,6 @@
import benchy, svg_cairo
let data = readFile("tests/images/svg/Ghostscript_Tiger.svg")
let data = readFile("tests/fileformats/svg/Ghostscript_Tiger.svg")
timeIt "svg decode":
keep decodeSvg(data)
discard decodeSvg(data)

View file

@ -990,6 +990,9 @@ proc superImage*(image: Image, x, y, w, h: int): Image {.raises: [PixieError].}
## Either cuts a sub image or returns a super image with padded transparency.
if x >= 0 and x + w <= image.width and y >= 0 and y + h <= image.height:
result = image.subImage(x, y, w, h)
elif abs(x) >= image.width or abs(y) >= image.height:
# Nothing to copy, just an empty new image
result = newImage(w, h)
else:
result = newImage(w, h)
result.draw(image, translate(vec2(-x.float32, -y.float32)), bmOverwrite)

View file

@ -45,7 +45,7 @@ type
startY, partitionHeight: uint32
const
epsilon: float32 = 0.0001 * PI ## Tiny value used for some computations.
epsilon: float64 = 0.0001 * PI ## Tiny value used for some computations. Must be float64 to prevent leaks.
defaultMiterLimit*: float32 = 4
when defined(release):
@ -651,7 +651,7 @@ proc commandsToShapes(
prevCommandKind = Move
prevCtrl, prevCtrl2: Vec2
let errorMargin = 0.2 / pixelScale
let errorMarginSq = pow(0.2.float32 / pixelScale, 2)
proc addSegment(shape: var seq[Vec2], at, to: Vec2) =
# Don't add any 0 length lines
@ -669,27 +669,29 @@ proc commandsToShapes(
(1 - t) * 3 * pow(t, 2) * ctrl2 +
pow(t, 3) * to
var prev = at
proc discretize(shape: var seq[Vec2], i, steps: int) =
# Closure captures at, ctrl1, ctrl2, to and prev
var
t: float32 # Where we are at on the curve from [0, 1]
step = 1.float32 # How far we want to try to move along the curve
prev = at
next = compute(at, ctrl1, ctrl2, to, t + step)
halfway = compute(at, ctrl1, ctrl2, to, t + step / 2)
while true:
let
tPrev = (i - 1).float32 / steps.float32
t = i.float32 / steps.float32
next = compute(at, ctrl1, ctrl2, to, t)
halfway = compute(at, ctrl1, ctrl2, to, tPrev + (t - tPrev) / 2)
midpoint = (prev + next) / 2
error = (midpoint - halfway).length
if error >= errorMargin:
# Error too large, double precision for this step
shape.discretize(i * 2 - 1, steps * 2)
shape.discretize(i * 2, steps * 2)
error = (midpoint - halfway).lengthSq
if error > errorMarginSq:
next = halfway
halfway = compute(at, ctrl1, ctrl2, to, t + step / 4)
step /= 2
else:
shape.addSegment(prev, next)
t += step
if t == 1:
break
prev = next
shape.discretize(1, 1)
step = min(step * 2, 1 - t) # Optimistically attempt larger steps
next = compute(at, ctrl1, ctrl2, to, t + step)
halfway = compute(at, ctrl1, ctrl2, to, t + step / 2)
proc addQuadratic(shape: var seq[Vec2], at, ctrl, to: Vec2) =
## Adds quadratic segments to shape.
@ -698,27 +700,34 @@ proc commandsToShapes(
2 * (1 - t) * t * ctrl +
pow(t, 2) * to
var prev = at
proc discretize(shape: var seq[Vec2], i, steps: int) =
# Closure captures at, ctrl, to and prev
var
t: float32 # Where we are at on the curve from [0, 1]
step = 1.float32 # How far we want to try to move along the curve
prev = at
next = compute(at, ctrl, to, t + step)
halfway = compute(at, ctrl, to, t + step / 2)
halfStepping = false
while true:
let
tPrev = (i - 1).float32 / steps.float32
t = i.float32 / steps.float32
next = compute(at, ctrl, to, t)
halfway = compute(at, ctrl, to, tPrev + (t - tPrev) / 2)
midpoint = (prev + next) / 2
error = (midpoint - halfway).length
if error >= errorMargin:
# Error too large, double precision for this step
shape.discretize(i * 2 - 1, steps * 2)
shape.discretize(i * 2, steps * 2)
error = (midpoint - halfway).lengthSq
if error > errorMarginSq:
next = halfway
halfway = compute(at, ctrl, to, t + step / 4)
step /= 2
halfStepping = true
else:
shape.addSegment(prev, next)
t += step
if t == 1:
break
prev = next
shape.discretize(1, 1)
if halfStepping:
step = min(step, 1 - t)
else:
step = min(step * 2, 1 - t) # Optimistically attempt larger steps
next = compute(at, ctrl, to, t + step)
halfway = compute(at, ctrl, to, t + step / 2)
proc addArc(
shape: var seq[Vec2],
@ -808,28 +817,37 @@ proc commandsToShapes(
result = vec2(cos(a) * arc.radii.x, sin(a) * arc.radii.y)
result = arc.rotMat * result + arc.center
var prev = at
let arc = endpointToCenterArcParams(at, radii, rotation, large, sweep, to)
proc discretize(shape: var seq[Vec2], arc: ArcParams, i, steps: int) =
var
t: float32 # Where we are at on the curve from [0, 1]
step = 1.float32 # How far we want to try to move along the curve
prev = at
while t != 1:
let
step = arc.delta / steps.float32
aPrev = arc.theta + step * (i - 1).float32
a = arc.theta + step * i.float32
aPrev = arc.theta + arc.delta * t
a = arc.theta + arc.delta * (t + step)
next = arc.compute(a)
halfway = arc.compute(aPrev + (a - aPrev) / 2)
midpoint = (prev + next) / 2
error = (midpoint - halfway).length
if error >= errorMargin:
# Error too large, try again with doubled precision
shape.discretize(arc, i * 2 - 1, steps * 2)
shape.discretize(arc, i * 2, steps * 2)
error = (midpoint - halfway).lengthSq
if error > errorMarginSq:
let
quarterway = arc.compute(aPrev + (a - aPrev) / 4)
midpoint = (prev + halfway) / 2
halfwayError = (midpoint - quarterway).lengthSq
if halfwayError < errorMarginSq:
shape.addSegment(prev, halfway)
prev = halfway
t += step / 2
step = min(step / 2, 1 - t) # Assume next steps hould be the same size
else:
step = step / 4 # We know a half-step is too big
else:
shape.addSegment(prev, next)
prev = next
let arc = endpointToCenterArcParams(at, radii, rotation, large, sweep, to)
shape.discretize(arc, 1, 1)
t += step
step = min(step * 2, 1 - t) # Optimistically attempt larger steps
for command in path.commands:
if command.numbers.len != command.kind.parameterCount():
@ -1002,9 +1020,7 @@ proc commandsToShapes(
shape.addSegment(at, start)
result.add(shape)
proc shapesToSegments(
shapes: seq[seq[Vec2]]
): seq[(Segment, int16)] =
proc shapesToSegments(shapes: seq[seq[Vec2]]): seq[(Segment, int16)] =
## Converts the shapes into a set of filtered segments with winding value.
for shape in shapes:
for segment in shape.segments:
@ -1084,8 +1100,8 @@ proc partitionSegments(
): Partitioning =
## Puts segments into the height partitions they intersect with.
let
maxPartitions = max(1, height div 10).uint32
numPartitions = min(maxPartitions, max(1, segments.len div 10).uint32)
maxPartitions = max(1, height div 4).uint32
numPartitions = min(maxPartitions, max(1, segments.len div 4).uint32)
result.partitions.setLen(numPartitions)
result.startY = top.uint32
@ -1214,7 +1230,7 @@ proc computeCoverage(
let
quality = if aa: 5 else: 1 # Must divide 255 cleanly (1, 3, 5, 15, 17, 51, 85)
sampleCoverage = (255 div quality).uint8
offset = 1 / quality.float32
offset = 1 / quality.float64
initialOffset = offset / 2 + epsilon
if aa: # Coverage is only used for anti-aliasing
@ -1224,7 +1240,7 @@ proc computeCoverage(
let
partitionIndex = partitioning.getIndexForY(y)
partitionEntryCount = partitioning.partitions[partitionIndex].len
var yLine = y.float32 + initialOffset - offset
var yLine = y.float64 + initialOffset - offset
for m in 0 ..< quality:
yLine += offset
numHits = 0
@ -1813,8 +1829,7 @@ proc fillPath*(
var shapes = parseSomePath(path, true, transform.pixelScale())
shapes.transform(transform)
var color = paint.color
if paint.opacity != 1:
color.a *= paint.opacity
color.a *= paint.opacity
image.fillShapes(shapes, color, windingRule, paint.blendMode)
return
@ -1897,8 +1912,7 @@ proc strokePath*(
)
strokeShapes.transform(transform)
var color = paint.color
if paint.opacity != 1:
color.a *= paint.opacity
color.a *= paint.opacity
image.fillShapes(strokeShapes, color, wrNonZero, paint.blendMode)
return

View file

@ -3,4 +3,4 @@ import benchy, pixie/fileformats/svg
let data = readFile("tests/fileformats/svg/Ghostscript_Tiger.svg")
timeIt "svg decode":
keep decodeSvg(data)
discard decodeSvg(data)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 KiB

After

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 644 KiB

After

Width:  |  Height:  |  Size: 644 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 783 KiB

After

Width:  |  Height:  |  Size: 783 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 MiB

After

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 357 KiB

After

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4 KiB

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 610 KiB

After

Width:  |  Height:  |  Size: 610 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 519 KiB

After

Width:  |  Height:  |  Size: 519 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

After

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Some files were not shown because too many files have changed in this diff Show more