Merge pull request #443 from guzba/master

path fillHits refactor
This commit is contained in:
Andre von Houck 2022-06-16 18:03:36 -07:00 committed by GitHub
commit 34a4056bb6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 288 additions and 219 deletions

View file

@ -140,7 +140,7 @@ proc SetSat(C: Color, s: float32): Color {.inline.} =
if satC > 0:
result = (C - min([C.r, C.g, C.b])) * s / satC
proc blendNormal*(backdrop, source: ColorRGBX): ColorRGBX =
proc blendNormal*(backdrop, source: ColorRGBX): ColorRGBX {.inline.} =
if backdrop.a == 0 or source.a == 255:
return source
if source.a == 0:
@ -152,7 +152,7 @@ proc blendNormal*(backdrop, source: ColorRGBX): ColorRGBX =
result.b = source.b + ((backdrop.b.uint32 * k) div 255).uint8
result.a = blendAlpha(backdrop.a, source.a)
proc blendDarken(backdrop, source: ColorRGBX): ColorRGBX =
proc blendDarken*(backdrop, source: ColorRGBX): ColorRGBX {.inline.} =
proc blend(
backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint8
): uint8 {.inline.} =
@ -166,7 +166,7 @@ proc blendDarken(backdrop, source: ColorRGBX): ColorRGBX =
result.b = blend(backdrop.b, backdrop.a, source.b, source.a)
result.a = blendAlpha(backdrop.a, source.a)
proc blendMultiply(backdrop, source: ColorRGBX): ColorRGBX =
proc blendMultiply*(backdrop, source: ColorRGBX): ColorRGBX {.inline.} =
proc blend(
backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint8
): uint8 {.inline.} =
@ -191,7 +191,7 @@ proc blendMultiply(backdrop, source: ColorRGBX): ColorRGBX =
# result = alphaFix(backdrop, source, result)
# result = result.toPremultipliedAlpha()
proc blendColorBurn(backdrop, source: ColorRGBX): ColorRGBX =
proc blendColorBurn*(backdrop, source: ColorRGBX): ColorRGBX =
let
backdrop = backdrop.rgba()
source = source.rgba()
@ -208,7 +208,7 @@ proc blendColorBurn(backdrop, source: ColorRGBX): ColorRGBX =
blended.b = blend(backdrop.b, source.b)
result = alphaFix(backdrop, source, blended).rgbx()
proc blendLighten(backdrop, source: ColorRGBX): ColorRGBX =
proc blendLighten*(backdrop, source: ColorRGBX): ColorRGBX {.inline.} =
proc blend(
backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint8
): uint8 {.inline.} =
@ -222,7 +222,7 @@ proc blendLighten(backdrop, source: ColorRGBX): ColorRGBX =
result.b = blend(backdrop.b, backdrop.a, source.b, source.a)
result.a = blendAlpha(backdrop.a, source.a)
proc blendScreen(backdrop, source: ColorRGBX): ColorRGBX =
proc blendScreen*(backdrop, source: ColorRGBX): ColorRGBX {.inline.} =
result.r = screen(backdrop.r, source.r)
result.g = screen(backdrop.g, source.g)
result.b = screen(backdrop.b, source.b)
@ -255,13 +255,13 @@ proc blendColorDodge(backdrop, source: ColorRGBX): ColorRGBX =
blended.b = blend(backdrop.b, source.b)
result = alphaFix(backdrop, source, blended).rgbx()
proc blendOverlay(backdrop, source: ColorRGBX): ColorRGBX =
proc blendOverlay*(backdrop, source: ColorRGBX): ColorRGBX =
result.r = hardLight(source.r, source.a, backdrop.r, backdrop.a)
result.g = hardLight(source.g, source.a, backdrop.g, backdrop.a)
result.b = hardLight(source.b, source.a, backdrop.b, backdrop.a)
result.a = blendAlpha(backdrop.a, source.a)
proc blendSoftLight(backdrop, source: ColorRGBX): ColorRGBX =
proc blendSoftLight*(backdrop, source: ColorRGBX): ColorRGBX =
# proc softLight(backdrop, source: int32): uint8 {.inline.} =
# ## Pegtop
# (
@ -335,7 +335,7 @@ proc blendSoftLight(backdrop, source: ColorRGBX): ColorRGBX =
result = rgba.rgbx()
proc blendHardLight(backdrop, source: ColorRGBX): ColorRGBX =
proc blendHardLight*(backdrop, source: ColorRGBX): ColorRGBX =
result.r = hardLight(backdrop.r, backdrop.a, source.r, source.a)
result.g = hardLight(backdrop.g, backdrop.a, source.g, source.a)
result.b = hardLight(backdrop.b, backdrop.a, source.b, source.a)
@ -394,44 +394,64 @@ proc blendSaturation(backdrop, source: ColorRGBX): ColorRGBX =
blended = SetLum(SetSat(backdrop, Sat(source)), Lum(backdrop))
result = alphaFix(backdrop, source, blended).rgba.rgbx()
proc blendMask*(backdrop, source: ColorRGBX): ColorRGBX =
proc blendMask*(backdrop, source: ColorRGBX): ColorRGBX {.inline.} =
let k = source.a.uint32
result.r = ((backdrop.r * k) div 255).uint8
result.g = ((backdrop.g * k) div 255).uint8
result.b = ((backdrop.b * k) div 255).uint8
result.a = ((backdrop.a * k) div 255).uint8
proc blendSubtractMask(backdrop, source: ColorRGBX): ColorRGBX =
proc blendSubtractMask*(backdrop, source: ColorRGBX): ColorRGBX {.inline.} =
let a = (backdrop.a.uint32 * (255 - source.a)) div 255
result.r = ((backdrop.r * a) div 255).uint8
result.g = ((backdrop.g * a) div 255).uint8
result.b = ((backdrop.b * a) div 255).uint8
result.a = a.uint8
proc blendExcludeMask(backdrop, source: ColorRGBX): ColorRGBX =
proc blendExcludeMask*(backdrop, source: ColorRGBX): ColorRGBX {.inline.} =
let a = max(backdrop.a, source.a).uint32 - min(backdrop.a, source.a)
result.r = ((source.r * a) div 255).uint8
result.g = ((source.g * a) div 255).uint8
result.b = ((source.b * a) div 255).uint8
result.a = a.uint8
proc blendOverwrite(backdrop, source: ColorRGBX): ColorRGBX =
proc normalBlender(backdrop, source: ColorRGBX): ColorRGBX =
blendNormal(backdrop, source)
proc darkenBlender(backdrop, source: ColorRGBX): ColorRGBX =
blendDarken(backdrop, source)
proc multiplyBlender(backdrop, source: ColorRGBX): ColorRGBX =
blendMultiply(backdrop, source)
proc lightenBlender(backdrop, source: ColorRGBX): ColorRGBX =
blendLighten(backdrop, source)
proc screenBlender(backdrop, source: ColorRGBX): ColorRGBX =
blendScreen(backdrop, source)
proc maskBlender(backdrop, source: ColorRGBX): ColorRGBX =
blendMask(backdrop, source)
proc overwriteBlender(backdrop, source: ColorRGBX): ColorRGBX =
source
# proc blendWhite(backdrop, source: ColorRGBX): ColorRGBX =
# ## For testing
# rgbx(255, 255, 255, 255)
proc subtractMaskBlender(backdrop, source: ColorRGBX): ColorRGBX =
blendSubtractMask(backdrop, source)
proc excludeMaskBlender(backdrop, source: ColorRGBX): ColorRGBX =
blendExcludeMask(backdrop, source)
proc blender*(blendMode: BlendMode): Blender {.raises: [].} =
## Returns a blend function for a given blend mode.
case blendMode:
of NormalBlend: blendNormal
of DarkenBlend: blendDarken
of MultiplyBlend: blendMultiply
of NormalBlend: normalBlender
of DarkenBlend: darkenBlender
of MultiplyBlend: multiplyBlender
# of BlendLinearBurn: blendLinearBurn
of ColorBurnBlend: blendColorBurn
of LightenBlend: blendLighten
of ScreenBlend: blendScreen
of LightenBlend: lightenBlender
of ScreenBlend: screenBlender
# of BlendLinearDodge: blendLinearDodge
of ColorDodgeBlend: blendColorDodge
of OverlayBlend: blendOverlay
@ -443,39 +463,50 @@ proc blender*(blendMode: BlendMode): Blender {.raises: [].} =
of SaturationBlend: blendSaturation
of ColorBlend: blendColor
of LuminosityBlend: blendLuminosity
of MaskBlend: blendMask
of OverwriteBlend: blendOverwrite
of SubtractMaskBlend: blendSubtractMask
of ExcludeMaskBlend: blendExcludeMask
of MaskBlend: maskBlender
of OverwriteBlend: overwriteBlender
of SubtractMaskBlend: subtractMaskBlender
of ExcludeMaskBlend: excludeMaskBlender
proc maskNormal(backdrop, source: uint8): uint8 =
## Blending masks
proc maskBlendNormal*(backdrop, source: uint8): uint8 {.inline.} =
## Normal blend masks
blendAlpha(backdrop, source)
proc maskMaskInline*(backdrop, source: uint8): uint8 {.inline.} =
## Masking masks
proc maskBlendMask*(backdrop, source: uint8): uint8 {.inline.} =
## Mask blend masks
((backdrop.uint32 * source) div 255).uint8
proc maskMask(backdrop, source: uint8): uint8 =
maskMaskInline(backdrop, source)
proc maskSubtract(backdrop, source: uint8): uint8 =
proc maskBlendSubtract*(backdrop, source: uint8): uint8 {.inline.} =
## Subtract blend masks
((backdrop.uint32 * (255 - source)) div 255).uint8
proc maskExclude(backdrop, source: uint8): uint8 =
proc maskBlendExclude*(backdrop, source: uint8): uint8 {.inline.} =
## Exclude blend masks
max(backdrop, source) - min(backdrop, source)
proc maskOverwrite(backdrop, source: uint8): uint8 =
proc maskBlendNormalMasker(backdrop, source: uint8): uint8 =
maskBlendNormal(backdrop, source)
proc maskBlendMaskMasker(backdrop, source: uint8): uint8 =
maskBlendMask(backdrop, source)
proc maskBlendSubtractMasker(backdrop, source: uint8): uint8 =
maskBlendSubtract(backdrop, source)
proc maskBlendExcludeMasker(backdrop, source: uint8): uint8 =
maskBlendExclude(backdrop, source)
proc maskBlendOverwriteMasker(backdrop, source: uint8): uint8 =
source
proc masker*(blendMode: BlendMode): Masker {.raises: [PixieError].} =
## Returns a blend masking function for a given blend masking mode.
case blendMode:
of NormalBlend: maskNormal
of MaskBlend: maskMask
of OverwriteBlend: maskOverwrite
of SubtractMaskBlend: maskSubtract
of ExcludeMaskBlend: maskExclude
of NormalBlend: maskBlendNormalMasker
of MaskBlend: maskBlendMaskMasker
of OverwriteBlend: maskBlendOverwriteMasker
of SubtractMaskBlend: maskBlendSubtractMasker
of ExcludeMaskBlend: maskBlendExcludeMasker
else:
raise newException(PixieError, "No masker for " & $blendMode)
@ -486,7 +517,7 @@ when defined(amd64) and allowSimd:
MaskerSimd* = proc(blackdrop, source: M128i): M128i {.gcsafe, raises: [].}
## Function signature returned by maskerSimd.
proc blendNormalInlineSimd*(backdrop, source: M128i): M128i {.inline.} =
proc blendNormalSimd*(backdrop, source: M128i): M128i {.inline.} =
let
alphaMask = mm_set1_epi32(cast[int32](0xff000000))
oddMask = mm_set1_epi16(cast[int16](0xff00))
@ -515,10 +546,7 @@ when defined(amd64) and allowSimd:
mm_or_si128(backdropEven, mm_slli_epi16(backdropOdd, 8))
)
proc blendNormalSimd(backdrop, source: M128i): M128i =
blendNormalInlineSimd(backdrop, source)
proc blendMaskInlineSimd*(backdrop, source: M128i): M128i {.inline.} =
proc blendMaskSimd*(backdrop, source: M128i): M128i {.inline.} =
let
alphaMask = mm_set1_epi32(cast[int32](0xff000000))
oddMask = mm_set1_epi16(cast[int16](0xff00))
@ -539,18 +567,21 @@ when defined(amd64) and allowSimd:
mm_or_si128(backdropEven, mm_slli_epi16(backdropOdd, 8))
proc blendMaskSimd(backdrop, source: M128i): M128i =
blendMaskInlineSimd(backdrop, source)
proc normalSimdBlender(backdrop, source: M128i): M128i =
blendNormalSimd(backdrop, source)
proc blendOverwriteSimd(backdrop, source: M128i): M128i =
proc maskSimdBlender(backdrop, source: M128i): M128i =
blendMaskSimd(backdrop, source)
proc overwriteSimdBlender(backdrop, source: M128i): M128i =
source
proc blenderSimd*(blendMode: BlendMode): BlenderSimd {.raises: [PixieError].} =
## Returns a blend function for a given blend mode with SIMD support.
case blendMode:
of NormalBlend: blendNormalSimd
of MaskBlend: blendMaskSimd
of OverwriteBlend: blendOverwriteSimd
of NormalBlend: normalSimdBlender
of MaskBlend: maskSimdBlender
of OverwriteBlend: overwriteSimdBlender
else:
raise newException(PixieError, "No SIMD blender for " & $blendMode)
@ -558,7 +589,7 @@ when defined(amd64) and allowSimd:
## Is there a blend function for a given blend mode with SIMD support?
blendMode in {NormalBlend, MaskBlend, OverwriteBlend}
proc maskNormalInlineSimd*(backdrop, source: M128i): M128i {.inline.} =
proc maskBlendNormalSimd*(backdrop, source: M128i): M128i {.inline.} =
## Blending masks
let
oddMask = mm_set1_epi16(cast[int16](0xff00))
@ -595,10 +626,7 @@ when defined(amd64) and allowSimd:
mm_or_si128(blendedEven, mm_slli_epi16(blendedOdd, 8))
proc maskNormalSimd(backdrop, source: M128i): M128i =
maskNormalInlineSimd(backdrop, source)
proc maskMaskInlineSimd*(backdrop, source: M128i): M128i =
proc maskBlendMaskSimd*(backdrop, source: M128i): M128i =
let
oddMask = mm_set1_epi16(cast[int16](0xff00))
div255 = mm_set1_epi16(cast[int16](0x8081))
@ -619,15 +647,18 @@ when defined(amd64) and allowSimd:
mm_or_si128(backdropEven, mm_slli_epi16(backdropOdd, 8))
proc maskMaskSimd(backdrop, source: M128i): M128i =
maskMaskInlineSimd(backdrop, source)
proc maskBlendNormalSimdMasker(backdrop, source: M128i): M128i =
maskBlendNormalSimd(backdrop, source)
proc maskBlendMaskSimdMasker(backdrop, source: M128i): M128i =
maskBlendMaskSimd(backdrop, source)
proc maskerSimd*(blendMode: BlendMode): MaskerSimd {.raises: [PixieError].} =
## Returns a blend masking function with SIMD support.
case blendMode:
of NormalBlend: maskNormalSimd
of MaskBlend: maskMaskSimd
of OverwriteBlend: blendOverwriteSimd
of NormalBlend: maskBlendNormalSimdMasker
of MaskBlend: maskBlendMaskSimdMasker
of OverwriteBlend: overwriteSimdBlender
else:
raise newException(PixieError, "No SIMD masker for " & $blendMode)

