commit
34a4056bb6
8 changed files with 288 additions and 219 deletions
|
@ -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)
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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)
|
||||
|
|
BIN
tests/paths/maskStrokeRectMask.png
Normal file
BIN
tests/paths/maskStrokeRectMask.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 221 B |
BIN
tests/paths/rectMaskStroke.png
Normal file
BIN
tests/paths/rectMaskStroke.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 400 B |
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue