From 134b7d246f55fd6b09ce1896c88f211e7427b181 Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Wed, 15 Jun 2022 22:26:54 -0500 Subject: [PATCH] fillHits refactor --- src/pixie/paths.nim | 169 +++++++++++++++++++++++--------------------- 1 file changed, 90 insertions(+), 79 deletions(-) diff --git a/src/pixie/paths.nim b/src/pixie/paths.nim index cc1f164..d74a11c 100644 --- a/src/pixie/paths.nim +++ b/src/pixie/paths.nim @@ -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],