View file

@ -847,7 +847,7 @@ proc drawUber(
backdropVec = mm_loadu_si128(a.data[backdropIdx].addr)
mm_storeu_si128(
a.data[backdropIdx].addr,
blendNormalInlineSimd(backdropVec, sourceVec)
blendNormalSimd(backdropVec, sourceVec)
)
else: # b is a Mask
var values = mm_loadu_si128(b.data[b.dataIndex(sx, sy)].addr)
@ -865,7 +865,7 @@ proc drawUber(
backdropVec = mm_loadu_si128(a.data[backdropIdx].addr)
mm_storeu_si128(
a.data[backdropIdx].addr,
blendNormalInlineSimd(backdropVec, sourceVec)
blendNormalSimd(backdropVec, sourceVec)
)
# Shuffle 32 bits off for the next iteration
values = mm_srli_si128(values, 4)
@ -882,7 +882,7 @@ proc drawUber(
let sourceVec = mm_loadu_si128(b.data[b.dataIndex(sx, sy)].addr)
mm_storeu_si128(
a.data[a.dataIndex(x, y)].addr,
maskNormalInlineSimd(backdropVec, sourceVec)
maskBlendNormalSimd(backdropVec, sourceVec)
)
x += 16
sx += 16
@ -904,7 +904,7 @@ proc drawUber(
let backdropVec = mm_loadu_si128(a.data[a.dataIndex(x + q, y)].addr)
mm_storeu_si128(
a.data[a.dataIndex(x + q, y)].addr,
blendMaskInlineSimd(backdropVec, sourceVec)
blendMaskSimd(backdropVec, sourceVec)
)
else: # b is a Mask
var values = mm_loadu_si128(b.data[b.dataIndex(sx, sy)].addr)
@ -922,7 +922,7 @@ proc drawUber(
let backdropVec = mm_loadu_si128(a.data[a.dataIndex(x + q, y)].addr)
mm_storeu_si128(
a.data[a.dataIndex(x + q, y)].addr,
blendMaskInlineSimd(backdropVec, sourceVec)
blendMaskSimd(backdropVec, sourceVec)
)
# Shuffle 32 bits off for the next iteration
values = mm_srli_si128(values, 4)
@ -939,7 +939,7 @@ proc drawUber(
let sourceVec = mm_loadu_si128(b.data[b.dataIndex(sx, sy)].addr)
mm_storeu_si128(
a.data[a.dataIndex(x, y)].addr,
maskMaskInlineSimd(backdropVec, sourceVec)
maskBlendMaskSimd(backdropVec, sourceVec)
)
x += 16
sx += 16
@ -1067,7 +1067,7 @@ proc drawUber(
a.unsafe[x, y] = 0
elif source != 255:
let backdrop = a.unsafe[x, y]
a.unsafe[x, y] = maskMaskInline(backdrop, source)
a.unsafe[x, y] = maskBlendMask(backdrop, source)
srcPos += dx
else:
for x in x ..< xStop:

View file

@ -1,4 +1,4 @@
import chroma, system/memory, vmath
import chroma, common, system/memory, vmath
const allowSimd* = not defined(pixieNoSimd) and not defined(tcc)
@ -10,6 +10,12 @@ template currentExceptionAsPixieError*(): untyped =
let e = getCurrentException()
newException(PixieError, e.getStackTrace & e.msg, e)
template failUnsupportedBlendMode*(blendMode: BlendMode) =
raise newException(
PixieError,
"Blend mode " & $blendMode & " not supported here"
)
when defined(release):
{.push checks: off.}
@ -65,11 +71,17 @@ proc fillUnsafe*(
else:
var i = start
when defined(amd64) and allowSimd:
# Align to 16 bytes
while i < (start + len) and (cast[uint](data[i].addr) and 15) != 0:
data[i] = rgbx
inc i
# When supported, SIMD fill until we run out of room
let colorVec = mm_set1_epi32(cast[int32](rgbx))
for _ in 0 ..< len div 8:
mm_storeu_si128(data[i + 0].addr, colorVec)
mm_storeu_si128(data[i + 4].addr, colorVec)
let
colorVec = mm_set1_epi32(cast[int32](rgbx))
remaining = start + len - i
for _ in 0 ..< remaining div 8:
mm_store_si128(data[i + 0].addr, colorVec)
mm_store_si128(data[i + 4].addr, colorVec)
i += 8
else:
when sizeof(int) == 8:

View file

@ -1372,7 +1372,7 @@ proc fillCoverage(
let backdrop = mm_loadu_si128(image.data[index + i * 4].addr)
mm_storeu_si128(
image.data[index + i * 4].addr,
blendNormalInlineSimd(backdrop, colorVec)
blendNormalSimd(backdrop, colorVec)
)
else:
for i in 0 ..< 4:
@ -1415,7 +1415,7 @@ proc fillCoverage(
coverageVec = mm_srli_si128(coverageVec, 4)
if blendMode == NormalBlend:
useCoverage(blendNormalInlineSimd)
useCoverage(blendNormalSimd)
else:
useCoverage(blenderSimd)
@ -1499,6 +1499,22 @@ 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
) =
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
inner
proc fillHits(
image: Image,
rgbx: ColorRGBX,
@ -1508,58 +1524,64 @@ 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,
blendNormalInlineSimd(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 filledTo = startX
walkHits hits, numHits, windingRule, y, image.width:
block: # Clear any gap between this fill and the previous fill
let gapBetween = fillStart - filledTo
if gapBetween > 0:
fillUnsafe(
image.data,
rgbx(0, 0, 0, 0),
image.dataIndex(filledTo, y),
gapBetween
)
filledTo = fillStart + fillLen
block: # Handle this fill
if rgbx.a != 255:
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 +1590,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 filledTo = startX
walkHits hits, numHits, windingRule,y, mask.width:
let gapBetween = fillStart - filledTo
if gapBetween > 0:
fillUnsafe(mask.data, 0, mask.dataIndex(filledTo, y), gapBetween)
filledTo = fillStart + fillLen
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],

View file

@ -31,86 +31,70 @@ let roundedRect = newPath()
roundedRect.roundedRect(10.5, 10.5, 479, 279, radius, radius, radius, radius)
# roundedRect.roundedRect(10, 10, 480, 280, radius, radius, radius, radius)
timeIt "rect Image OverwriteBlend":
paint.blendMode = OverwriteBlend
block:
let image = newImage(width, height)
image.fillPath(rect, paint)
timeIt "rect Image NormalBlend":
paint.blendMode = NormalBlend
timeIt "rect Image OverwriteBlend":
paint.blendMode = OverwriteBlend
image.fillPath(rect, paint)
let image = newImage(width, height)
image.fillPath(rect, paint)
timeIt "rect Image NormalBlend":
paint.blendMode = NormalBlend
image.fillPath(rect, paint)
timeIt "rect Image MaskBlend":
paint.blendMode = MaskBlend
timeIt "rect Image MaskBlend":
paint.blendMode = MaskBlend
image.fill(rgbx(255, 255, 255, 255))
image.fillPath(rect, paint)
let image = newImage(width, height)
image.fill(rgbx(255, 255, 255, 255))
image.fillPath(rect, paint)
timeIt "roundedRect Image OverwriteBlend":
paint.blendMode = OverwriteBlend
image.fillPath(roundedRect, paint)
timeIt "roundedRect Image OverwriteBlend":
paint.blendMode = OverwriteBlend
timeIt "roundedRect Image NormalBlend":
paint.blendMode = NormalBlend
image.fillPath(roundedRect, paint)
let image = newImage(width, height)
image.fillPath(roundedRect, paint)
timeIt "roundedRect Image MaskBlend":
paint.blendMode = MaskBlend
image.fill(rgbx(255, 255, 255, 255))
image.fillPath(roundedRect, paint)
timeIt "roundedRect Image NormalBlend":
paint.blendMode = NormalBlend
let image = newImage(width, height)
image.fillPath(roundedRect, paint)
timeIt "roundedRect Image MaskBlend":
paint.blendMode = MaskBlend
let image = newImage(width, height)
image.fill(rgbx(255, 255, 255, 255))
image.fillPath(roundedRect, paint)
timeIt "rect Mask OverwriteBlend":
block:
let mask = newMask(width, height)
mask.fillPath(roundedRect, blendMode = OverwriteBlend)
timeIt "rect Mask NormalBlend":
let mask = newMask(width, height)
mask.fillPath(rect, blendMode = NormalBlend)
timeIt "rect Mask OverwriteBlend":
mask.fillPath(rect, blendMode = OverwriteBlend)
timeIt "rect Mask MaskBlend":
let mask = newMask(width, height)
mask.fill(255)
mask.fillPath(rect, blendMode = MaskBlend)
timeIt "rect Mask NormalBlend":
mask.fillPath(rect, blendMode = NormalBlend)
timeIt "rect Mask SubtractMaskBlend":
let mask = newMask(width, height)
mask.fill(255)
mask.fillPath(rect, blendMode = SubtractMaskBlend)
timeIt "rect Mask MaskBlend":
mask.fill(255)
mask.fillPath(rect, blendMode = MaskBlend)
timeIt "rect Mask ExcludeMaskBlend":
let mask = newMask(width, height)
mask.fill(255)
mask.fillPath(rect, blendMode = ExcludeMaskBlend)
timeIt "rect Mask SubtractMaskBlend":
mask.fill(255)
mask.fillPath(rect, blendMode = SubtractMaskBlend)
timeIt "roundedRect Mask OverwriteBlend":
let mask = newMask(width, height)
mask.fillPath(roundedRect, blendMode = OverwriteBlend)
timeIt "rect Mask ExcludeMaskBlend":
mask.fill(255)
mask.fillPath(rect, blendMode = ExcludeMaskBlend)
timeIt "roundedRect Mask NormalBlend":
let mask = newMask(width, height)
mask.fillPath(roundedRect, blendMode = NormalBlend)
timeIt "roundedRect Mask OverwriteBlend":
mask.fillPath(roundedRect, blendMode = OverwriteBlend)
timeIt "roundedRect Mask MaskBlend":
let mask = newMask(width, height)
mask.fill(255)
mask.fillPath(roundedRect, blendMode = MaskBlend)
timeIt "roundedRect Mask NormalBlend":
mask.fillPath(roundedRect, blendMode = NormalBlend)
timeIt "roundedRect Mask SubtractMaskBlend":
let mask = newMask(width, height)
mask.fill(255)
mask.fillPath(roundedRect, blendMode = SubtractMaskBlend)
timeIt "roundedRect Mask MaskBlend":
mask.fill(255)
mask.fillPath(roundedRect, blendMode = MaskBlend)
timeIt "roundedRect Mask ExcludeMaskBlend":
let mask = newMask(width, height)
mask.fill(255)
mask.fillPath(roundedRect, blendMode = ExcludeMaskBlend)
timeIt "roundedRect Mask SubtractMaskBlend":
mask.fill(255)
mask.fillPath(roundedRect, blendMode = SubtractMaskBlend)
timeIt "roundedRect Mask ExcludeMaskBlend":
mask.fill(255)
mask.fillPath(roundedRect, blendMode = ExcludeMaskBlend)

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

View file

@ -451,6 +451,24 @@ block:
)
image.writeFile("tests/paths/rectMaskAA.png")
block:
let image = newImage(100, 100)
image.fillPath(
"M 10 10 H 60 V 60 H 10 z",
rgbx(255, 0, 0, 255)
)
let paint = newPaint(SolidPaint)
paint.color = color(0, 1, 0, 1)
paint.blendMode = MaskBlend
image.strokePath(
"M 30 30 H 50 V 50 H 30 z",
paint,
strokeWidth = 10
)
image.writeFile("tests/paths/rectMaskStroke.png")
block:
let mask = newMask(100, 100)
mask.fillPath("M 10 10 H 60 V 60 H 10 z")
@ -478,6 +496,16 @@ block:
mask.fillPath("M 30.1 30.1 H 80.1 V 80.1 H 30.1 z", blendMode = MaskBlend)
writeFile("tests/paths/maskRectMaskAA.png", mask.encodePng())
block:
let mask = newMask(100, 100)
mask.fillPath("M 10 10 H 60 V 60 H 10 z")
mask.strokePath(
"M 30 30 H 50 V 50 H 30 z",
strokeWidth = 10,
blendMode = MaskBlend
)
writeFile("tests/paths/maskStrokeRectMask.png", mask.encodePng())
block:
var
surface = newImage(256, 256)