diff --git a/src/pixie/paths.nim b/src/pixie/paths.nim index e098980..ac89a5a 100644 --- a/src/pixie/paths.nim +++ b/src/pixie/paths.nim @@ -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 @@ -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 @@ -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: @@ -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 @@ -1271,7 +1280,7 @@ 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: @@ -1279,27 +1288,27 @@ proc computeCoverage( 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: @@ -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 @@ -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 @@ -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 @@ -1643,7 +1648,7 @@ proc fillShapes( hits, numHits, aa, - image.width.float32, + image.width, y, startX, partitioning, @@ -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 @@ -1712,7 +1717,7 @@ proc fillShapes( hits, numHits, aa, - mask.width.float32, + mask.width, y, startX, partitioning, @@ -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)) @@ -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