Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 56 additions & 49 deletions src/pixie/paths.nim
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ type
partitions: seq[Partition]
startY, partitionHeight: uint32

Fixed32 = int32 ## 24.8 fixed point

const
epsilon: float32 = 0.0001 * PI ## Tiny value used for some computations.
pixelErrorMargin: float32 = 0.2
Expand Down Expand Up @@ -1149,20 +1151,20 @@ proc partitionSegments(
partition.requiresAntiAliasing =
requiresAntiAliasing(partition.entries)

proc getIndexForY(partitioning: var Partitioning, y: int): uint32 {.inline.} =
if partitioning.partitions.len == 1:
0.uint32
else:
min(
(y.uint32 - partitioning.startY) div partitioning.partitionHeight,
partitioning.partitions.high.uint32
)

proc maxEntryCount(partitioning: var Partitioning): int =
for i in 0 ..< partitioning.partitions.len:
result = max(result, partitioning.partitions[i].entries.len)

proc sortHits(hits: var seq[(float32, int16)], inl, inr: int) =
proc fixed32(f: float32): Fixed32 {.inline.} =
Fixed32(f * 256)

proc integer(p: Fixed32): int {.inline.} =
p div 256

proc trunc(p: Fixed32): Fixed32 {.inline.} =
(p div 256) * 256

proc sortHits(hits: var seq[(Fixed32, int16)], inl, inr: int) =
## Quicksort + insertion sort, in-place and faster than standard lib sort.
let n = inr - inl + 1
if n < 32: # Use insertion sort for the rest
Expand Down Expand Up @@ -1202,15 +1204,15 @@ proc shouldFill(
count mod 2 != 0

iterator walk(
hits: seq[(float32, int16)],
hits: seq[(Fixed32, int16)],
numHits: int,
windingRule: WindingRule,
y: int,
width: float32
): (float32, float32, int) =
width: int
): (Fixed32, Fixed32, int) =
var
i, count: int
prevAt: float32
prevAt: Fixed32
while i < numHits:
let (at, winding) = hits[i]
if at > 0:
Expand All @@ -1236,20 +1238,27 @@ iterator walk(
inc i

when defined(pixieLeakCheck):
if prevAt != width and count != 0:
if prevAt != width.float32.fixed32 and count != 0:
echo "Leak detected: ", count, " @ (", prevAt, ", ", y, ")"

proc computeCoverage(
coverages: ptr UncheckedArray[uint8],
hits: var seq[(float32, int16)],
hits: var seq[(Fixed32, int16)],
numHits: var int,
aa: var bool,
width: float32,
width: int,
y, startX: int,
partitioning: var Partitioning,
windingRule: WindingRule
) {.inline.} =
let partitionIndex = partitioning.getIndexForY(y)
let partitionIndex =
if partitioning.partitions.len == 1:
0.uint32
else:
min(
(y.uint32 - partitioning.startY) div partitioning.partitionHeight,
partitioning.partitions.high.uint32
)

aa = partitioning.partitions[partitionIndex].requiresAntiAliasing

Expand All @@ -1271,35 +1280,35 @@ proc computeCoverage(
else:
(yLine - entry.b) / entry.m

hits[numHits] = (min(x, width), entry.winding)
hits[numHits] = (min(x, width.float32).fixed32, entry.winding)
inc numHits

if numHits > 0:
sortHits(hits, 0, numHits - 1)

if aa:
for (prevAt, at, count) in hits.walk(numHits, windingRule, y, width):
var fillStart = prevAt.int
var fillStart = prevAt.integer

let
pixelCrossed = at.int - prevAt.int > 0
pixelCrossed = at.integer != prevAt.integer
leftCover =
if pixelCrossed:
trunc(prevAt) + 1 - prevAt
prevAt.trunc + 1.0.fixed32 - prevAt
else:
at - prevAt
if leftCover != 0:
inc fillStart
coverages[prevAt.int - startX] +=
(leftCover * sampleCoverage.float32).uint8
coverages[prevAt.integer - startX] +=
(leftCover * sampleCoverage.int32).integer.uint8

if pixelCrossed:
let rightCover = at - trunc(at)
let rightCover = at - at.trunc
if rightCover > 0:
coverages[at.int - startX] +=
(rightCover * sampleCoverage.float32).uint8
coverages[at.integer - startX] +=
(rightCover * sampleCoverage.int32).integer.uint8

let fillLen = at.int - fillStart
let fillLen = at.integer - fillStart
if fillLen > 0:
var i = fillStart
when defined(amd64) and allowSimd:
Expand Down Expand Up @@ -1494,19 +1503,17 @@ proc fillHits(
image: Image,
rgbx: ColorRGBX,
startX, y: int,
hits: seq[(float32, int16)],
hits: seq[(Fixed32, int16)],
numHits: int,
windingRule: WindingRule,
blendMode: BlendMode
) =
let
blender = blendMode.blender()
width = image.width.float32
let blender = blendMode.blender()
var filledTo: int
for (prevAt, at, count) in hits.walk(numHits, windingRule, y, width):
for (prevAt, at, count) in hits.walk(numHits, windingRule, y, image.width):
let
fillStart = prevAt.int
fillLen = at.int - fillStart
fillStart = prevAt.integer
fillLen = at.integer - fillStart
if fillLen <= 0:
continue

Expand Down Expand Up @@ -1556,19 +1563,17 @@ proc fillHits(
proc fillHits(
mask: Mask,
startX, y: int,
hits: seq[(float32, int16)],
hits: seq[(Fixed32, int16)],
numHits: int,
windingRule: WindingRule,
blendMode: BlendMode
) =
let
masker = blendMode.masker()
width = mask.width.float32
let masker = blendMode.masker()
var filledTo: int
for (prevAt, at, count) in hits.walk(numHits, windingRule, y, width):
for (prevAt, at, count) in hits.walk(numHits, windingRule, y, mask.width):
let
fillStart = prevAt.int
fillLen = at.int - fillStart
fillStart = prevAt.integer
fillLen = at.integer - fillStart
if fillLen <= 0:
continue

Expand Down Expand Up @@ -1633,7 +1638,7 @@ proc fillShapes(
var
partitioning = partitionSegments(segments, startY, pathHeight - startY)
coverages = newSeq[uint8](pathWidth)
hits = newSeq[(float32, int16)](partitioning.maxEntryCount)
hits = newSeq[(Fixed32, int16)](partitioning.maxEntryCount)
numHits: int
aa: bool

Expand All @@ -1643,7 +1648,7 @@ proc fillShapes(
hits,
numHits,
aa,
image.width.float32,
image.width,
y,
startX,
partitioning,
Expand Down Expand Up @@ -1702,7 +1707,7 @@ proc fillShapes(
var
partitioning = partitionSegments(segments, startY, pathHeight)
coverages = newSeq[uint8](pathWidth)
hits = newSeq[(float32, int16)](partitioning.maxEntryCount)
hits = newSeq[(Fixed32, int16)](partitioning.maxEntryCount)
numHits: int
aa: bool

Expand All @@ -1712,7 +1717,7 @@ proc fillShapes(
hits,
numHits,
aa,
mask.width.float32,
mask.width,
y,
startX,
partitioning,
Expand Down Expand Up @@ -2071,7 +2076,7 @@ proc overlaps(
test: Vec2,
windingRule: WindingRule
): bool =
var hits: seq[(float32, int16)]
var hits: seq[(Fixed32, int16)]

let
scanline = line(vec2(0, test.y), vec2(1000, test.y))
Expand All @@ -2081,13 +2086,15 @@ proc overlaps(
var at: Vec2
if scanline.intersects(segment, at):
if segment.to != at:
hits.add((at.x, winding))
hits.add((at.x.fixed32, winding))

sortHits(hits, 0, hits.high)

let testX = test.x.fixed32

var count: int
for (at, winding) in hits:
if at > test.x:
if at > testX:
return shouldFill(windingRule, count)
count += winding

Expand Down