fillHits refactor

This commit is contained in:
Ryan Oldenburg 2022-06-15 22:26:54 -05:00
parent cdc52f31ab
commit 134b7d246f

View file

@ -1499,6 +1499,25 @@ proc fillCoverage(
mask.clearUnsafe(0, y, startX, y)
mask.clearUnsafe(startX + coverages.len, y, mask.width, y)
template walkHits(
hits: seq[(int32, int16)],
numHits: int,
windingRule: WindingRule,
y, width: int,
inner: untyped
) =
var filledTo {.inject.}: int
for (prevAt, at, count) in hits.walk(numHits, windingRule, y, width):
let
fillStart {.inject.} = prevAt.integer
fillLen {.inject.} = at.integer - fillStart
if fillLen <= 0:
continue
filledTo = fillStart + fillLen
inner
proc fillHits(
image: Image,
rgbx: ColorRGBX,
@ -1508,58 +1527,58 @@ proc fillHits(
windingRule: WindingRule,
blendMode: BlendMode
) =
let blender = blendMode.blender()
var filledTo: int
for (prevAt, at, count) in hits.walk(numHits, windingRule, y, image.width):
let
fillStart = prevAt.integer
fillLen = at.integer - fillStart
if fillLen <= 0:
continue
filledTo = fillStart + fillLen
if blendMode == OverwriteBlend or (blendMode == NormalBlend and rgbx.a == 255):
fillUnsafe(image.data, rgbx, image.dataIndex(fillStart, y), fillLen)
continue
var x = fillStart
when defined(amd64) and allowSimd:
if blendMode.hasSimdBlender():
# When supported, SIMD blend as much as possible
template simdBlob(image: Image, x: var int, blendProc: untyped) =
when allowSimd:
when defined(amd64):
let colorVec = mm_set1_epi32(cast[int32](rgbx))
if blendMode == NormalBlend:
# For path filling, NormalBlend is almost always used.
# Inline SIMD is faster here.
for _ in 0 ..< fillLen div 4:
let
index = image.dataIndex(x, y)
backdrop = mm_loadu_si128(image.data[index].addr)
mm_storeu_si128(
image.data[index].addr,
blendNormalSimd(backdrop, colorVec)
)
x += 4
else:
let blenderSimd = blendMode.blenderSimd()
for _ in 0 ..< fillLen div 4:
let
index = image.dataIndex(x, y)
backdrop = mm_loadu_si128(image.data[index].addr)
mm_storeu_si128(
image.data[index].addr,
blenderSimd(backdrop, colorVec)
)
x += 4
for _ in 0 ..< fillLen div 4:
let
index = image.dataIndex(x, y)
backdrop = mm_loadu_si128(image.data[index].addr)
mm_storeu_si128(image.data[index].addr, blendProc(backdrop, colorVec))
x += 4
for x in x ..< fillStart + fillLen:
let backdrop = image.unsafe[x, y]
image.unsafe[x, y] = blender(backdrop, rgbx)
case blendMode:
of OverwriteBlend:
walkHits hits, numHits, windingRule, y, image.width:
fillUnsafe(image.data, rgbx, image.dataIndex(fillStart, y), fillLen)
of NormalBlend:
walkHits hits, numHits, windingRule, y, image.width:
if rgbx.a == 255:
fillUnsafe(image.data, rgbx, image.dataIndex(fillStart, y), fillLen)
else:
var x = fillStart
simdBlob(image, x, blendNormalSimd)
for x in x ..< fillStart + fillLen:
let backdrop = image.unsafe[x, y]
image.unsafe[x, y] = blendNormal(backdrop, rgbx)
of MaskBlend:
var prevFilledTo: int
walkHits hits, numHits, windingRule, y, image.width:
block: # Clear any gap between this fill and the previous fill
let gapBetweenHits = fillStart - prevFilledTo
if gapBetweenHits > 0:
fillUnsafe(image.data, rgbx(0, 0, 0, 0), image.dataIndex(prevFilledTo, y), gapBetweenHits)
prevFilledTo = filledTo
block: # Handle this fill
var x = fillStart
simdBlob(image, x, blendMaskSimd)
for x in x ..< fillStart + fillLen:
let backdrop = image.unsafe[x, y]
image.unsafe[x, y] = blendMask(backdrop, rgbx)
if blendMode == MaskBlend:
image.clearUnsafe(0, y, startX, y)
image.clearUnsafe(filledTo, y, image.width, y)
else:
let blender = blendMode.blender()
walkHits hits, numHits, windingRule, y, image.width:
for x in fillStart ..< fillStart + fillLen:
let backdrop = image.unsafe[x, y]
image.unsafe[x, y] = blender(backdrop, rgbx)
proc fillHits(
mask: Mask,
startX, y: int,
@ -1568,45 +1587,37 @@ proc fillHits(
windingRule: WindingRule,
blendMode: BlendMode
) =
let masker = blendMode.masker()
var filledTo: int
for (prevAt, at, count) in hits.walk(numHits, windingRule, y, mask.width):
let
fillStart = prevAt.integer
fillLen = at.integer - fillStart
if fillLen <= 0:
continue
filledTo = fillStart + fillLen
if blendMode in {NormalBlend, OverwriteBlend}:
case blendMode:
of NormalBlend, OverwriteBlend:
walkHits hits, numHits, windingRule, y, mask.width:
fillUnsafe(mask.data, 255, mask.dataIndex(fillStart, y), fillLen)
continue
var x = fillStart
when defined(amd64) and allowSimd:
if blendMode.hasSimdMasker():
let
maskerSimd = blendMode.maskerSimd()
valueVec = mm_set1_epi8(cast[int8](255))
for _ in 0 ..< fillLen div 16:
let
index = mask.dataIndex(x, y)
backdrop = mm_loadu_si128(mask.data[index].addr)
mm_storeu_si128(
mask.data[index].addr,
maskerSimd(backdrop, valueVec)
)
x += 16
of MaskBlend:
var prevFilledTo: int
walkHits hits, numHits, windingRule,y, mask.width:
let gapBetweenHits = fillStart - prevFilledTo
if gapBetweenHits > 0:
fillUnsafe(mask.data, 0, mask.dataIndex(prevFilledTo, y), gapBetweenHits)
prevFilledTo = filledTo
for x in x ..< fillStart + fillLen:
let backdrop = mask.unsafe[x, y]
mask.unsafe[x, y] = masker(backdrop, 255)
if blendMode == MaskBlend:
mask.clearUnsafe(0, y, startX, y)
mask.clearUnsafe(filledTo, y, mask.width, y)
of SubtractMaskBlend:
walkHits hits, numHits, windingRule, y, mask.width:
for x in fillStart ..< fillStart + fillLen:
let backdrop = mask.unsafe[x, y]
mask.unsafe[x, y] = maskBlendSubtract(backdrop, 255)
of ExcludeMaskBlend:
walkHits hits, numHits, windingRule, y, mask.width:
for x in fillStart ..< fillStart + fillLen:
let backdrop = mask.unsafe[x, y]
mask.unsafe[x, y] = maskBlendExclude(backdrop, 255)
else:
failUnsupportedBlendMode(blendMode)
proc fillShapes(
image: Image,
shapes: seq[Polygon],