Merge pull request #322 from guzba/master

merge hits that stop and then start fill at same spot, other stuff
This commit is contained in:
treeform 2021-11-21 17:09:20 -08:00 committed by GitHub
commit 92010d3b35
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 74 additions and 57 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View file

@ -1068,6 +1068,17 @@ proc computeBounds*(
shapes.transform(transform) shapes.transform(transform)
computeBounds(shapes.shapesToSegments()) computeBounds(shapes.shapesToSegments())
proc initPartitionEntry(segment: Segment, winding: int16): PartitionEntry =
result.atY = segment.at.y
result.toY = segment.to.y
result.winding = winding
let d = segment.at.x - segment.to.x
if d == 0:
result.b = segment.at.x # Leave m = 0, store the x we want in b
else:
result.m = (segment.at.y - segment.to.y) / d
result.b = segment.at.y - result.m * segment.at.x
proc partitionSegments( proc partitionSegments(
segments: seq[(Segment, int16)], top, height: int segments: seq[(Segment, int16)], top, height: int
): Partitioning = ): Partitioning =
@ -1081,17 +1092,7 @@ proc partitionSegments(
result.partitionHeight = height.uint32 div numPartitions result.partitionHeight = height.uint32 div numPartitions
for (segment, winding) in segments: for (segment, winding) in segments:
var entry: PartitionEntry let entry = initPartitionEntry(segment, winding)
entry.atY = segment.at.y
entry.toY = segment.to.y
entry.winding = winding
let d = segment.at.x - segment.to.x
if d == 0:
entry.b = segment.at.x # Leave m = 0, store the x we want in b
else:
entry.m = (segment.at.y - segment.to.y) / d
entry.b = segment.at.y - entry.m * segment.at.x
if result.partitionHeight == 0: if result.partitionHeight == 0:
result.partitions[0].add(entry) result.partitions[0].add(entry)
else: else:
@ -1106,7 +1107,7 @@ proc partitionSegments(
result.partitions[i].add(entry) result.partitions[i].add(entry)
proc getIndexForY(partitioning: Partitioning, y: int): uint32 {.inline.} = proc getIndexForY(partitioning: Partitioning, y: int): uint32 {.inline.} =
if partitioning.partitionHeight == 0 or partitioning.partitions.len == 1: if partitioning.partitions.len == 1:
0.uint32 0.uint32
else: else:
min( min(
@ -1114,39 +1115,43 @@ proc getIndexForY(partitioning: Partitioning, y: int): uint32 {.inline.} =
partitioning.partitions.high.uint32 partitioning.partitions.high.uint32
) )
proc maxEntryCount(partitioning: Partitioning): int =
for i in 0 ..< partitioning.partitions.len:
result = max(result, partitioning.partitions[i].len)
proc insertionSort( proc insertionSort(
a: var seq[(float32, int16)], lo, hi: int hits: var seq[(float32, int16)], lo, hi: int
) {.inline.} = ) {.inline.} =
for i in lo + 1 .. hi: for i in lo + 1 .. hi:
var var
j = i - 1 j = i - 1
k = i k = i
while j >= 0 and a[j][0] > a[k][0]: while j >= 0 and hits[j][0] > hits[k][0]:
swap(a[j + 1], a[j]) swap(hits[j + 1], hits[j])
dec j dec j
dec k dec k
proc sort(a: var seq[(float32, int16)], inl, inr: int) = proc sort(hits: var seq[(float32, int16)], inl, inr: int) =
## Quicksort + insertion sort, in-place and faster than standard lib sort. ## Quicksort + insertion sort, in-place and faster than standard lib sort.
let n = inr - inl + 1 let n = inr - inl + 1
if n < 32: if n < 32:
insertionSort(a, inl, inr) insertionSort(hits, inl, inr)
return return
var var
l = inl l = inl
r = inr r = inr
let p = a[l + n div 2][0] let p = hits[l + n div 2][0]
while l <= r: while l <= r:
if a[l][0] < p: if hits[l][0] < p:
inc l inc l
elif a[r][0] > p: elif hits[r][0] > p:
dec r dec r
else: else:
swap(a[l], a[r]) swap(hits[l], hits[r])
inc l inc l
dec r dec r
sort(a, inl, r) sort(hits, inl, r)
sort(a, l, inr) sort(hits, l, inr)
proc shouldFill( proc shouldFill(
windingRule: WindingRule, count: int windingRule: WindingRule, count: int
@ -1166,28 +1171,37 @@ iterator walk(
width: float32 width: float32
): (float32, float32, int) = ): (float32, float32, int) =
var var
i, count: int
prevAt: float32 prevAt: float32
count: int while i < numHits:
for i in 0 ..< numHits:
let (at, winding) = hits[i] let (at, winding) = hits[i]
if windingRule == wrNonZero and
(count != 0) == (count + winding != 0) and
i < numHits - 1:
# Shortcut: if nonzero rule, we only care about when the count changes
# between zero and nonzero (or the last hit)
count += winding
continue
if at > 0: if at > 0:
if shouldFill(windingRule, count): if shouldFill(windingRule, count):
if i < numHits - 1:
# Look ahead to see if the next hit is in the same spot as this hit.
# If it is, see if this hit and the next hit's windings cancel out.
# If they do, skip the hits. It will be yielded later in a
# larger chunk.
let (nextAt, nextWinding) = hits[i + 1]
if nextAt == at and winding + nextWinding == 0:
i += 2
continue
# Shortcut: we only care about when we stop filling (or the last hit).
# If we continue filling, move to next hit.
if windingRule == wrNonZero and count + winding != 0:
count += winding
inc i
continue
yield (prevAt, at, count) yield (prevAt, at, count)
prevAt = at prevAt = at
count += winding count += winding
inc i
when defined(pixieLeakCheck): when defined(pixieLeakCheck):
if prevAt != width and count != 0: if prevAt != width and count != 0:
echo "Leak detected: ", count, " @ (", prevAt, ", ", y, ")" echo "Leak detected: ", count, " @ (", prevAt, ", ", y, ")"
proc computeCoverages( proc computeCoverage(
coverages: var seq[uint8], coverages: var seq[uint8],
hits: var seq[(float32, int16)], hits: var seq[(float32, int16)],
numHits: var int, numHits: var int,
@ -1215,7 +1229,7 @@ proc computeCoverages(
yLine += offset yLine += offset
numHits = 0 numHits = 0
for i in 0 ..< partitionEntryCount: # Perf for i in 0 ..< partitionEntryCount: # Perf
let entry = partitioning.partitions[partitionIndex][i] let entry = partitioning.partitions[partitionIndex][i].unsafeAddr # Perf
if entry.atY <= yLine and entry.toY >= yLine: if entry.atY <= yLine and entry.toY >= yLine:
let x = let x =
if entry.m == 0: if entry.m == 0:
@ -1223,8 +1237,6 @@ proc computeCoverages(
else: else:
(yLine - entry.b) / entry.m (yLine - entry.b) / entry.m
if numHits == hits.len:
hits.setLen(hits.len * 2)
hits[numHits] = (min(x, width), entry.winding) hits[numHits] = (min(x, width), entry.winding)
inc numHits inc numHits
@ -1259,9 +1271,9 @@ proc computeCoverages(
when defined(amd64) and not defined(pixieNoSimd): when defined(amd64) and not defined(pixieNoSimd):
let sampleCoverageVec = mm_set1_epi8(cast[int8](sampleCoverage)) let sampleCoverageVec = mm_set1_epi8(cast[int8](sampleCoverage))
for _ in 0 ..< fillLen div 16: for _ in 0 ..< fillLen div 16:
var coverage = mm_loadu_si128(coverages[i - startX].addr) var coverageVec = mm_loadu_si128(coverages[i - startX].addr)
coverage = mm_add_epi8(coverage, sampleCoverageVec) coverageVec = mm_add_epi8(coverageVec, sampleCoverageVec)
mm_storeu_si128(coverages[i - startX].addr, coverage) mm_storeu_si128(coverages[i - startX].addr, coverageVec)
i += 16 i += 16
for j in i ..< fillStart + fillLen: for j in i ..< fillStart + fillLen:
coverages[j - startX] += sampleCoverage coverages[j - startX] += sampleCoverage
@ -1299,11 +1311,11 @@ proc fillCoverage(
for _ in 0 ..< coverages.len div 16: for _ in 0 ..< coverages.len div 16:
let let
index = image.dataIndex(x, y) index = image.dataIndex(x, y)
coverage = mm_loadu_si128(coverages[x - startX].unsafeAddr) coverageVec = mm_loadu_si128(coverages[x - startX].unsafeAddr)
if mm_movemask_epi8(mm_cmpeq_epi16(coverage, zeroVec)) != 0xffff: if mm_movemask_epi8(mm_cmpeq_epi16(coverageVec, zeroVec)) != 0xffff:
# If the coverages are not all zero # If the coverages are not all zero
if mm_movemask_epi8(mm_cmpeq_epi32(coverage, vec255)) == 0xffff: if mm_movemask_epi8(mm_cmpeq_epi32(coverageVec, vec255)) == 0xffff:
# If the coverages are all 255 # If the coverages are all 255
if blendMode == bmNormal and rgbx.a == 255: if blendMode == bmNormal and rgbx.a == 255:
for i in 0 ..< 4: for i in 0 ..< 4:
@ -1317,9 +1329,9 @@ proc fillCoverage(
) )
else: else:
# Coverages are not all 255 # Coverages are not all 255
var coverage = coverage var coverageVec = coverageVec
for i in 0 ..< 4: for i in 0 ..< 4:
var unpacked = unpackAlphaValues(coverage) var unpacked = unpackAlphaValues(coverageVec)
# Shift the coverages from `a` to `g` and `a` for multiplying # Shift the coverages from `a` to `g` and `a` for multiplying
unpacked = mm_or_si128(unpacked, mm_srli_epi32(unpacked, 16)) unpacked = mm_or_si128(unpacked, mm_srli_epi32(unpacked, 16))
@ -1342,7 +1354,7 @@ proc fillCoverage(
blenderSimd(backdrop, source) blenderSimd(backdrop, source)
) )
coverage = mm_srli_si128(coverage, 4) coverageVec = mm_srli_si128(coverageVec, 4)
elif blendMode == bmMask: elif blendMode == bmMask:
for i in 0 ..< 4: for i in 0 ..< 4:
@ -1534,11 +1546,11 @@ proc fillShapes(
var var
coverages = newSeq[uint8](bounds.w.int) coverages = newSeq[uint8](bounds.w.int)
hits = newSeq[(float32, int16)](4) hits = newSeq[(float32, int16)](partitioning.maxEntryCount)
numHits: int numHits: int
for y in startY ..< pathHeight: for y in startY ..< pathHeight:
computeCoverages( computeCoverage(
coverages, coverages,
hits, hits,
numHits, numHits,
@ -1591,11 +1603,11 @@ proc fillShapes(
var var
coverages = newSeq[uint8](bounds.w.int) coverages = newSeq[uint8](bounds.w.int)
hits = newSeq[(float32, int16)](4) hits = newSeq[(float32, int16)](partitioning.maxEntryCount)
numHits: int numHits: int
for y in startY ..< pathHeight: for y in startY ..< pathHeight:
computeCoverages( computeCoverage(
coverages, coverages,
hits, hits,
numHits, numHits,
@ -1937,10 +1949,7 @@ proc overlaps(
let let
scanline = line(vec2(0, test.y), vec2(1000, test.y)) scanline = line(vec2(0, test.y), vec2(1000, test.y))
segments = shapes.shapesToSegments() segments = shapes.shapesToSegments()
for i in 0 ..< segments.len: # For gc:arc for (segment, winding) in segments:
let
segment = segments[i][0]
winding = segments[i][1]
if segment.at.y <= scanline.a.y and segment.to.y >= scanline.a.y: if segment.at.y <= scanline.a.y and segment.to.y >= scanline.a.y:
var at: Vec2 var at: Vec2
if scanline.intersects(segment, at): if scanline.intersects(segment, at):
@ -1950,8 +1959,7 @@ proc overlaps(
sort(hits, 0, hits.high) sort(hits, 0, hits.high)
var count: int var count: int
for i in 0 ..< hits.len: # For gc:arc for (at, winding) in hits:
let (at, winding) = hits[i]
if at > test.x: if at > test.x:
return shouldFill(windingRule, count) return shouldFill(windingRule, count)
count += winding count += winding

View file

@ -5,8 +5,16 @@ let pathStr = "m57.611-8.591c-1.487,1.851-4.899,4.42-1.982,6.348,0.194,0.129,0.5
timeIt "parsePath": timeIt "parsePath":
keep parsePath(pathStr) keep parsePath(pathStr)
let image = newImage(500, 300) block:
image.fill(rgba(255, 255, 255, 255)) let path = parsePath("""
M 10,30
A 20,20 0,0,1 50,30
A 20,20 0,0,1 90,30
Q 90,60 50,90
Q 10,60 10,30 z
""")
timeIt "fillOverlaps":
doAssert path.fillOverlaps(vec2(1, 1)) == false
timeIt "roundedRect": timeIt "roundedRect":
const radius = 20 const radius = 20
@ -15,4 +23,5 @@ timeIt "roundedRect":
path.roundedRect(0.5, 0.5, 499, 299, radius, radius, radius, radius) path.roundedRect(0.5, 0.5, 499, 299, radius, radius, radius, radius)
# path.roundedRect(0, 0, 500, 300, radius, radius, radius, radius) # path.roundedRect(0, 0, 500, 300, radius, radius, radius, radius)
let image = newImage(500, 300)
image.fillPath(path, rgba(0, 0, 0, 255)) image.fillPath(path, rgba(0, 0, 0, 255))

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: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 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: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 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: 9.4 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 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: 32 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 23 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: 358 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: 30 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 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: 31 KiB

After

Width:  |  Height:  |  Size: 29 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: 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: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8 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: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8 KiB

After

Width:  |  Height:  |  Size: 8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

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: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1,002 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 893 B

After

Width:  |  Height:  |  Size: 836 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB