faster path filling, precompute m and b
This commit is contained in:
parent
87c7e9fee6
commit
85c4833e70
1 changed files with 62 additions and 53 deletions
|
@ -35,8 +35,13 @@ type
|
||||||
|
|
||||||
SomePath* = Path | string
|
SomePath* = Path | string
|
||||||
|
|
||||||
|
PartitionEntry = object
|
||||||
|
atY, toY: float32
|
||||||
|
m, b: float32
|
||||||
|
winding: int16
|
||||||
|
|
||||||
Partitioning = object
|
Partitioning = object
|
||||||
partitions: seq[seq[(Segment, int16)]]
|
partitions: seq[seq[PartitionEntry]]
|
||||||
startY, partitionHeight: uint32
|
startY, partitionHeight: uint32
|
||||||
|
|
||||||
const
|
const
|
||||||
|
@ -1076,8 +1081,19 @@ 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
|
||||||
|
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((segment, winding))
|
result.partitions[0].add(entry)
|
||||||
else:
|
else:
|
||||||
var
|
var
|
||||||
atPartition = max(0, segment.at.y - result.startY.float32).uint32
|
atPartition = max(0, segment.at.y - result.startY.float32).uint32
|
||||||
|
@ -1087,7 +1103,7 @@ proc partitionSegments(
|
||||||
atPartition = clamp(atPartition, 0, result.partitions.high.uint32)
|
atPartition = clamp(atPartition, 0, result.partitions.high.uint32)
|
||||||
toPartition = clamp(toPartition, 0, result.partitions.high.uint32)
|
toPartition = clamp(toPartition, 0, result.partitions.high.uint32)
|
||||||
for i in atPartition .. toPartition:
|
for i in atPartition .. toPartition:
|
||||||
result.partitions[i].add((segment, winding))
|
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.partitionHeight == 0 or partitioning.partitions.len == 1:
|
||||||
|
@ -1191,71 +1207,64 @@ proc computeCoverages(
|
||||||
zeroMem(coverages[0].addr, coverages.len)
|
zeroMem(coverages[0].addr, coverages.len)
|
||||||
|
|
||||||
# Do scanlines for this row
|
# Do scanlines for this row
|
||||||
let partitionIndex = partitioning.getIndexForY(y)
|
let
|
||||||
|
partitionIndex = partitioning.getIndexForY(y)
|
||||||
|
partitionEntryCount = partitioning.partitions[partitionIndex].len
|
||||||
var yLine = y.float32 + initialOffset - offset
|
var yLine = y.float32 + initialOffset - offset
|
||||||
for m in 0 ..< quality:
|
for m in 0 ..< quality:
|
||||||
yLine += offset
|
yLine += offset
|
||||||
numHits = 0
|
numHits = 0
|
||||||
for i in 0 ..< partitioning.partitions[partitionIndex].len: # Perf
|
for i in 0 ..< partitionEntryCount: # Perf
|
||||||
let
|
let entry = partitioning.partitions[partitionIndex][i]
|
||||||
segment = partitioning.partitions[partitionIndex][i][0]
|
if entry.atY <= yLine and entry.toY >= yLine:
|
||||||
winding = partitioning.partitions[partitionIndex][i][1]
|
let x =
|
||||||
if segment.at.y <= yLine and segment.to.y >= yLine:
|
if entry.m == 0:
|
||||||
let
|
entry.b
|
||||||
d = segment.at.x - segment.to.x
|
else:
|
||||||
x =
|
(yLine - entry.b) / entry.m
|
||||||
if d == 0:
|
|
||||||
segment.at.x
|
|
||||||
else:
|
|
||||||
let
|
|
||||||
m = (segment.at.y - segment.to.y) / d
|
|
||||||
b = segment.at.y - m * segment.at.x
|
|
||||||
(yLine - b) / m
|
|
||||||
|
|
||||||
if numHits == hits.len:
|
if numHits == hits.len:
|
||||||
hits.setLen(hits.len * 2)
|
hits.setLen(hits.len * 2)
|
||||||
hits[numHits] = (min(x, width), winding)
|
hits[numHits] = (min(x, width), entry.winding)
|
||||||
inc numHits
|
inc numHits
|
||||||
|
|
||||||
if numHits > 0:
|
if numHits > 0:
|
||||||
sort(hits, 0, numHits - 1)
|
sort(hits, 0, numHits - 1)
|
||||||
|
|
||||||
if not aa:
|
if aa:
|
||||||
continue
|
for (prevAt, at, count) in hits.walk(numHits, windingRule, y, width):
|
||||||
|
var fillStart = prevAt.int
|
||||||
|
|
||||||
for (prevAt, at, count) in hits.walk(numHits, windingRule, y, width):
|
let
|
||||||
var fillStart = prevAt.int
|
pixelCrossed = at.int - prevAt.int > 0
|
||||||
|
leftCover =
|
||||||
|
if pixelCrossed:
|
||||||
|
trunc(prevAt) + 1 - prevAt
|
||||||
|
else:
|
||||||
|
at - prevAt
|
||||||
|
if leftCover != 0:
|
||||||
|
inc fillStart
|
||||||
|
coverages[prevAt.int - startX] +=
|
||||||
|
(leftCover * sampleCoverage.float32).uint8
|
||||||
|
|
||||||
let
|
if pixelCrossed:
|
||||||
pixelCrossed = at.int - prevAt.int > 0
|
let rightCover = at - trunc(at)
|
||||||
leftCover =
|
if rightCover > 0:
|
||||||
if pixelCrossed:
|
coverages[at.int - startX] +=
|
||||||
trunc(prevAt) + 1 - prevAt
|
(rightCover * sampleCoverage.float32).uint8
|
||||||
else:
|
|
||||||
at - prevAt
|
|
||||||
if leftCover != 0:
|
|
||||||
inc fillStart
|
|
||||||
coverages[prevAt.int - startX] +=
|
|
||||||
(leftCover * sampleCoverage.float32).uint8
|
|
||||||
|
|
||||||
if pixelCrossed:
|
let fillLen = at.int - fillStart
|
||||||
let rightCover = at - trunc(at)
|
if fillLen > 0:
|
||||||
if rightCover > 0:
|
var i = fillStart
|
||||||
coverages[at.int - startX] +=
|
when defined(amd64) and not defined(pixieNoSimd):
|
||||||
(rightCover * sampleCoverage.float32).uint8
|
let vSampleCoverage = mm_set1_epi8(cast[int8](sampleCoverage))
|
||||||
|
for j in countup(i, fillStart + fillLen - 16, 16):
|
||||||
let fillLen = at.int - fillStart
|
var coverage = mm_loadu_si128(coverages[j - startX].addr)
|
||||||
if fillLen > 0:
|
coverage = mm_add_epi8(coverage, vSampleCoverage)
|
||||||
var i = fillStart
|
mm_storeu_si128(coverages[j - startX].addr, coverage)
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
i += 16
|
||||||
let vSampleCoverage = mm_set1_epi8(cast[int8](sampleCoverage))
|
for j in i ..< fillStart + fillLen:
|
||||||
for j in countup(i, fillStart + fillLen - 16, 16):
|
coverages[j - startX] += sampleCoverage
|
||||||
var coverage = mm_loadu_si128(coverages[j - startX].addr)
|
|
||||||
coverage = mm_add_epi8(coverage, vSampleCoverage)
|
|
||||||
mm_storeu_si128(coverages[j - startX].addr, coverage)
|
|
||||||
i += 16
|
|
||||||
for j in i ..< fillStart + fillLen:
|
|
||||||
coverages[j - startX] += sampleCoverage
|
|
||||||
|
|
||||||
proc clearUnsafe(target: Image | Mask, startX, startY, toX, toY: int) =
|
proc clearUnsafe(target: Image | Mask, startX, startY, toX, toY: int) =
|
||||||
## Clears data from [start, to).
|
## Clears data from [start, to).
|
||||||
|
|
Loading…
Reference in a new issue