Merge pull request #451 from guzba/master
2 pixel-aligned vertical lines shortcut
This commit is contained in:
commit
461d713fc1
1 changed files with 85 additions and 17 deletions
|
@ -43,8 +43,8 @@ type
|
||||||
|
|
||||||
Partition = object
|
Partition = object
|
||||||
entries: seq[PartitionEntry]
|
entries: seq[PartitionEntry]
|
||||||
requiresAntiAliasing: bool
|
requiresAntiAliasing, twoNonintersectingSpanningSegments: bool
|
||||||
bottom: int
|
top, bottom: int
|
||||||
|
|
||||||
Fixed32 = int32 ## 24.8 fixed point
|
Fixed32 = int32 ## 24.8 fixed point
|
||||||
|
|
||||||
|
@ -1131,6 +1131,18 @@ proc partitionSegments(
|
||||||
startY = top.uint32
|
startY = top.uint32
|
||||||
partitionHeight = height.uint32 div numPartitions
|
partitionHeight = height.uint32 div numPartitions
|
||||||
|
|
||||||
|
# Set the bottom values for the partitions (y value where this partition ends)
|
||||||
|
result[0].top = top
|
||||||
|
result[0].bottom = top + partitionHeight.int
|
||||||
|
for i in 1 ..< result.len:
|
||||||
|
result[i].top = result[i - 1].bottom
|
||||||
|
result[i].bottom = result[i - 1].bottom + partitionHeight.int
|
||||||
|
|
||||||
|
# Ensure the final partition goes to the actual bottom
|
||||||
|
# This is needed since the final partition includes
|
||||||
|
# height - (height div numPartitions) * numPartitions
|
||||||
|
result[^1].bottom = top + height
|
||||||
|
|
||||||
var entries = newSeq[PartitionEntry](segments.len)
|
var entries = newSeq[PartitionEntry](segments.len)
|
||||||
for i, (segment, winding) in segments:
|
for i, (segment, winding) in segments:
|
||||||
entries[i] = initPartitionEntry(segment, winding)
|
entries[i] = initPartitionEntry(segment, winding)
|
||||||
|
@ -1166,18 +1178,33 @@ proc partitionSegments(
|
||||||
result[partitionIndex].entries[indexes[partitionIndex]] = entries[i]
|
result[partitionIndex].entries[indexes[partitionIndex]] = entries[i]
|
||||||
inc indexes[partitionIndex]
|
inc indexes[partitionIndex]
|
||||||
|
|
||||||
# Set the bottom values for the partitions (y value where this partition ends)
|
|
||||||
var partitionBottom = top + partitionHeight.int
|
|
||||||
for partition in result.mitems:
|
for partition in result.mitems:
|
||||||
partition.bottom = partitionBottom
|
partition.requiresAntiAliasing = requiresAntiAliasing(partition.entries)
|
||||||
partition.requiresAntiAliasing =
|
if partition.entries.len == 2:
|
||||||
requiresAntiAliasing(partition.entries)
|
# Clip the entries to the parition bounds
|
||||||
partitionBottom += partitionHeight.int
|
let
|
||||||
|
top = partition.top.float32
|
||||||
|
bottom = partition.bottom.float32
|
||||||
|
topLine = line(vec2(0, top), vec2(1000, top))
|
||||||
|
bottomLine = line(vec2(0, bottom), vec2(1000, bottom))
|
||||||
|
for entry in partition.entries.mitems:
|
||||||
|
if entry.segment.at.y <= top and entry.segment.to.y >= bottom:
|
||||||
|
var at: Vec2
|
||||||
|
discard intersects(entry.segment, topLine, at)
|
||||||
|
entry.segment.at = at
|
||||||
|
discard intersects(entry.segment, bottomLine, at)
|
||||||
|
entry.segment.to = at
|
||||||
|
|
||||||
# Ensure the final partition goes to the actual bottom
|
let
|
||||||
# This is needed since the final partition includes
|
entry0 = partition.entries[0].segment
|
||||||
# height - (height div numPartitions) * numPartitions
|
entry1 = partition.entries[1].segment
|
||||||
result[^1].bottom = top + height
|
var at: Vec2
|
||||||
|
if not intersects(entry0, entry1, at):
|
||||||
|
# These two segments do not intersect, enable shortcut
|
||||||
|
partition.twoNonintersectingSpanningSegments = true
|
||||||
|
# Ensure entry[0] is on the left
|
||||||
|
if entry1.at.x < entry0.at.x:
|
||||||
|
swap partition.entries[1], partition.entries[0]
|
||||||
|
|
||||||
proc maxEntryCount(partitions: var seq[Partition]): int =
|
proc maxEntryCount(partitions: var seq[Partition]): int =
|
||||||
for i in 0 ..< partitions.len:
|
for i in 0 ..< partitions.len:
|
||||||
|
@ -1273,12 +1300,9 @@ proc computeCoverage(
|
||||||
width: int,
|
width: int,
|
||||||
y, startX: int,
|
y, startX: int,
|
||||||
partitions: var seq[Partition],
|
partitions: var seq[Partition],
|
||||||
partitionIndex: var int,
|
partitionIndex: int,
|
||||||
windingRule: WindingRule
|
windingRule: WindingRule
|
||||||
) {.inline.} =
|
) {.inline.} =
|
||||||
if y >= partitions[partitionIndex].bottom:
|
|
||||||
inc partitionIndex
|
|
||||||
|
|
||||||
aa = partitions[partitionIndex].requiresAntiAliasing
|
aa = partitions[partitionIndex].requiresAntiAliasing
|
||||||
|
|
||||||
let
|
let
|
||||||
|
@ -1823,7 +1847,45 @@ proc fillShapes(
|
||||||
numHits: int
|
numHits: int
|
||||||
aa: bool
|
aa: bool
|
||||||
|
|
||||||
for y in startY ..< pathHeight:
|
var y = startY
|
||||||
|
while y < pathHeight:
|
||||||
|
if y >= partitions[partitionIndex].bottom:
|
||||||
|
inc partitionIndex
|
||||||
|
|
||||||
|
let
|
||||||
|
partitionTop = partitions[partitionIndex].top
|
||||||
|
partitionBottom = partitions[partitionIndex].bottom
|
||||||
|
partitionHeight = partitionBottom - partitionTop
|
||||||
|
if partitionHeight == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if partitions[partitionIndex].twoNonintersectingSpanningSegments:
|
||||||
|
if partitions[partitionIndex].requiresAntiAliasing:
|
||||||
|
discard
|
||||||
|
else: # No AA required, must be 2 vertical pixel-aligned lines
|
||||||
|
let
|
||||||
|
left = partitions[partitionIndex].entries[0].segment.at.x.int
|
||||||
|
right = partitions[partitionIndex].entries[1].segment.at.x.int
|
||||||
|
minX = left.clamp(0, image.width)
|
||||||
|
maxX = right.clamp(0, image.width)
|
||||||
|
skipBlending =
|
||||||
|
blendMode == OverwriteBlend or
|
||||||
|
(blendMode == NormalBlend and rgbx.a == 255)
|
||||||
|
if skipBlending and minX == 0 and maxX == image.width:
|
||||||
|
# We can be greedy, just do one big mult-row fill
|
||||||
|
let
|
||||||
|
start = image.dataIndex(0, y)
|
||||||
|
len = image.dataIndex(0, y + partitionHeight) - start
|
||||||
|
fillUnsafe(image.data, rgbx, start, len)
|
||||||
|
else:
|
||||||
|
for r in 0 ..< partitionHeight:
|
||||||
|
hits[0] = (cast[Fixed32](minX * 256), 1.int16)
|
||||||
|
hits[1] = (cast[Fixed32](maxX * 256), -1.int16)
|
||||||
|
image.fillHits(rgbx, 0, y + r, hits, 2, NonZero, blendMode)
|
||||||
|
|
||||||
|
y += partitionHeight
|
||||||
|
continue
|
||||||
|
|
||||||
computeCoverage(
|
computeCoverage(
|
||||||
cast[ptr UncheckedArray[uint8]](coverages[0].addr),
|
cast[ptr UncheckedArray[uint8]](coverages[0].addr),
|
||||||
hits,
|
hits,
|
||||||
|
@ -1836,6 +1898,7 @@ proc fillShapes(
|
||||||
partitionIndex,
|
partitionIndex,
|
||||||
windingRule
|
windingRule
|
||||||
)
|
)
|
||||||
|
|
||||||
if aa:
|
if aa:
|
||||||
image.fillCoverage(
|
image.fillCoverage(
|
||||||
rgbx,
|
rgbx,
|
||||||
|
@ -1856,6 +1919,8 @@ proc fillShapes(
|
||||||
blendMode
|
blendMode
|
||||||
)
|
)
|
||||||
|
|
||||||
|
inc y
|
||||||
|
|
||||||
if blendMode == MaskBlend:
|
if blendMode == MaskBlend:
|
||||||
image.clearUnsafe(0, 0, 0, startY)
|
image.clearUnsafe(0, 0, 0, startY)
|
||||||
image.clearUnsafe(0, pathHeight, 0, image.height)
|
image.clearUnsafe(0, pathHeight, 0, image.height)
|
||||||
|
@ -1895,6 +1960,9 @@ proc fillShapes(
|
||||||
aa: bool
|
aa: bool
|
||||||
|
|
||||||
for y in startY ..< pathHeight:
|
for y in startY ..< pathHeight:
|
||||||
|
if y >= partitions[partitionIndex].bottom:
|
||||||
|
inc partitionIndex
|
||||||
|
|
||||||
computeCoverage(
|
computeCoverage(
|
||||||
cast[ptr UncheckedArray[uint8]](coverages[0].addr),
|
cast[ptr UncheckedArray[uint8]](coverages[0].addr),
|
||||||
hits,
|
hits,
|
||||||
|
|
Loading…
Reference in a new issue