commit
576ecfb918
6 changed files with 269 additions and 145 deletions
|
@ -238,7 +238,7 @@ proc blendScreen*(backdrop, source: ColorRGBX): ColorRGBX {.inline.} =
|
||||||
# result = alphaFix(backdrop, source, result)
|
# result = alphaFix(backdrop, source, result)
|
||||||
# result = result.toPremultipliedAlpha()
|
# result = result.toPremultipliedAlpha()
|
||||||
|
|
||||||
proc blendColorDodge(backdrop, source: ColorRGBX): ColorRGBX =
|
proc blendColorDodge*(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
let
|
let
|
||||||
backdrop = backdrop.rgba()
|
backdrop = backdrop.rgba()
|
||||||
source = source.rgba()
|
source = source.rgba()
|
||||||
|
@ -341,7 +341,7 @@ proc blendHardLight*(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
result.b = hardLight(backdrop.b, backdrop.a, source.b, source.a)
|
result.b = hardLight(backdrop.b, backdrop.a, source.b, source.a)
|
||||||
result.a = blendAlpha(backdrop.a, source.a)
|
result.a = blendAlpha(backdrop.a, source.a)
|
||||||
|
|
||||||
proc blendDifference(backdrop, source: ColorRGBX): ColorRGBX =
|
proc blendDifference*(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
proc blend(
|
proc blend(
|
||||||
backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint8
|
backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint8
|
||||||
): uint8 {.inline.} =
|
): uint8 {.inline.} =
|
||||||
|
@ -357,7 +357,7 @@ proc blendDifference(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
result.b = blend(backdrop.b, backdrop.a, source.b, source.a)
|
result.b = blend(backdrop.b, backdrop.a, source.b, source.a)
|
||||||
result.a = blendAlpha(backdrop.a, source.a)
|
result.a = blendAlpha(backdrop.a, source.a)
|
||||||
|
|
||||||
proc blendExclusion(backdrop, source: ColorRGBX): ColorRGBX =
|
proc blendExclusion*(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
proc blend(backdrop, source: uint32): uint8 {.inline.} =
|
proc blend(backdrop, source: uint32): uint8 {.inline.} =
|
||||||
let v = (backdrop + source).int32 - ((2 * backdrop * source) div 255).int32
|
let v = (backdrop + source).int32 - ((2 * backdrop * source) div 255).int32
|
||||||
max(0, v).uint8
|
max(0, v).uint8
|
||||||
|
@ -366,28 +366,28 @@ proc blendExclusion(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
result.b = blend(backdrop.b.uint32, source.b.uint32)
|
result.b = blend(backdrop.b.uint32, source.b.uint32)
|
||||||
result.a = blendAlpha(backdrop.a, source.a)
|
result.a = blendAlpha(backdrop.a, source.a)
|
||||||
|
|
||||||
proc blendColor(backdrop, source: ColorRGBX): ColorRGBX =
|
proc blendColor*(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
let
|
let
|
||||||
backdrop = backdrop.rgba().color
|
backdrop = backdrop.rgba().color
|
||||||
source = source.rgba().color
|
source = source.rgba().color
|
||||||
blended = SetLum(source, Lum(backdrop))
|
blended = SetLum(source, Lum(backdrop))
|
||||||
result = alphaFix(backdrop, source, blended).rgba.rgbx()
|
result = alphaFix(backdrop, source, blended).rgba.rgbx()
|
||||||
|
|
||||||
proc blendLuminosity(backdrop, source: ColorRGBX): ColorRGBX =
|
proc blendLuminosity*(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
let
|
let
|
||||||
backdrop = backdrop.rgba().color
|
backdrop = backdrop.rgba().color
|
||||||
source = source.rgba().color
|
source = source.rgba().color
|
||||||
blended = SetLum(backdrop, Lum(source))
|
blended = SetLum(backdrop, Lum(source))
|
||||||
result = alphaFix(backdrop, source, blended).rgba.rgbx()
|
result = alphaFix(backdrop, source, blended).rgba.rgbx()
|
||||||
|
|
||||||
proc blendHue(backdrop, source: ColorRGBX): ColorRGBX =
|
proc blendHue*(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
let
|
let
|
||||||
backdrop = backdrop.rgba().color
|
backdrop = backdrop.rgba().color
|
||||||
source = source.rgba().color
|
source = source.rgba().color
|
||||||
blended = SetLum(SetSat(source, Sat(backdrop)), Lum(backdrop))
|
blended = SetLum(SetSat(source, Sat(backdrop)), Lum(backdrop))
|
||||||
result = alphaFix(backdrop, source, blended).rgba.rgbx()
|
result = alphaFix(backdrop, source, blended).rgba.rgbx()
|
||||||
|
|
||||||
proc blendSaturation(backdrop, source: ColorRGBX): ColorRGBX =
|
proc blendSaturation*(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
let
|
let
|
||||||
backdrop = backdrop.rgba().color
|
backdrop = backdrop.rgba().color
|
||||||
source = source.rgba().color
|
source = source.rgba().color
|
||||||
|
|
|
@ -550,6 +550,7 @@ proc newImage*(svg: Svg): Image {.raises: [PixieError].} =
|
||||||
result = newImage(svg.width, svg.height)
|
result = newImage(svg.width, svg.height)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
var blendMode = OverwriteBlend # Start as overwrite
|
||||||
for (path, props) in svg.elements:
|
for (path, props) in svg.elements:
|
||||||
if props.display and props.opacity > 0:
|
if props.display and props.opacity > 0:
|
||||||
if props.fill != "none":
|
if props.fill != "none":
|
||||||
|
@ -573,9 +574,12 @@ proc newImage*(svg: Svg): Image {.raises: [PixieError].} =
|
||||||
paint = parseHtmlColor(props.fill).rgbx
|
paint = parseHtmlColor(props.fill).rgbx
|
||||||
|
|
||||||
paint.opacity = props.fillOpacity * props.opacity
|
paint.opacity = props.fillOpacity * props.opacity
|
||||||
|
paint.blendMode = blendMode
|
||||||
|
|
||||||
result.fillPath(path, paint, props.transform, props.fillRule)
|
result.fillPath(path, paint, props.transform, props.fillRule)
|
||||||
|
|
||||||
|
blendMode = NormalBlend # Switch to normal when compositing multiple paths
|
||||||
|
|
||||||
if props.stroke != rgbx(0, 0, 0, 0) and props.strokeWidth > 0:
|
if props.stroke != rgbx(0, 0, 0, 0) and props.strokeWidth > 0:
|
||||||
let paint = newPaint(props.stroke)
|
let paint = newPaint(props.stroke)
|
||||||
paint.color.a *= (props.opacity * props.strokeOpacity)
|
paint.color.a *= (props.opacity * props.strokeOpacity)
|
||||||
|
|
|
@ -1337,117 +1337,164 @@ proc fillCoverage(
|
||||||
blendMode: BlendMode
|
blendMode: BlendMode
|
||||||
) =
|
) =
|
||||||
var x = startX
|
var x = startX
|
||||||
when defined(amd64) and allowSimd:
|
|
||||||
if blendMode.hasSimdBlender():
|
when allowSimd:
|
||||||
# When supported, SIMD blend as much as possible
|
when defined(amd64):
|
||||||
let
|
iterator simd(
|
||||||
blenderSimd = blendMode.blenderSimd()
|
coverages: seq[uint8], x: var int, startX: int
|
||||||
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
): (M128i, bool, bool) =
|
||||||
div255 = mm_set1_epi16(cast[int16](0x8081))
|
|
||||||
vec255 = mm_set1_epi32(cast[int32](uint32.high))
|
|
||||||
vecZero = mm_setzero_si128()
|
|
||||||
colorVec = mm_set1_epi32(cast[int32](rgbx))
|
|
||||||
for _ in 0 ..< coverages.len div 16:
|
for _ in 0 ..< coverages.len div 16:
|
||||||
let
|
let
|
||||||
index = image.dataIndex(x, y)
|
|
||||||
coverageVec = mm_loadu_si128(coverages[x - startX].unsafeAddr)
|
coverageVec = mm_loadu_si128(coverages[x - startX].unsafeAddr)
|
||||||
|
eqZero = mm_cmpeq_epi8(coverageVec, mm_setzero_si128())
|
||||||
|
eq255 = mm_cmpeq_epi8(coverageVec, mm_set1_epi8(cast[int8](255)))
|
||||||
|
allZeroes = mm_movemask_epi8(eqZero) == 0xffff
|
||||||
|
all255 = mm_movemask_epi8(eq255) == 0xffff
|
||||||
|
yield (coverageVec, allZeroes, all255)
|
||||||
|
x += 16
|
||||||
|
|
||||||
|
proc source(colorVec, coverageVec: M128i): M128i {.inline.} =
|
||||||
|
let
|
||||||
|
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
||||||
|
div255 = mm_set1_epi16(cast[int16](0x8081))
|
||||||
|
|
||||||
if mm_movemask_epi8(mm_cmpeq_epi16(coverageVec, vecZero)) != 0xffff:
|
|
||||||
# If the coverages are not all zero
|
|
||||||
if mm_movemask_epi8(mm_cmpeq_epi32(coverageVec, vec255)) == 0xffff:
|
|
||||||
# If the coverages are all 255
|
|
||||||
if blendMode == OverwriteBlend:
|
|
||||||
for i in 0 ..< 4:
|
|
||||||
mm_storeu_si128(image.data[index + i * 4].addr, colorVec)
|
|
||||||
elif blendMode == NormalBlend:
|
|
||||||
if rgbx.a == 255:
|
|
||||||
for i in 0 ..< 4:
|
|
||||||
mm_storeu_si128(image.data[index + i * 4].addr, colorVec)
|
|
||||||
else:
|
|
||||||
for i in 0 ..< 4:
|
|
||||||
let backdrop = mm_loadu_si128(image.data[index + i * 4].addr)
|
|
||||||
mm_storeu_si128(
|
|
||||||
image.data[index + i * 4].addr,
|
|
||||||
blendNormalSimd(backdrop, colorVec)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
for i in 0 ..< 4:
|
|
||||||
let backdrop = mm_loadu_si128(image.data[index + i * 4].addr)
|
|
||||||
mm_storeu_si128(
|
|
||||||
image.data[index + i * 4].addr,
|
|
||||||
blenderSimd(backdrop, colorVec)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# Coverages are not all 255
|
|
||||||
template useCoverage(blendProc: untyped) =
|
|
||||||
var coverageVec = coverageVec
|
|
||||||
for i in 0 ..< 4:
|
|
||||||
var unpacked = unpackAlphaValues(coverageVec)
|
var unpacked = unpackAlphaValues(coverageVec)
|
||||||
# Shift the coverages from `a` to `g` and `a` for multiplying
|
|
||||||
unpacked = mm_or_si128(unpacked, mm_srli_epi32(unpacked, 16))
|
unpacked = mm_or_si128(unpacked, mm_srli_epi32(unpacked, 16))
|
||||||
|
|
||||||
var
|
var
|
||||||
source = colorVec
|
sourceEven = mm_slli_epi16(colorVec, 8)
|
||||||
sourceEven = mm_slli_epi16(source, 8)
|
sourceOdd = mm_and_si128(colorVec, oddMask)
|
||||||
sourceOdd = mm_and_si128(source, oddMask)
|
|
||||||
|
|
||||||
sourceEven = mm_mulhi_epu16(sourceEven, unpacked)
|
sourceEven = mm_mulhi_epu16(sourceEven, unpacked)
|
||||||
sourceOdd = mm_mulhi_epu16(sourceOdd, unpacked)
|
sourceOdd = mm_mulhi_epu16(sourceOdd, unpacked)
|
||||||
|
|
||||||
sourceEven = mm_srli_epi16(mm_mulhi_epu16(sourceEven, div255), 7)
|
sourceEven = mm_srli_epi16(mm_mulhi_epu16(sourceEven, div255), 7)
|
||||||
sourceOdd = mm_srli_epi16(mm_mulhi_epu16(sourceOdd, div255), 7)
|
sourceOdd = mm_srli_epi16(mm_mulhi_epu16(sourceOdd, div255), 7)
|
||||||
|
result = mm_or_si128(sourceEven, mm_slli_epi16(sourceOdd, 8))
|
||||||
|
|
||||||
source = mm_or_si128(sourceEven, mm_slli_epi16(sourceOdd, 8))
|
let colorVec = mm_set1_epi32(cast[int32](rgbx))
|
||||||
|
|
||||||
if blendMode == OverwriteBlend:
|
proc source(rgbx: ColorRGBX, coverage: uint8): ColorRGBX {.inline.} =
|
||||||
mm_storeu_si128(image.data[index + i * 4].addr, source)
|
if coverage > 0:
|
||||||
|
if coverage == 255:
|
||||||
|
result = rgbx
|
||||||
else:
|
else:
|
||||||
let backdrop = mm_loadu_si128(image.data[index + i * 4].addr)
|
result = rgbx(
|
||||||
mm_storeu_si128(
|
((rgbx.r.uint32 * coverage) div 255).uint8,
|
||||||
image.data[index + i * 4].addr,
|
((rgbx.g.uint32 * coverage) div 255).uint8,
|
||||||
blendProc(backdrop, source)
|
((rgbx.b.uint32 * coverage) div 255).uint8,
|
||||||
|
((rgbx.a.uint32 * coverage) div 255).uint8
|
||||||
)
|
)
|
||||||
|
|
||||||
|
case blendMode:
|
||||||
|
of OverwriteBlend:
|
||||||
|
when allowSimd:
|
||||||
|
when defined(amd64):
|
||||||
|
for (coverageVec, allZeroes, all255) in simd(coverages, x, startX):
|
||||||
|
if not allZeroes:
|
||||||
|
if all255:
|
||||||
|
for i in 0 ..< 4:
|
||||||
|
mm_storeu_si128(image.unsafe[x + i * 4, y].addr, colorVec)
|
||||||
|
else:
|
||||||
|
var coverageVec = coverageVec
|
||||||
|
for i in 0 ..< 4:
|
||||||
|
let source = source(colorVec, coverageVec)
|
||||||
|
mm_storeu_si128(image.unsafe[x + i * 4, y].addr, source)
|
||||||
coverageVec = mm_srli_si128(coverageVec, 4)
|
coverageVec = mm_srli_si128(coverageVec, 4)
|
||||||
|
|
||||||
if blendMode == NormalBlend:
|
for x in x ..< startX + coverages.len:
|
||||||
useCoverage(blendNormalSimd)
|
let coverage = coverages[x - startX]
|
||||||
else:
|
if coverage != 0:
|
||||||
useCoverage(blenderSimd)
|
image.unsafe[x, y] = source(rgbx, coverage)
|
||||||
|
|
||||||
elif blendMode == MaskBlend:
|
of NormalBlend:
|
||||||
|
when allowSimd:
|
||||||
|
when defined(amd64):
|
||||||
|
for (coverageVec, allZeroes, all255) in simd(coverages, x, startX):
|
||||||
|
if not allZeroes:
|
||||||
|
if all255 and rgbx.a == 255:
|
||||||
for i in 0 ..< 4:
|
for i in 0 ..< 4:
|
||||||
mm_storeu_si128(image.data[index + i * 4].addr, vecZero)
|
mm_storeu_si128(image.unsafe[x + i * 4, y].addr, colorVec)
|
||||||
|
else:
|
||||||
|
var coverageVec = coverageVec
|
||||||
|
for i in 0 ..< 4:
|
||||||
|
let
|
||||||
|
backdrop = mm_loadu_si128(image.unsafe[x + i * 4, y].addr)
|
||||||
|
source = source(colorVec, coverageVec)
|
||||||
|
mm_storeu_si128(
|
||||||
|
image.unsafe[x + i * 4, y].addr,
|
||||||
|
blendNormalSimd(backdrop, source)
|
||||||
|
)
|
||||||
|
coverageVec = mm_srli_si128(coverageVec, 4)
|
||||||
|
|
||||||
x += 16
|
for x in x ..< startX + coverages.len:
|
||||||
|
let coverage = coverages[x - startX]
|
||||||
|
if coverage == 255 and rgbx.a == 255:
|
||||||
|
image.unsafe[x, y] = rgbx
|
||||||
|
elif coverage == 0:
|
||||||
|
discard
|
||||||
|
else:
|
||||||
|
let backdrop = image.unsafe[x, y]
|
||||||
|
image.unsafe[x, y] = blendNormal(backdrop, source(rgbx, coverage))
|
||||||
|
|
||||||
|
of MaskBlend:
|
||||||
|
{.linearScanEnd.}
|
||||||
|
|
||||||
|
when allowSimd:
|
||||||
|
when defined(amd64):
|
||||||
|
for (coverageVec, allZeroes, all255) in simd(coverages, x, startX):
|
||||||
|
if not allZeroes:
|
||||||
|
if all255:
|
||||||
|
discard
|
||||||
|
else:
|
||||||
|
var coverageVec = coverageVec
|
||||||
|
for i in 0 ..< 4:
|
||||||
|
let
|
||||||
|
backdrop = mm_loadu_si128(image.unsafe[x + i * 4, y].addr)
|
||||||
|
source = source(colorVec, coverageVec)
|
||||||
|
mm_storeu_si128(
|
||||||
|
image.unsafe[x + i * 4, y].addr,
|
||||||
|
blendMaskSimd(backdrop, source)
|
||||||
|
)
|
||||||
|
coverageVec = mm_srli_si128(coverageVec, 4)
|
||||||
|
else:
|
||||||
|
for i in 0 ..< 4:
|
||||||
|
mm_storeu_si128(image.unsafe[x + i * 4, y].addr, mm_setzero_si128())
|
||||||
|
|
||||||
|
for x in x ..< startX + coverages.len:
|
||||||
|
let coverage = coverages[x - startX]
|
||||||
|
if coverage == 0:
|
||||||
|
image.unsafe[x, y] = rgbx(0, 0, 0, 0)
|
||||||
|
elif coverage == 255:
|
||||||
|
discard
|
||||||
|
else:
|
||||||
|
let backdrop = image.unsafe[x, y]
|
||||||
|
image.unsafe[x, y] = blendMask(backdrop, source(rgbx, coverage))
|
||||||
|
|
||||||
|
image.clearUnsafe(0, y, startX, y)
|
||||||
|
image.clearUnsafe(startX + coverages.len, y, image.width, y)
|
||||||
|
|
||||||
|
of SubtractMaskBlend:
|
||||||
|
for x in x ..< startX + coverages.len:
|
||||||
|
let coverage = coverages[x - startX]
|
||||||
|
if coverage == 255 and rgbx.a == 255:
|
||||||
|
image.unsafe[x, y] = rgbx(0, 0, 0, 0)
|
||||||
|
elif coverage != 0:
|
||||||
|
let backdrop = image.unsafe[x, y]
|
||||||
|
image.unsafe[x, y] = blendSubtractMask(backdrop, source(rgbx, coverage))
|
||||||
|
|
||||||
|
of ExcludeMaskBlend:
|
||||||
|
for x in x ..< startX + coverages.len:
|
||||||
|
let
|
||||||
|
coverage = coverages[x - startX]
|
||||||
|
backdrop = image.unsafe[x, y]
|
||||||
|
image.unsafe[x, y] = blendExcludeMask(backdrop, source(rgbx, coverage))
|
||||||
|
|
||||||
|
else:
|
||||||
let blender = blendMode.blender()
|
let blender = blendMode.blender()
|
||||||
for x in x ..< startX + coverages.len:
|
for x in x ..< startX + coverages.len:
|
||||||
let coverage = coverages[x - startX]
|
let coverage = coverages[x - startX]
|
||||||
if coverage != 0 or blendMode == ExcludeMaskBlend:
|
if coverage != 0:
|
||||||
if blendMode == NormalBlend and coverage == 255 and rgbx.a == 255:
|
|
||||||
# Skip blending
|
|
||||||
image.unsafe[x, y] = rgbx
|
|
||||||
continue
|
|
||||||
|
|
||||||
var source = rgbx
|
|
||||||
if coverage != 255:
|
|
||||||
source.r = ((source.r.uint32 * coverage) div 255).uint8
|
|
||||||
source.g = ((source.g.uint32 * coverage) div 255).uint8
|
|
||||||
source.b = ((source.b.uint32 * coverage) div 255).uint8
|
|
||||||
source.a = ((source.a.uint32 * coverage) div 255).uint8
|
|
||||||
|
|
||||||
if blendMode == OverwriteBlend:
|
|
||||||
image.unsafe[x, y] = source
|
|
||||||
else:
|
|
||||||
let backdrop = image.unsafe[x, y]
|
let backdrop = image.unsafe[x, y]
|
||||||
image.unsafe[x, y] = blender(backdrop, source)
|
image.unsafe[x, y] = blender(backdrop, source(rgbx, coverage))
|
||||||
elif blendMode == MaskBlend:
|
|
||||||
image.unsafe[x, y] = rgbx(0, 0, 0, 0)
|
|
||||||
|
|
||||||
if blendMode == MaskBlend:
|
|
||||||
image.clearUnsafe(0, y, startX, y)
|
|
||||||
image.clearUnsafe(startX + coverages.len, y, image.width, y)
|
|
||||||
|
|
||||||
proc fillCoverage(
|
proc fillCoverage(
|
||||||
mask: Mask,
|
mask: Mask,
|
||||||
|
@ -1456,45 +1503,83 @@ proc fillCoverage(
|
||||||
blendMode: BlendMode
|
blendMode: BlendMode
|
||||||
) =
|
) =
|
||||||
var x = startX
|
var x = startX
|
||||||
when defined(amd64) and allowSimd:
|
|
||||||
if blendMode.hasSimdMaskBlender():
|
template simdBlob(blendProc: untyped) =
|
||||||
let
|
when allowSimd:
|
||||||
maskerSimd = blendMode.maskBlenderSimd()
|
when defined(amd64):
|
||||||
vecZero = mm_setzero_si128()
|
|
||||||
for _ in 0 ..< coverages.len div 16:
|
for _ in 0 ..< coverages.len div 16:
|
||||||
let
|
let
|
||||||
index = mask.dataIndex(x, y)
|
coveragesVec = mm_loadu_si128(coverages[x - startX].unsafeAddr)
|
||||||
coverageVec = mm_loadu_si128(coverages[x - startX].unsafeAddr)
|
eqZero = mm_cmpeq_epi8(coveragesVec, mm_setzero_si128())
|
||||||
if mm_movemask_epi8(mm_cmpeq_epi16(coverageVec, vecZero)) != 0xffff:
|
allZeroes = mm_movemask_epi8(eqZero) == 0xffff
|
||||||
# If the coverages are not all zero
|
if not allZeroes:
|
||||||
if blendMode == OverwriteBlend:
|
let backdrop = mm_loadu_si128(mask.unsafe[x, y].addr)
|
||||||
mm_storeu_si128(mask.data[index].addr, coverageVec)
|
|
||||||
else:
|
|
||||||
let backdrop = mm_loadu_si128(mask.data[index].addr)
|
|
||||||
mm_storeu_si128(
|
mm_storeu_si128(
|
||||||
mask.data[index].addr,
|
mask.unsafe[x, y].addr,
|
||||||
maskerSimd(backdrop, coverageVec)
|
blendProc(backdrop, coveragesVec)
|
||||||
)
|
)
|
||||||
elif blendMode == MaskBlend:
|
|
||||||
mm_storeu_si128(mask.data[index].addr, vecZero)
|
|
||||||
x += 16
|
x += 16
|
||||||
|
|
||||||
let maskBlender = blendMode.maskBlender()
|
template blendBlob(blendProc: untyped) =
|
||||||
for x in x ..< startX + coverages.len:
|
for x in x ..< startX + coverages.len:
|
||||||
let coverage = coverages[x - startX]
|
let coverage = coverages[x - startX]
|
||||||
if coverage != 0 or blendMode == ExcludeMaskBlend:
|
if coverage != 0:
|
||||||
if blendMode == OverwriteBlend:
|
|
||||||
mask.unsafe[x, y] = coverage
|
|
||||||
else:
|
|
||||||
let backdrop = mask.unsafe[x, y]
|
let backdrop = mask.unsafe[x, y]
|
||||||
mask.unsafe[x, y] = maskBlender(backdrop, coverage)
|
mask.unsafe[x, y] = blendProc(backdrop, coverage)
|
||||||
elif blendMode == MaskBlend:
|
|
||||||
|
case blendMode:
|
||||||
|
of OverwriteBlend:
|
||||||
|
copyMem(
|
||||||
|
mask.unsafe[startX, y].addr,
|
||||||
|
coverages[0].unsafeAddr,
|
||||||
|
coverages.len
|
||||||
|
)
|
||||||
|
|
||||||
|
of NormalBlend:
|
||||||
|
simdBlob(maskBlendNormalSimd)
|
||||||
|
blendBlob(maskBlendNormal)
|
||||||
|
|
||||||
|
of MaskBlend:
|
||||||
|
when allowSimd:
|
||||||
|
when defined(amd64):
|
||||||
|
for _ in 0 ..< coverages.len div 16:
|
||||||
|
let
|
||||||
|
coveragesVec = mm_loadu_si128(coverages[x - startX].unsafeAddr)
|
||||||
|
eqZero = mm_cmpeq_epi8(coveragesVec, mm_setzero_si128())
|
||||||
|
allZeroes = mm_movemask_epi8(eqZero) == 0xffff
|
||||||
|
if not allZeroes:
|
||||||
|
let backdrop = mm_loadu_si128(mask.unsafe[x, y].addr)
|
||||||
|
mm_storeu_si128(
|
||||||
|
mask.unsafe[x, y].addr,
|
||||||
|
maskBlendMaskSimd(backdrop, coveragesVec)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
mm_storeu_si128(mask.unsafe[x, y].addr, mm_setzero_si128())
|
||||||
|
x += 16
|
||||||
|
|
||||||
|
for x in x ..< startX + coverages.len:
|
||||||
|
let coverage = coverages[x - startX]
|
||||||
|
if coverage != 0:
|
||||||
|
let backdrop = mask.unsafe[x, y]
|
||||||
|
mask.unsafe[x, y] = maskBlendMask(backdrop, coverage)
|
||||||
|
else:
|
||||||
mask.unsafe[x, y] = 0
|
mask.unsafe[x, y] = 0
|
||||||
|
|
||||||
if blendMode == MaskBlend:
|
|
||||||
mask.clearUnsafe(0, y, startX, y)
|
mask.clearUnsafe(0, y, startX, y)
|
||||||
mask.clearUnsafe(startX + coverages.len, y, mask.width, y)
|
mask.clearUnsafe(startX + coverages.len, y, mask.width, y)
|
||||||
|
|
||||||
|
of SubtractMaskBlend:
|
||||||
|
simdBlob(maskBlendSubtractSimd)
|
||||||
|
blendBlob(maskBlendSubtract)
|
||||||
|
|
||||||
|
of ExcludeMaskBlend:
|
||||||
|
simdBlob(maskBlendExcludeSimd)
|
||||||
|
blendBlob(maskBlendExclude)
|
||||||
|
|
||||||
|
else:
|
||||||
|
let maskBlender = blendMode.maskBlender()
|
||||||
|
blendBlob(maskBlender)
|
||||||
|
|
||||||
proc fillHits(
|
proc fillHits(
|
||||||
image: Image,
|
image: Image,
|
||||||
rgbx: ColorRGBX,
|
rgbx: ColorRGBX,
|
||||||
|
@ -1530,6 +1615,8 @@ proc fillHits(
|
||||||
image.unsafe[x, y] = blendNormal(backdrop, rgbx)
|
image.unsafe[x, y] = blendNormal(backdrop, rgbx)
|
||||||
|
|
||||||
of MaskBlend:
|
of MaskBlend:
|
||||||
|
{.linearScanEnd.}
|
||||||
|
|
||||||
var filledTo = startX
|
var filledTo = startX
|
||||||
for (start, len) in hits.walkInteger(numHits, windingRule, y, image.width):
|
for (start, len) in hits.walkInteger(numHits, windingRule, y, image.width):
|
||||||
block: # Clear any gap between this fill and the previous fill
|
block: # Clear any gap between this fill and the previous fill
|
||||||
|
@ -1553,6 +1640,21 @@ proc fillHits(
|
||||||
image.clearUnsafe(0, y, startX, y)
|
image.clearUnsafe(0, y, startX, y)
|
||||||
image.clearUnsafe(filledTo, y, image.width, y)
|
image.clearUnsafe(filledTo, y, image.width, y)
|
||||||
|
|
||||||
|
of SubtractMaskBlend:
|
||||||
|
for (start, len) in hits.walkInteger(numHits, windingRule, y, image.width):
|
||||||
|
for x in start ..< start + len:
|
||||||
|
if rgbx.a == 255:
|
||||||
|
image.unsafe[x, y] = rgbx(0, 0, 0, 0)
|
||||||
|
else:
|
||||||
|
let backdrop = image.unsafe[x, y]
|
||||||
|
image.unsafe[x, y] = blendSubtractMask(backdrop, rgbx)
|
||||||
|
|
||||||
|
of ExcludeMaskBlend:
|
||||||
|
for (start, len) in hits.walkInteger(numHits, windingRule, y, image.width):
|
||||||
|
for x in start ..< start + len:
|
||||||
|
let backdrop = image.unsafe[x, y]
|
||||||
|
image.unsafe[x, y] = blendExcludeMask(backdrop, rgbx)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
let blender = blendMode.blender()
|
let blender = blendMode.blender()
|
||||||
for (start, len) in hits.walkInteger(numHits, windingRule, y, image.width):
|
for (start, len) in hits.walkInteger(numHits, windingRule, y, image.width):
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import benchy, chroma, pixie/images, vmath
|
import benchy, chroma, pixie/blends, pixie/images, vmath
|
||||||
|
|
||||||
include pixie/blends
|
|
||||||
|
|
||||||
let
|
let
|
||||||
backdrop = newImage(256, 256)
|
backdrop = newImage(256, 256)
|
||||||
|
|
|
@ -47,6 +47,16 @@ block:
|
||||||
image.fill(rgbx(255, 255, 255, 255))
|
image.fill(rgbx(255, 255, 255, 255))
|
||||||
image.fillPath(rect, paint)
|
image.fillPath(rect, paint)
|
||||||
|
|
||||||
|
timeIt "rect Image SubtractMaskBlend":
|
||||||
|
paint.blendMode = SubtractMaskBlend
|
||||||
|
image.fill(rgbx(255, 255, 255, 255))
|
||||||
|
image.fillPath(rect, paint)
|
||||||
|
|
||||||
|
timeIt "rect Image ExcludeMaskBlend":
|
||||||
|
paint.blendMode = ExcludeMaskBlend
|
||||||
|
image.fill(rgbx(255, 255, 255, 255))
|
||||||
|
image.fillPath(rect, paint)
|
||||||
|
|
||||||
timeIt "roundedRect Image OverwriteBlend":
|
timeIt "roundedRect Image OverwriteBlend":
|
||||||
paint.blendMode = OverwriteBlend
|
paint.blendMode = OverwriteBlend
|
||||||
image.fillPath(roundedRect, paint)
|
image.fillPath(roundedRect, paint)
|
||||||
|
@ -60,6 +70,16 @@ block:
|
||||||
image.fill(rgbx(255, 255, 255, 255))
|
image.fill(rgbx(255, 255, 255, 255))
|
||||||
image.fillPath(roundedRect, paint)
|
image.fillPath(roundedRect, paint)
|
||||||
|
|
||||||
|
timeIt "roundedRect Image SubtractMaskBlend":
|
||||||
|
paint.blendMode = SubtractMaskBlend
|
||||||
|
image.fill(rgbx(255, 255, 255, 255))
|
||||||
|
image.fillPath(roundedRect, paint)
|
||||||
|
|
||||||
|
timeIt "roundedRect Image ExcludeMaskBlend":
|
||||||
|
paint.blendMode = ExcludeMaskBlend
|
||||||
|
image.fill(rgbx(255, 255, 255, 255))
|
||||||
|
image.fillPath(roundedRect, paint)
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let mask = newMask(width, height)
|
let mask = newMask(width, height)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue