Merge pull request #485 from treeform/guzba
switch to split draw, either pixel-aligned fast or smooth slower
This commit is contained in:
commit
aabbf69e7c
19 changed files with 474 additions and 579 deletions
|
@ -1,39 +1,4 @@
|
||||||
import benchy, cairo, pixie, pixie/blends, pixie/internal
|
import benchy, cairo, pixie
|
||||||
|
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
|
||||||
import nimsimd/sse2
|
|
||||||
|
|
||||||
when defined(release):
|
|
||||||
{.push checks: off.}
|
|
||||||
|
|
||||||
proc drawBasic(backdrop, source: Image) =
|
|
||||||
for y in 0 ..< min(backdrop.height, source.height):
|
|
||||||
if isOpaque(source.data, source.dataIndex(0, y), source.width):
|
|
||||||
copyMem(
|
|
||||||
backdrop.data[backdrop.dataIndex(0, y)].addr,
|
|
||||||
source.data[source.dataIndex(0, y)].addr,
|
|
||||||
min(backdrop.width, source.width) * 4
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
var x: int
|
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
|
||||||
let vec255 = mm_set1_epi32(cast[int32](uint32.high))
|
|
||||||
for _ in 0 ..< min(backdrop.width, source.width) div 4:
|
|
||||||
let sourceVec = mm_loadu_si128(source.data[source.dataIndex(x, y)].addr)
|
|
||||||
if mm_movemask_epi8(mm_cmpeq_epi8(sourceVec, mm_setzero_si128())) != 0xffff:
|
|
||||||
if (mm_movemask_epi8(mm_cmpeq_epi8(sourceVec, vec255)) and 0x8888) == 0x8888:
|
|
||||||
mm_storeu_si128(backdrop.data[backdrop.dataIndex(x, y)].addr, sourceVec)
|
|
||||||
else:
|
|
||||||
let backdropVec = mm_loadu_si128(backdrop.data[backdrop.dataIndex(x, y)].addr)
|
|
||||||
mm_storeu_si128(
|
|
||||||
backdrop.data[backdrop.dataIndex(x, y)].addr,
|
|
||||||
blendNormalInlineSimd(backdropVec, sourceVec)
|
|
||||||
)
|
|
||||||
x += 4
|
|
||||||
# No scalar for now
|
|
||||||
|
|
||||||
when defined(release):
|
|
||||||
{.pop.}
|
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
|
@ -43,12 +8,6 @@ block:
|
||||||
ctx = tmp.create()
|
ctx = tmp.create()
|
||||||
|
|
||||||
timeIt "cairo draw normal":
|
timeIt "cairo draw normal":
|
||||||
# ctx.setSourceRgba(0.5, 0.5, 0.5, 1)
|
|
||||||
# let operator = ctx.getOperator()
|
|
||||||
# ctx.setOperator(OperatorSource)
|
|
||||||
# ctx.paint()
|
|
||||||
# ctx.setOperator(operator)
|
|
||||||
|
|
||||||
ctx.setSource(backdrop, 0, 0)
|
ctx.setSource(backdrop, 0, 0)
|
||||||
ctx.paint()
|
ctx.paint()
|
||||||
ctx.setSource(source, 0, 0)
|
ctx.setSource(source, 0, 0)
|
||||||
|
@ -64,7 +23,6 @@ block:
|
||||||
tmp = newImage(1568, 940)
|
tmp = newImage(1568, 940)
|
||||||
|
|
||||||
timeIt "pixie draw normal":
|
timeIt "pixie draw normal":
|
||||||
# tmp.fill(rgbx(127, 127, 127, 255))
|
|
||||||
tmp.draw(backdrop)
|
tmp.draw(backdrop)
|
||||||
tmp.draw(source)
|
tmp.draw(source)
|
||||||
|
|
||||||
|
@ -77,25 +35,11 @@ block:
|
||||||
tmp = newImage(1568, 940)
|
tmp = newImage(1568, 940)
|
||||||
|
|
||||||
timeIt "pixie draw overwrite":
|
timeIt "pixie draw overwrite":
|
||||||
# tmp.fill(rgbx(127, 127, 127, 255))
|
|
||||||
tmp.draw(backdrop, blendMode = OverwriteBlend)
|
tmp.draw(backdrop, blendMode = OverwriteBlend)
|
||||||
tmp.draw(source)
|
tmp.draw(source)
|
||||||
|
|
||||||
# tmp.writeFile("tmp2.png")
|
# tmp.writeFile("tmp2.png")
|
||||||
|
|
||||||
block:
|
|
||||||
let
|
|
||||||
backdrop = readImage("tests/fileformats/svg/masters/dragon2.png")
|
|
||||||
source = readImage("tests/fileformats/svg/masters/Ghostscript_Tiger.png")
|
|
||||||
tmp = newImage(1568, 940)
|
|
||||||
|
|
||||||
timeIt "pixie draw basic":
|
|
||||||
# tmp.fill(rgbx(127, 127, 127, 255))
|
|
||||||
tmp.drawBasic(backdrop)
|
|
||||||
tmp.drawBasic(source)
|
|
||||||
|
|
||||||
# tmp.writeFile("tmp2.png")
|
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
backdrop = imageSurfaceCreateFromPng("tests/fileformats/svg/masters/dragon2.png")
|
backdrop = imageSurfaceCreateFromPng("tests/fileformats/svg/masters/dragon2.png")
|
||||||
|
@ -104,17 +48,11 @@ block:
|
||||||
ctx = tmp.create()
|
ctx = tmp.create()
|
||||||
|
|
||||||
timeIt "cairo draw mask":
|
timeIt "cairo draw mask":
|
||||||
ctx.setSourceRgba(1, 1, 1, 1)
|
|
||||||
let operator = ctx.getOperator()
|
|
||||||
ctx.setOperator(OperatorSource)
|
|
||||||
ctx.paint()
|
|
||||||
ctx.setOperator(operator)
|
|
||||||
|
|
||||||
ctx.setSource(backdrop, 0, 0)
|
ctx.setSource(backdrop, 0, 0)
|
||||||
ctx.mask(source, 0, 0)
|
ctx.mask(source, 0, 0)
|
||||||
tmp.flush()
|
tmp.flush()
|
||||||
|
|
||||||
# echo tmp.writeToPng("tmp_masked.png")
|
# tmp.writeToPng("tmp_masked.png")
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
## Blending modes.
|
## Blending modes.
|
||||||
|
|
||||||
import chroma, common, simd, std/math
|
import chroma, common, std/math
|
||||||
|
|
||||||
# See https://www.w3.org/TR/compositing-1/
|
# See https://www.w3.org/TR/compositing-1/
|
||||||
# See https://www.khronos.org/registry/OpenGL/extensions/KHR/KHR_blend_equation_advanced.txt
|
# See https://www.khronos.org/registry/OpenGL/extensions/KHR/KHR_blend_equation_advanced.txt
|
||||||
|
@ -412,82 +412,5 @@ proc blender*(blendMode: BlendMode): Blender {.raises: [].} =
|
||||||
of SubtractMaskBlend: subtractMaskBlender
|
of SubtractMaskBlend: subtractMaskBlender
|
||||||
of ExcludeMaskBlend: excludeMaskBlender
|
of ExcludeMaskBlend: excludeMaskBlender
|
||||||
|
|
||||||
when defined(amd64) and allowSimd:
|
|
||||||
type
|
|
||||||
BlenderSimd* = proc(blackdrop, source: M128i): M128i {.gcsafe, raises: [].}
|
|
||||||
## Function signature returned by blenderSimd.
|
|
||||||
|
|
||||||
proc blendNormalSimd*(backdrop, source: M128i): M128i {.inline.} =
|
|
||||||
let
|
|
||||||
alphaMask = mm_set1_epi32(cast[int32](0xff000000))
|
|
||||||
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
|
||||||
div255 = mm_set1_epi16(cast[int16](0x8081))
|
|
||||||
|
|
||||||
var
|
|
||||||
sourceAlpha = mm_and_si128(source, alphaMask)
|
|
||||||
backdropEven = mm_slli_epi16(backdrop, 8)
|
|
||||||
backdropOdd = mm_and_si128(backdrop, oddMask)
|
|
||||||
|
|
||||||
sourceAlpha = mm_or_si128(sourceAlpha, mm_srli_epi32(sourceAlpha, 16))
|
|
||||||
|
|
||||||
let k = mm_sub_epi32(
|
|
||||||
mm_set1_epi32(cast[int32]([0.uint8, 255, 0, 255])),
|
|
||||||
sourceAlpha
|
|
||||||
)
|
|
||||||
|
|
||||||
backdropEven = mm_mulhi_epu16(backdropEven, k)
|
|
||||||
backdropOdd = mm_mulhi_epu16(backdropOdd, k)
|
|
||||||
|
|
||||||
backdropEven = mm_srli_epi16(mm_mulhi_epu16(backdropEven, div255), 7)
|
|
||||||
backdropOdd = mm_srli_epi16(mm_mulhi_epu16(backdropOdd, div255), 7)
|
|
||||||
|
|
||||||
mm_add_epi8(
|
|
||||||
source,
|
|
||||||
mm_or_si128(backdropEven, mm_slli_epi16(backdropOdd, 8))
|
|
||||||
)
|
|
||||||
|
|
||||||
proc blendMaskSimd*(backdrop, source: M128i): M128i {.inline.} =
|
|
||||||
let
|
|
||||||
alphaMask = mm_set1_epi32(cast[int32](0xff000000))
|
|
||||||
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
|
||||||
div255 = mm_set1_epi16(cast[int16](0x8081))
|
|
||||||
|
|
||||||
var
|
|
||||||
sourceAlpha = mm_and_si128(source, alphaMask)
|
|
||||||
backdropEven = mm_slli_epi16(backdrop, 8)
|
|
||||||
backdropOdd = mm_and_si128(backdrop, oddMask)
|
|
||||||
|
|
||||||
sourceAlpha = mm_or_si128(sourceAlpha, mm_srli_epi32(sourceAlpha, 16))
|
|
||||||
|
|
||||||
backdropEven = mm_mulhi_epu16(backdropEven, sourceAlpha)
|
|
||||||
backdropOdd = mm_mulhi_epu16(backdropOdd, sourceAlpha)
|
|
||||||
|
|
||||||
backdropEven = mm_srli_epi16(mm_mulhi_epu16(backdropEven, div255), 7)
|
|
||||||
backdropOdd = mm_srli_epi16(mm_mulhi_epu16(backdropOdd, div255), 7)
|
|
||||||
|
|
||||||
mm_or_si128(backdropEven, mm_slli_epi16(backdropOdd, 8))
|
|
||||||
|
|
||||||
proc normalSimdBlender(backdrop, source: M128i): M128i =
|
|
||||||
blendNormalSimd(backdrop, source)
|
|
||||||
|
|
||||||
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: normalSimdBlender
|
|
||||||
of MaskBlend: maskSimdBlender
|
|
||||||
of OverwriteBlend: overwriteSimdBlender
|
|
||||||
else:
|
|
||||||
raise newException(PixieError, "No SIMD blender for " & $blendMode)
|
|
||||||
|
|
||||||
proc hasSimdBlender*(blendMode: BlendMode): bool {.inline, raises: [].} =
|
|
||||||
## Is there a blend function for a given blend mode with SIMD support?
|
|
||||||
blendMode in {NormalBlend, MaskBlend, OverwriteBlend}
|
|
||||||
|
|
||||||
when defined(release):
|
when defined(release):
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
@ -394,63 +394,11 @@ proc getRgbaSmooth*(
|
||||||
else:
|
else:
|
||||||
topMix
|
topMix
|
||||||
|
|
||||||
proc blitLine(a, b: ptr UncheckedArray[ColorRGBX], len: int, blender: Blender) =
|
|
||||||
for i in 0 ..< len:
|
|
||||||
a[i] = blender(a[i], b[i])
|
|
||||||
|
|
||||||
proc blitLineNormal(a, b: ptr UncheckedArray[ColorRGBX], len: int) {.hasSimd.} =
|
|
||||||
for i in 0 ..< len:
|
|
||||||
a[i] = blendNormal(a[i], b[i])
|
|
||||||
|
|
||||||
proc blitLineOverwrite(a, b: ptr UncheckedArray[ColorRGBX], len: int) =
|
|
||||||
copyMem(a[0].addr, b[0].addr, len * 4)
|
|
||||||
|
|
||||||
template getUncheckedArray(a: Image, x, y: int): ptr UncheckedArray[ColorRGBX] =
|
|
||||||
cast[ptr UncheckedArray[ColorRGBX]](a.data[a.dataIndex(x, y)].addr)
|
|
||||||
|
|
||||||
proc blitRect(
|
|
||||||
a, b: Image, pos = ivec2(0, 0), blendMode = NormalBlend
|
|
||||||
) =
|
|
||||||
## Blits one image onto another using integer position with color blending.
|
|
||||||
let
|
|
||||||
px = pos.x.int
|
|
||||||
py = pos.y.int
|
|
||||||
xStart = max(-px, 0)
|
|
||||||
yStart = max(-py, 0)
|
|
||||||
xEnd = min(b.width, a.width - px)
|
|
||||||
yEnd = min(b.height, a.height - py)
|
|
||||||
|
|
||||||
case blendMode:
|
|
||||||
of NormalBlend:
|
|
||||||
for y in yStart ..< yEnd:
|
|
||||||
blitLineNormal(
|
|
||||||
a.getUncheckedArray(xStart + px, y + py),
|
|
||||||
b.getUncheckedArray(xStart, y),
|
|
||||||
xEnd - xStart
|
|
||||||
)
|
|
||||||
of OverwriteBlend:
|
|
||||||
{.linearScanEnd.}
|
|
||||||
for y in yStart ..< yEnd:
|
|
||||||
blitLineOverwrite(
|
|
||||||
a.getUncheckedArray(xStart + px, y + py),
|
|
||||||
b.getUncheckedArray(xStart, y),
|
|
||||||
xEnd - xStart
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
let blender = blendMode.blender()
|
|
||||||
for y in yStart ..< yEnd:
|
|
||||||
blitLine(
|
|
||||||
a.getUncheckedArray(xStart + px, y + py),
|
|
||||||
b.getUncheckedArray(xStart, y),
|
|
||||||
xEnd - xStart,
|
|
||||||
blender
|
|
||||||
)
|
|
||||||
|
|
||||||
proc drawCorrect(
|
proc drawCorrect(
|
||||||
a, b: Image, transform = mat3(), blendMode = NormalBlend, tiled = false
|
a, b: Image, transform = mat3(), blendMode: BlendMode, tiled: bool
|
||||||
) {.raises: [PixieError].} =
|
) =
|
||||||
## Draws one image onto another using matrix with color blending.
|
## Draws one image onto another using a matrix transform and color blending.
|
||||||
|
## This proc is not about performance, it should be as simple as possible.
|
||||||
var
|
var
|
||||||
inverseTransform = transform.inverse()
|
inverseTransform = transform.inverse()
|
||||||
# Compute movement vectors
|
# Compute movement vectors
|
||||||
|
@ -466,7 +414,131 @@ proc drawCorrect(
|
||||||
dx /= 2
|
dx /= 2
|
||||||
dy /= 2
|
dy /= 2
|
||||||
filterBy2 /= 2
|
filterBy2 /= 2
|
||||||
inverseTransform = scale(vec2(1/2, 1/2)) * inverseTransform
|
inverseTransform = scale(vec2(0.5, 0.5)) * inverseTransform
|
||||||
|
|
||||||
|
while filterBy2 <= 0.5:
|
||||||
|
b = b.magnifyBy2()
|
||||||
|
p *= 2
|
||||||
|
dx *= 2
|
||||||
|
dy *= 2
|
||||||
|
filterBy2 *= 2
|
||||||
|
inverseTransform = scale(vec2(2, 2)) * inverseTransform
|
||||||
|
|
||||||
|
let blender = blendMode.blender()
|
||||||
|
for y in 0 ..< a.height:
|
||||||
|
for x in 0 ..< a.width:
|
||||||
|
let
|
||||||
|
samplePos = inverseTransform * vec2(x.float32 + h, y.float32 + h)
|
||||||
|
xFloat = samplePos.x - h
|
||||||
|
yFloat = samplePos.y - h
|
||||||
|
backdrop = a.unsafe[x, y]
|
||||||
|
sample = b.getRgbaSmooth(xFloat, yFloat, tiled)
|
||||||
|
blended = blender(backdrop, sample)
|
||||||
|
a.unsafe[x, y] = blended
|
||||||
|
|
||||||
|
template getUncheckedArray(
|
||||||
|
image: Image, x, y: int
|
||||||
|
): ptr UncheckedArray[ColorRGBX] =
|
||||||
|
cast[ptr UncheckedArray[ColorRGBX]](image.data[image.dataIndex(x, y)].addr)
|
||||||
|
|
||||||
|
proc blitLine(a, b: ptr UncheckedArray[ColorRGBX], len: int, blender: Blender) {.inline.} =
|
||||||
|
for i in 0 ..< len:
|
||||||
|
a[i] = blender(a[i], b[i])
|
||||||
|
|
||||||
|
proc blitLineOverwrite(a, b: ptr UncheckedArray[ColorRGBX], len: int) {.inline.} =
|
||||||
|
copyMem(a[0].addr, b[0].addr, len * 4)
|
||||||
|
|
||||||
|
proc blitLineNormal(a, b: ptr UncheckedArray[ColorRGBX], len: int) {.hasSimd.} =
|
||||||
|
for i in 0 ..< len:
|
||||||
|
a[i] = blendNormal(a[i], b[i])
|
||||||
|
|
||||||
|
proc blitLineMask(a, b: ptr UncheckedArray[ColorRGBX], len: int) {.hasSimd.} =
|
||||||
|
for i in 0 ..< len:
|
||||||
|
a[i] = blendMask(a[i], b[i])
|
||||||
|
|
||||||
|
proc blitRect(a, b: Image, pos: Ivec2, blendMode: BlendMode) =
|
||||||
|
let
|
||||||
|
px = pos.x.int
|
||||||
|
py = pos.y.int
|
||||||
|
|
||||||
|
if px >= a.width or px + b.width <= 0 or py >= a.height or py + b.height <= 0:
|
||||||
|
if blendMode == MaskBlend:
|
||||||
|
a.fill(rgbx(0, 0, 0, 0))
|
||||||
|
return
|
||||||
|
|
||||||
|
let
|
||||||
|
xStart = max(-px, 0)
|
||||||
|
yStart = max(-py, 0)
|
||||||
|
xEnd = min(b.width, a.width - px)
|
||||||
|
yEnd = min(b.height, a.height - py)
|
||||||
|
|
||||||
|
case blendMode:
|
||||||
|
of NormalBlend:
|
||||||
|
for y in yStart ..< yEnd:
|
||||||
|
blitLineNormal(
|
||||||
|
a.getUncheckedArray(xStart + px, y + py),
|
||||||
|
b.getUncheckedArray(xStart, y),
|
||||||
|
xEnd - xStart
|
||||||
|
)
|
||||||
|
of OverwriteBlend:
|
||||||
|
for y in yStart ..< yEnd:
|
||||||
|
blitLineOverwrite(
|
||||||
|
a.getUncheckedArray(xStart + px, y + py),
|
||||||
|
b.getUncheckedArray(xStart, y),
|
||||||
|
xEnd - xStart
|
||||||
|
)
|
||||||
|
of MaskBlend:
|
||||||
|
{.linearScanEnd.}
|
||||||
|
if yStart + py > 0:
|
||||||
|
zeroMem(a.data[0].addr, (yStart + py) * a.width * 4)
|
||||||
|
for y in yStart ..< yEnd:
|
||||||
|
if xStart + px > 0:
|
||||||
|
zeroMem(a.data[a.dataIndex(0, y + py)].addr, (xStart + px) * 4)
|
||||||
|
blitLineMask(
|
||||||
|
a.getUncheckedArray(xStart + px, y + py),
|
||||||
|
b.getUncheckedArray(xStart, y),
|
||||||
|
xEnd - xStart
|
||||||
|
)
|
||||||
|
if xEnd + px < a.width:
|
||||||
|
zeroMem(
|
||||||
|
a.data[a.dataIndex(xEnd + px, y + py)].addr,
|
||||||
|
(a.width - (xEnd + px)) * 4
|
||||||
|
)
|
||||||
|
if yEnd + py < a.height:
|
||||||
|
zeroMem(
|
||||||
|
a.data[a.dataIndex(0, yEnd + py)].addr,
|
||||||
|
(a.height - (yEnd + py)) * a.width * 4
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
let blender = blendMode.blender()
|
||||||
|
for y in yStart ..< yEnd:
|
||||||
|
blitLine(
|
||||||
|
a.getUncheckedArray(xStart + px, y + py),
|
||||||
|
b.getUncheckedArray(xStart, y),
|
||||||
|
xEnd - xStart,
|
||||||
|
blender
|
||||||
|
)
|
||||||
|
|
||||||
|
proc draw*(
|
||||||
|
a, b: Image, transform = mat3(), blendMode = NormalBlend
|
||||||
|
) {.raises: [PixieError].} =
|
||||||
|
## Draws one image onto another using a matrix transform and color blending.
|
||||||
|
var
|
||||||
|
inverseTransform = transform.inverse()
|
||||||
|
# Compute movement vectors
|
||||||
|
p = inverseTransform * vec2(0 + h, 0 + h)
|
||||||
|
dx = inverseTransform * vec2(1 + h, 0 + h) - p
|
||||||
|
dy = inverseTransform * vec2(0 + h, 1 + h) - p
|
||||||
|
filterBy2 = max(dx.length, dy.length)
|
||||||
|
b = b
|
||||||
|
|
||||||
|
while filterBy2 >= 2.0:
|
||||||
|
b = b.minifyBy2()
|
||||||
|
p /= 2
|
||||||
|
dx /= 2
|
||||||
|
dy /= 2
|
||||||
|
filterBy2 /= 2
|
||||||
|
inverseTransform = scale(vec2(0.5, 0.5)) * inverseTransform
|
||||||
|
|
||||||
while filterBy2 <= 0.5:
|
while filterBy2 <= 0.5:
|
||||||
b = b.magnifyBy2()
|
b = b.magnifyBy2()
|
||||||
|
@ -485,277 +557,10 @@ proc drawCorrect(
|
||||||
transform[2, 1].fractional == 0.0
|
transform[2, 1].fractional == 0.0
|
||||||
)
|
)
|
||||||
|
|
||||||
if not hasRotationOrScaling and not smooth and not tiled:
|
|
||||||
blitRect(a, b, ivec2(transform[2, 0].int32, transform[2, 1].int32), blendMode)
|
|
||||||
return
|
|
||||||
|
|
||||||
let blender = blendMode.blender()
|
|
||||||
for y in 0 ..< a.height:
|
|
||||||
for x in 0 ..< a.width:
|
|
||||||
let
|
|
||||||
samplePos = inverseTransform * vec2(x.float32 + h, y.float32 + h)
|
|
||||||
xFloat = samplePos.x - h
|
|
||||||
yFloat = samplePos.y - h
|
|
||||||
backdrop = a.unsafe[x, y]
|
|
||||||
sample = b.getRgbaSmooth(xFloat, yFloat, tiled)
|
|
||||||
blended = blender(backdrop, sample)
|
|
||||||
a.unsafe[x, y] = blended
|
|
||||||
|
|
||||||
proc drawUber(
|
|
||||||
a, b: Image, transform = mat3(), blendMode: BlendMode
|
|
||||||
) {.raises: [PixieError].} =
|
|
||||||
let
|
|
||||||
corners = [
|
|
||||||
transform * vec2(0, 0),
|
|
||||||
transform * vec2(b.width.float32, 0),
|
|
||||||
transform * vec2(b.width.float32, b.height.float32),
|
|
||||||
transform * vec2(0, b.height.float32)
|
|
||||||
]
|
|
||||||
perimeter = [
|
|
||||||
segment(corners[0], corners[1]),
|
|
||||||
segment(corners[1], corners[2]),
|
|
||||||
segment(corners[2], corners[3]),
|
|
||||||
segment(corners[3], corners[0])
|
|
||||||
]
|
|
||||||
|
|
||||||
var
|
|
||||||
inverseTransform = transform.inverse()
|
|
||||||
# Compute movement vectors
|
|
||||||
p = inverseTransform * vec2(0 + h, 0 + h)
|
|
||||||
dx = inverseTransform * vec2(1 + h, 0 + h) - p
|
|
||||||
dy = inverseTransform * vec2(0 + h, 1 + h) - p
|
|
||||||
filterBy2 = max(dx.length, dy.length)
|
|
||||||
b = b
|
|
||||||
|
|
||||||
while filterBy2 >= 2.0:
|
|
||||||
b = b.minifyBy2()
|
|
||||||
p /= 2
|
|
||||||
dx /= 2
|
|
||||||
dy /= 2
|
|
||||||
filterBy2 /= 2
|
|
||||||
|
|
||||||
while filterBy2 <= 0.5:
|
|
||||||
b = b.magnifyBy2()
|
|
||||||
p *= 2
|
|
||||||
dx *= 2
|
|
||||||
dy *= 2
|
|
||||||
filterBy2 *= 2
|
|
||||||
|
|
||||||
let
|
|
||||||
hasRotationOrScaling = not(dx == vec2(1, 0) and dy == vec2(0, 1))
|
|
||||||
smooth = not(
|
|
||||||
dx.length == 1.0 and
|
|
||||||
dy.length == 1.0 and
|
|
||||||
transform[2, 0].fractional == 0.0 and
|
|
||||||
transform[2, 1].fractional == 0.0
|
|
||||||
)
|
|
||||||
|
|
||||||
# Determine where we should start and stop drawing in the y dimension
|
|
||||||
var
|
|
||||||
yMin = a.height
|
|
||||||
yMax = 0
|
|
||||||
for segment in perimeter:
|
|
||||||
yMin = min(yMin, segment.at.y.floor.int)
|
|
||||||
yMax = max(yMax, segment.at.y.ceil.int)
|
|
||||||
yMin = yMin.clamp(0, a.height)
|
|
||||||
yMax = yMax.clamp(0, a.height)
|
|
||||||
|
|
||||||
let blender = blendMode.blender()
|
|
||||||
|
|
||||||
if blendMode == MaskBlend:
|
|
||||||
if yMin > 0:
|
|
||||||
zeroMem(a.data[0].addr, 4 * yMin * a.width)
|
|
||||||
|
|
||||||
for y in yMin ..< yMax:
|
|
||||||
# Determine where we should start and stop drawing in the x dimension
|
|
||||||
var
|
|
||||||
xMin = a.width.float32
|
|
||||||
xMax = 0.float32
|
|
||||||
for yOffset in [0.float32, 1]:
|
|
||||||
let scanLine = Line(
|
|
||||||
a: vec2(-1000, y.float32 + yOffset),
|
|
||||||
b: vec2(1000, y.float32 + yOffset)
|
|
||||||
)
|
|
||||||
for segment in perimeter:
|
|
||||||
var at: Vec2
|
|
||||||
if scanline.intersects(segment, at) and segment.to != at:
|
|
||||||
xMin = min(xMin, at.x)
|
|
||||||
xMax = max(xMax, at.x)
|
|
||||||
|
|
||||||
var xStart, xStop: int
|
|
||||||
if hasRotationOrScaling or smooth:
|
if hasRotationOrScaling or smooth:
|
||||||
xStart = xMin.floor.int
|
a.drawCorrect(b, inverseTransform.inverse(), blendMode, false)
|
||||||
xStop = xMax.ceil.int
|
|
||||||
else:
|
else:
|
||||||
# Rotation of 360 degrees can cause knife-edge issues with floor and ceil
|
a.blitRect(b, ivec2(transform[2, 0].int32, transform[2, 1].int32), blendMode)
|
||||||
xStart = xMin.round().int
|
|
||||||
xStop = xMax.round().int
|
|
||||||
xStart = xStart.clamp(0, a.width)
|
|
||||||
xStop = xStop.clamp(0, a.width)
|
|
||||||
|
|
||||||
# Skip this row if there is nothing in-bounds to draw
|
|
||||||
if xStart == a.width or xStop == 0:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if blendMode == MaskBlend:
|
|
||||||
if xStart > 0:
|
|
||||||
zeroMem(a.data[a.dataIndex(0, y)].addr, 4 * xStart)
|
|
||||||
|
|
||||||
if smooth:
|
|
||||||
var srcPos = p + dx * xStart.float32 + dy * y.float32
|
|
||||||
srcPos = vec2(srcPos.x - h, srcPos.y - h)
|
|
||||||
for x in xStart ..< xStop:
|
|
||||||
let
|
|
||||||
backdrop = a.unsafe[x, y]
|
|
||||||
sample = b.getRgbaSmooth(srcPos.x, srcPos.y)
|
|
||||||
blended = blender(backdrop, sample)
|
|
||||||
a.unsafe[x, y] = blended
|
|
||||||
srcPos += dx
|
|
||||||
|
|
||||||
else:
|
|
||||||
var x = xStart
|
|
||||||
if not hasRotationOrScaling:
|
|
||||||
let
|
|
||||||
srcPos = p + dx * x.float32 + dy * y.float32
|
|
||||||
sy = srcPos.y.int
|
|
||||||
var sx = srcPos.x.int
|
|
||||||
|
|
||||||
if blendMode in {NormalBlend, OverwriteBlend} and
|
|
||||||
isOpaque(b.data, b.dataIndex(sx, sy), xStop - xStart):
|
|
||||||
copyMem(
|
|
||||||
a.data[a.dataIndex(x, y)].addr,
|
|
||||||
b.data[b.dataIndex(sx, sy)].addr,
|
|
||||||
(xStop - xStart) * 4
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
|
|
||||||
when defined(amd64) and allowSimd:
|
|
||||||
case blendMode:
|
|
||||||
of OverwriteBlend:
|
|
||||||
for _ in 0 ..< (xStop - xStart) div 16:
|
|
||||||
for q in [0, 4, 8, 12]:
|
|
||||||
let sourceVec = mm_loadu_si128(b.data[b.dataIndex(sx + q, sy)].addr)
|
|
||||||
mm_storeu_si128(a.data[a.dataIndex(x + q, y)].addr, sourceVec)
|
|
||||||
x += 16
|
|
||||||
sx += 16
|
|
||||||
of NormalBlend:
|
|
||||||
let vec255 = mm_set1_epi32(cast[int32](uint32.high))
|
|
||||||
for _ in 0 ..< (xStop - xStart) div 16:
|
|
||||||
for q in [0, 4, 8, 12]:
|
|
||||||
let
|
|
||||||
sourceVec = mm_loadu_si128(b.data[b.dataIndex(sx + q, sy)].addr)
|
|
||||||
eqZer0 = mm_cmpeq_epi8(sourceVec, mm_setzero_si128())
|
|
||||||
if mm_movemask_epi8(eqZer0) != 0xffff:
|
|
||||||
let eq255 = mm_cmpeq_epi8(sourceVec, vec255)
|
|
||||||
if (mm_movemask_epi8(eq255) and 0x8888) == 0x8888:
|
|
||||||
mm_storeu_si128(a.data[a.dataIndex(x + q, y)].addr, sourceVec)
|
|
||||||
else:
|
|
||||||
let
|
|
||||||
backdropIdx = a.dataIndex(x + q, y)
|
|
||||||
backdropVec = mm_loadu_si128(a.data[backdropIdx].addr)
|
|
||||||
mm_storeu_si128(
|
|
||||||
a.data[backdropIdx].addr,
|
|
||||||
blendNormalSimd(backdropVec, sourceVec)
|
|
||||||
)
|
|
||||||
x += 16
|
|
||||||
sx += 16
|
|
||||||
of MaskBlend:
|
|
||||||
let vec255 = mm_set1_epi32(cast[int32](uint32.high))
|
|
||||||
for _ in 0 ..< (xStop - xStart) div 16:
|
|
||||||
for q in [0, 4, 8, 12]:
|
|
||||||
let
|
|
||||||
sourceVec = mm_loadu_si128(b.data[b.dataIndex(sx + q, sy)].addr)
|
|
||||||
eqZer0 = mm_cmpeq_epi8(sourceVec, mm_setzero_si128())
|
|
||||||
if mm_movemask_epi8(eqZer0) == 0xffff:
|
|
||||||
mm_storeu_si128(
|
|
||||||
a.data[a.dataIndex(x + q, y)].addr,
|
|
||||||
mm_setzero_si128()
|
|
||||||
)
|
|
||||||
elif mm_movemask_epi8(mm_cmpeq_epi8(sourceVec, vec255)) != 0xffff:
|
|
||||||
let backdropVec = mm_loadu_si128(a.data[a.dataIndex(x + q, y)].addr)
|
|
||||||
mm_storeu_si128(
|
|
||||||
a.data[a.dataIndex(x + q, y)].addr,
|
|
||||||
blendMaskSimd(backdropVec, sourceVec)
|
|
||||||
)
|
|
||||||
x += 16
|
|
||||||
sx += 16
|
|
||||||
else:
|
|
||||||
when type(a) is Image:
|
|
||||||
if blendMode.hasSimdBlender():
|
|
||||||
let blenderSimd = blendMode.blenderSimd()
|
|
||||||
for _ in 0 ..< (xStop - xStart) div 16:
|
|
||||||
for q in [0, 4, 8, 12]:
|
|
||||||
let
|
|
||||||
backdrop = mm_loadu_si128(a.data[a.dataIndex(x + q, y)].addr)
|
|
||||||
source = mm_loadu_si128(b.data[b.dataIndex(sx + q, sy)].addr)
|
|
||||||
mm_storeu_si128(
|
|
||||||
a.data[a.dataIndex(x + q, y)].addr,
|
|
||||||
blenderSimd(backdrop, source)
|
|
||||||
)
|
|
||||||
x += 16
|
|
||||||
sx += 16
|
|
||||||
|
|
||||||
var srcPos = p + dx * x.float32 + dy * y.float32
|
|
||||||
srcPos = vec2(
|
|
||||||
clamp(srcPos.x, 0, b.width.float32),
|
|
||||||
clamp(srcPos.y, 0, b.height.float32)
|
|
||||||
)
|
|
||||||
|
|
||||||
case blendMode:
|
|
||||||
of OverwriteBlend:
|
|
||||||
for x in x ..< xStop:
|
|
||||||
let
|
|
||||||
samplePos = ivec2((srcPos.x - h).int32, (srcPos.y - h).int32)
|
|
||||||
source = b.unsafe[samplePos.x, samplePos.y]
|
|
||||||
if source.a > 0:
|
|
||||||
a.unsafe[x, y] = source
|
|
||||||
srcPos += dx
|
|
||||||
of NormalBlend:
|
|
||||||
for x in x ..< xStop:
|
|
||||||
let
|
|
||||||
samplePos = ivec2((srcPos.x - h).int32, (srcPos.y - h).int32)
|
|
||||||
source = b.unsafe[samplePos.x, samplePos.y]
|
|
||||||
if source.a > 0:
|
|
||||||
if source.a == 255:
|
|
||||||
a.unsafe[x, y] = source
|
|
||||||
else:
|
|
||||||
let backdrop = a.unsafe[x, y]
|
|
||||||
a.unsafe[x, y] = blendNormal(backdrop, source)
|
|
||||||
srcPos += dx
|
|
||||||
of MaskBlend:
|
|
||||||
for x in x ..< xStop:
|
|
||||||
let
|
|
||||||
samplePos = ivec2((srcPos.x - h).int32, (srcPos.y - h).int32)
|
|
||||||
source = b.unsafe[samplePos.x, samplePos.y]
|
|
||||||
if source.a == 0:
|
|
||||||
a.unsafe[x, y] = rgbx(0, 0, 0, 0)
|
|
||||||
elif source.a != 255:
|
|
||||||
let backdrop = a.unsafe[x, y]
|
|
||||||
a.unsafe[x, y] = blendMask(backdrop, source)
|
|
||||||
srcPos += dx
|
|
||||||
else:
|
|
||||||
for x in x ..< xStop:
|
|
||||||
let
|
|
||||||
samplePos = ivec2((srcPos.x - h).int32, (srcPos.y - h).int32)
|
|
||||||
backdrop = a.unsafe[x, y]
|
|
||||||
sample = b.unsafe[samplePos.x, samplePos.y]
|
|
||||||
blended = blender(backdrop, sample)
|
|
||||||
a.unsafe[x, y] = blended
|
|
||||||
srcPos += dx
|
|
||||||
|
|
||||||
if blendMode == MaskBlend:
|
|
||||||
if a.width - xStop > 0:
|
|
||||||
zeroMem(a.data[a.dataIndex(xStop, y)].addr, 4 * (a.width - xStop))
|
|
||||||
|
|
||||||
if blendMode == MaskBlend:
|
|
||||||
if a.height - yMax > 0:
|
|
||||||
zeroMem(a.data[a.dataIndex(0, yMax)].addr, 4 * a.width * (a.height - yMax))
|
|
||||||
|
|
||||||
proc draw*(
|
|
||||||
a, b: Image, transform = mat3(), blendMode = NormalBlend
|
|
||||||
) {.inline, raises: [PixieError].} =
|
|
||||||
## Draws one image onto another using matrix with color blending.
|
|
||||||
a.drawUber(b, transform, blendMode)
|
|
||||||
|
|
||||||
proc drawTiled*(
|
proc drawTiled*(
|
||||||
dst, src: Image, mat: Mat3, blendMode = NormalBlend
|
dst, src: Image, mat: Mat3, blendMode = NormalBlend
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import avx, chroma, internal, nimsimd/avx2, pixie/common, vmath
|
import avx, chroma, internal, nimsimd/avx2, pixie/blends, pixie/common, vmath
|
||||||
|
|
||||||
when defined(gcc) or defined(clang):
|
when defined(gcc) or defined(clang):
|
||||||
{.localPassc: "-mavx2".}
|
{.localPassc: "-mavx2".}
|
||||||
|
@ -107,6 +107,10 @@ proc toPremultipliedAlphaAvx2*(data: var seq[ColorRGBA | ColorRGBX]) {.simd.} =
|
||||||
|
|
||||||
let
|
let
|
||||||
alphaMask = mm256_set1_epi32(cast[int32](0xff000000))
|
alphaMask = mm256_set1_epi32(cast[int32](0xff000000))
|
||||||
|
shuffleControl = mm256_set_epi8(
|
||||||
|
15, -1, 15, -1, 11, -1, 11, -1, 7, -1, 7, -1, 3, -1, 3, -1,
|
||||||
|
15, -1, 15, -1, 11, -1, 11, -1, 7, -1, 7, -1, 3, -1, 3, -1
|
||||||
|
)
|
||||||
oddMask = mm256_set1_epi16(0xff00)
|
oddMask = mm256_set1_epi16(0xff00)
|
||||||
vec128 = mm256_set1_epi16(128)
|
vec128 = mm256_set1_epi16(128)
|
||||||
hiMask = mm256_set1_epi16(255 shl 8)
|
hiMask = mm256_set1_epi16(255 shl 8)
|
||||||
|
@ -118,7 +122,7 @@ proc toPremultipliedAlphaAvx2*(data: var seq[ColorRGBA | ColorRGBX]) {.simd.} =
|
||||||
eq = mm256_cmpeq_epi8(values, alphaMask)
|
eq = mm256_cmpeq_epi8(values, alphaMask)
|
||||||
if (mm256_movemask_epi8(eq) and 0x88888888) != 0x88888888:
|
if (mm256_movemask_epi8(eq) and 0x88888888) != 0x88888888:
|
||||||
let
|
let
|
||||||
evenMultiplier = mm256_or_si256(alpha, mm256_srli_epi32(alpha, 16))
|
evenMultiplier = mm256_shuffle_epi8(alpha, shuffleControl)
|
||||||
oddMultiplier = mm256_or_si256(evenMultiplier, alphaMask)
|
oddMultiplier = mm256_or_si256(evenMultiplier, alphaMask)
|
||||||
var
|
var
|
||||||
colorsEven = mm256_slli_epi16(values, 8)
|
colorsEven = mm256_slli_epi16(values, 8)
|
||||||
|
@ -376,5 +380,100 @@ proc minifyBy2Avx2*(image: Image, power = 1): Image {.simd.} =
|
||||||
# Set src as this result for if we do another power
|
# Set src as this result for if we do another power
|
||||||
src = result
|
src = result
|
||||||
|
|
||||||
|
proc blitLineNormalAvx2*(
|
||||||
|
a, b: ptr UncheckedArray[ColorRGBX], len: int
|
||||||
|
) {.simd.} =
|
||||||
|
let
|
||||||
|
alphaMask = mm256_set1_epi32(cast[int32](0xff000000))
|
||||||
|
oddMask = mm256_set1_epi16(cast[int16](0xff00))
|
||||||
|
div255 = mm256_set1_epi16(cast[int16](0x8081))
|
||||||
|
vec255 = mm256_set1_epi8(255)
|
||||||
|
vecAlpha255 = mm256_set1_epi32(cast[int32]([0.uint8, 255, 0, 255]))
|
||||||
|
shuffleControl = mm256_set_epi8(
|
||||||
|
15, -1, 15, -1, 11, -1, 11, -1, 7, -1, 7, -1, 3, -1, 3, -1,
|
||||||
|
15, -1, 15, -1, 11, -1, 11, -1, 7, -1, 7, -1, 3, -1, 3, -1
|
||||||
|
)
|
||||||
|
|
||||||
|
var i: int
|
||||||
|
while i < len - 8:
|
||||||
|
let
|
||||||
|
source = mm256_loadu_si256(b[i].addr)
|
||||||
|
eq255 = mm256_cmpeq_epi8(source, vec255)
|
||||||
|
if (mm256_movemask_epi8(eq255) and 0x88888888) == 0x88888888: # Opaque source
|
||||||
|
mm256_storeu_si256(a[i].addr, source)
|
||||||
|
else:
|
||||||
|
let backdrop = mm256_loadu_si256(a[i].addr)
|
||||||
|
|
||||||
|
var
|
||||||
|
sourceAlpha = mm256_and_si256(source, alphaMask)
|
||||||
|
backdropEven = mm256_slli_epi16(backdrop, 8)
|
||||||
|
backdropOdd = mm256_and_si256(backdrop, oddMask)
|
||||||
|
|
||||||
|
sourceAlpha = mm256_shuffle_epi8(sourceAlpha, shuffleControl)
|
||||||
|
|
||||||
|
let multiplier = mm256_sub_epi32(vecAlpha255, sourceAlpha)
|
||||||
|
|
||||||
|
backdropEven = mm256_mulhi_epu16(backdropEven, multiplier)
|
||||||
|
backdropOdd = mm256_mulhi_epu16(backdropOdd, multiplier)
|
||||||
|
backdropEven = mm256_srli_epi16(mm256_mulhi_epu16(backdropEven, div255), 7)
|
||||||
|
backdropOdd = mm256_srli_epi16(mm256_mulhi_epu16(backdropOdd, div255), 7)
|
||||||
|
|
||||||
|
let added = mm256_add_epi8(
|
||||||
|
source,
|
||||||
|
mm256_or_si256(backdropEven, mm256_slli_epi16(backdropOdd, 8))
|
||||||
|
)
|
||||||
|
|
||||||
|
mm256_storeu_si256(a[i].addr, added)
|
||||||
|
|
||||||
|
i += 8
|
||||||
|
|
||||||
|
for i in i ..< len:
|
||||||
|
a[i] = blendNormal(a[i], b[i])
|
||||||
|
|
||||||
|
proc blitLineMaskAvx2*(
|
||||||
|
a, b: ptr UncheckedArray[ColorRGBX], len: int
|
||||||
|
) {.simd.} =
|
||||||
|
let
|
||||||
|
alphaMask = mm256_set1_epi32(cast[int32](0xff000000))
|
||||||
|
oddMask = mm256_set1_epi16(cast[int16](0xff00))
|
||||||
|
div255 = mm256_set1_epi16(cast[int16](0x8081))
|
||||||
|
vec255 = mm256_set1_epi8(255)
|
||||||
|
shuffleControl = mm256_set_epi8(
|
||||||
|
15, -1, 15, -1, 11, -1, 11, -1, 7, -1, 7, -1, 3, -1, 3, -1,
|
||||||
|
15, -1, 15, -1, 11, -1, 11, -1, 7, -1, 7, -1, 3, -1, 3, -1
|
||||||
|
)
|
||||||
|
|
||||||
|
var i: int
|
||||||
|
while i < len - 8:
|
||||||
|
let
|
||||||
|
source = mm256_loadu_si256(b[i].addr)
|
||||||
|
eq255 = mm256_cmpeq_epi8(source, vec255)
|
||||||
|
if (mm256_movemask_epi8(eq255) and 0x88888888) == 0x88888888: # Opaque source
|
||||||
|
discard
|
||||||
|
else:
|
||||||
|
let backdrop = mm256_loadu_si256(a[i].addr)
|
||||||
|
|
||||||
|
var
|
||||||
|
sourceAlpha = mm256_and_si256(source, alphaMask)
|
||||||
|
backdropEven = mm256_slli_epi16(backdrop, 8)
|
||||||
|
backdropOdd = mm256_and_si256(backdrop, oddMask)
|
||||||
|
|
||||||
|
sourceAlpha = mm256_shuffle_epi8(sourceAlpha, shuffleControl)
|
||||||
|
|
||||||
|
backdropEven = mm256_mulhi_epu16(backdropEven, sourceAlpha)
|
||||||
|
backdropOdd = mm256_mulhi_epu16(backdropOdd, sourceAlpha)
|
||||||
|
backdropEven = mm256_srli_epi16(mm256_mulhi_epu16(backdropEven, div255), 7)
|
||||||
|
backdropOdd = mm256_srli_epi16(mm256_mulhi_epu16(backdropOdd, div255), 7)
|
||||||
|
|
||||||
|
mm256_storeu_si256(
|
||||||
|
a[i].addr,
|
||||||
|
mm256_or_si256(backdropEven, mm256_slli_epi16(backdropOdd, 8))
|
||||||
|
)
|
||||||
|
|
||||||
|
i += 8
|
||||||
|
|
||||||
|
for i in i ..< len:
|
||||||
|
a[i] = blendMask(a[i], b[i])
|
||||||
|
|
||||||
when defined(release):
|
when defined(release):
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import chroma, internal, nimsimd/sse2, pixie/common, vmath
|
import chroma, internal, nimsimd/sse2, pixie/blends, pixie/common, vmath
|
||||||
|
|
||||||
when defined(release):
|
when defined(release):
|
||||||
{.push checks: off.}
|
{.push checks: off.}
|
||||||
|
@ -15,6 +15,56 @@ proc unpackAlphaValues*(v: M128i): M128i {.inline, raises: [].} =
|
||||||
result = mm_unpacklo_epi8(mm_setzero_si128(), v)
|
result = mm_unpacklo_epi8(mm_setzero_si128(), v)
|
||||||
result = mm_unpacklo_epi8(mm_setzero_si128(), result)
|
result = mm_unpacklo_epi8(mm_setzero_si128(), result)
|
||||||
|
|
||||||
|
proc blendNormalSimd*(backdrop, source: M128i): M128i {.inline.} =
|
||||||
|
let
|
||||||
|
alphaMask = mm_set1_epi32(cast[int32](0xff000000))
|
||||||
|
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
||||||
|
div255 = mm_set1_epi16(cast[int16](0x8081))
|
||||||
|
|
||||||
|
var
|
||||||
|
sourceAlpha = mm_and_si128(source, alphaMask)
|
||||||
|
backdropEven = mm_slli_epi16(backdrop, 8)
|
||||||
|
backdropOdd = mm_and_si128(backdrop, oddMask)
|
||||||
|
|
||||||
|
sourceAlpha = mm_or_si128(sourceAlpha, mm_srli_epi32(sourceAlpha, 16))
|
||||||
|
|
||||||
|
let k = mm_sub_epi32(
|
||||||
|
mm_set1_epi32(cast[int32]([0.uint8, 255, 0, 255])),
|
||||||
|
sourceAlpha
|
||||||
|
)
|
||||||
|
|
||||||
|
backdropEven = mm_mulhi_epu16(backdropEven, k)
|
||||||
|
backdropOdd = mm_mulhi_epu16(backdropOdd, k)
|
||||||
|
|
||||||
|
backdropEven = mm_srli_epi16(mm_mulhi_epu16(backdropEven, div255), 7)
|
||||||
|
backdropOdd = mm_srli_epi16(mm_mulhi_epu16(backdropOdd, div255), 7)
|
||||||
|
|
||||||
|
mm_add_epi8(
|
||||||
|
source,
|
||||||
|
mm_or_si128(backdropEven, mm_slli_epi16(backdropOdd, 8))
|
||||||
|
)
|
||||||
|
|
||||||
|
proc blendMaskSimd*(backdrop, source: M128i): M128i {.inline.} =
|
||||||
|
let
|
||||||
|
alphaMask = mm_set1_epi32(cast[int32](0xff000000))
|
||||||
|
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
||||||
|
div255 = mm_set1_epi16(cast[int16](0x8081))
|
||||||
|
|
||||||
|
var
|
||||||
|
sourceAlpha = mm_and_si128(source, alphaMask)
|
||||||
|
backdropEven = mm_slli_epi16(backdrop, 8)
|
||||||
|
backdropOdd = mm_and_si128(backdrop, oddMask)
|
||||||
|
|
||||||
|
sourceAlpha = mm_or_si128(sourceAlpha, mm_srli_epi32(sourceAlpha, 16))
|
||||||
|
|
||||||
|
backdropEven = mm_mulhi_epu16(backdropEven, sourceAlpha)
|
||||||
|
backdropOdd = mm_mulhi_epu16(backdropOdd, sourceAlpha)
|
||||||
|
|
||||||
|
backdropEven = mm_srli_epi16(mm_mulhi_epu16(backdropEven, div255), 7)
|
||||||
|
backdropOdd = mm_srli_epi16(mm_mulhi_epu16(backdropOdd, div255), 7)
|
||||||
|
|
||||||
|
mm_or_si128(backdropEven, mm_slli_epi16(backdropOdd, 8))
|
||||||
|
|
||||||
proc fillUnsafeSse2*(
|
proc fillUnsafeSse2*(
|
||||||
data: var seq[ColorRGBX],
|
data: var seq[ColorRGBX],
|
||||||
color: SomeColor,
|
color: SomeColor,
|
||||||
|
@ -480,18 +530,22 @@ proc magnifyBy2Sse2*(image: Image, power = 1): Image {.simd.} =
|
||||||
proc blitLineNormalSse2*(
|
proc blitLineNormalSse2*(
|
||||||
a, b: ptr UncheckedArray[ColorRGBX], len: int
|
a, b: ptr UncheckedArray[ColorRGBX], len: int
|
||||||
) {.simd.} =
|
) {.simd.} =
|
||||||
|
|
||||||
# TODO align to 16
|
|
||||||
|
|
||||||
var i = 0
|
|
||||||
while i < len - 4:
|
|
||||||
|
|
||||||
let
|
let
|
||||||
source = mm_loadu_si128(b[i].addr)
|
|
||||||
backdrop = mm_loadu_si128(a[i].addr)
|
|
||||||
alphaMask = mm_set1_epi32(cast[int32](0xff000000))
|
alphaMask = mm_set1_epi32(cast[int32](0xff000000))
|
||||||
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
||||||
div255 = mm_set1_epi16(cast[int16](0x8081))
|
div255 = mm_set1_epi16(cast[int16](0x8081))
|
||||||
|
vec255 = mm_set1_epi8(255)
|
||||||
|
vecAlpha255 = mm_set1_epi32(cast[int32]([0.uint8, 255, 0, 255]))
|
||||||
|
|
||||||
|
var i: int
|
||||||
|
while i < len - 4:
|
||||||
|
let
|
||||||
|
source = mm_loadu_si128(b[i].addr)
|
||||||
|
eq255 = mm_cmpeq_epi8(source, vec255)
|
||||||
|
if (mm_movemask_epi8(eq255) and 0x00008888) == 0x00008888: # Opaque source
|
||||||
|
mm_storeu_si128(a[i].addr, source)
|
||||||
|
else:
|
||||||
|
let backdrop = mm_loadu_si128(a[i].addr)
|
||||||
|
|
||||||
var
|
var
|
||||||
sourceAlpha = mm_and_si128(source, alphaMask)
|
sourceAlpha = mm_and_si128(source, alphaMask)
|
||||||
|
@ -500,29 +554,65 @@ proc blitLineNormalSse2*(
|
||||||
|
|
||||||
sourceAlpha = mm_or_si128(sourceAlpha, mm_srli_epi32(sourceAlpha, 16))
|
sourceAlpha = mm_or_si128(sourceAlpha, mm_srli_epi32(sourceAlpha, 16))
|
||||||
|
|
||||||
let k = mm_sub_epi32(
|
let multiplier = mm_sub_epi32(vecAlpha255, sourceAlpha)
|
||||||
mm_set1_epi32(cast[int32]([0.uint8, 255, 0, 255])),
|
|
||||||
sourceAlpha
|
|
||||||
)
|
|
||||||
|
|
||||||
backdropEven = mm_mulhi_epu16(backdropEven, k)
|
|
||||||
backdropOdd = mm_mulhi_epu16(backdropOdd, k)
|
|
||||||
|
|
||||||
|
backdropEven = mm_mulhi_epu16(backdropEven, multiplier)
|
||||||
|
backdropOdd = mm_mulhi_epu16(backdropOdd, multiplier)
|
||||||
backdropEven = mm_srli_epi16(mm_mulhi_epu16(backdropEven, div255), 7)
|
backdropEven = mm_srli_epi16(mm_mulhi_epu16(backdropEven, div255), 7)
|
||||||
backdropOdd = mm_srli_epi16(mm_mulhi_epu16(backdropOdd, div255), 7)
|
backdropOdd = mm_srli_epi16(mm_mulhi_epu16(backdropOdd, div255), 7)
|
||||||
|
|
||||||
let done = mm_add_epi8(
|
let added = mm_add_epi8(
|
||||||
source,
|
source,
|
||||||
mm_or_si128(backdropEven, mm_slli_epi16(backdropOdd, 8))
|
mm_or_si128(backdropEven, mm_slli_epi16(backdropOdd, 8))
|
||||||
)
|
)
|
||||||
|
|
||||||
mm_storeu_si128(a[i].addr, done)
|
mm_storeu_si128(a[i].addr, added)
|
||||||
|
|
||||||
i += 4
|
i += 4
|
||||||
|
|
||||||
# TODO last 1-3 pixels
|
for i in i ..< len:
|
||||||
# for i in i ..< len:
|
a[i] = blendNormal(a[i], b[i])
|
||||||
# a[i] = blendNormal(a[i], b[i])
|
|
||||||
|
proc blitLineMaskSse2*(
|
||||||
|
a, b: ptr UncheckedArray[ColorRGBX], len: int
|
||||||
|
) {.simd.} =
|
||||||
|
let
|
||||||
|
alphaMask = mm_set1_epi32(cast[int32](0xff000000))
|
||||||
|
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
||||||
|
div255 = mm_set1_epi16(cast[int16](0x8081))
|
||||||
|
vec255 = mm_set1_epi8(255)
|
||||||
|
|
||||||
|
var i: int
|
||||||
|
while i < len - 4:
|
||||||
|
let
|
||||||
|
source = mm_loadu_si128(b[i].addr)
|
||||||
|
eq255 = mm_cmpeq_epi8(source, vec255)
|
||||||
|
if (mm_movemask_epi8(eq255) and 0x00008888) == 0x00008888: # Opaque source
|
||||||
|
discard
|
||||||
|
else:
|
||||||
|
let backdrop = mm_loadu_si128(a[i].addr)
|
||||||
|
|
||||||
|
var
|
||||||
|
sourceAlpha = mm_and_si128(source, alphaMask)
|
||||||
|
backdropEven = mm_slli_epi16(backdrop, 8)
|
||||||
|
backdropOdd = mm_and_si128(backdrop, oddMask)
|
||||||
|
|
||||||
|
sourceAlpha = mm_or_si128(sourceAlpha, mm_srli_epi32(sourceAlpha, 16))
|
||||||
|
|
||||||
|
backdropEven = mm_mulhi_epu16(backdropEven, sourceAlpha)
|
||||||
|
backdropOdd = mm_mulhi_epu16(backdropOdd, sourceAlpha)
|
||||||
|
backdropEven = mm_srli_epi16(mm_mulhi_epu16(backdropEven, div255), 7)
|
||||||
|
backdropOdd = mm_srli_epi16(mm_mulhi_epu16(backdropOdd, div255), 7)
|
||||||
|
|
||||||
|
mm_storeu_si128(
|
||||||
|
a[i].addr,
|
||||||
|
mm_or_si128(backdropEven, mm_slli_epi16(backdropOdd, 8))
|
||||||
|
)
|
||||||
|
|
||||||
|
i += 4
|
||||||
|
|
||||||
|
for i in i ..< len:
|
||||||
|
a[i] = blendMask(a[i], b[i])
|
||||||
|
|
||||||
when defined(release):
|
when defined(release):
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
@ -133,17 +133,3 @@ reset()
|
||||||
timeIt "blendExcludeMask":
|
timeIt "blendExcludeMask":
|
||||||
for i in 0 ..< backdrop.data.len:
|
for i in 0 ..< backdrop.data.len:
|
||||||
backdrop.data[i] = blendExcludeMask(backdrop.data[i], source.data[i])
|
backdrop.data[i] = blendExcludeMask(backdrop.data[i], source.data[i])
|
||||||
|
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
|
||||||
import nimsimd/sse2
|
|
||||||
|
|
||||||
reset()
|
|
||||||
|
|
||||||
timeIt "blendNormal [simd]":
|
|
||||||
var i: int
|
|
||||||
while i < backdrop.data.len - 4:
|
|
||||||
let
|
|
||||||
b = mm_loadu_si128(backdrop.data[i].addr)
|
|
||||||
s = mm_loadu_si128(source.data[i].addr)
|
|
||||||
mm_storeu_si128(backdrop.data[i].addr, blendNormalSimd(b, s))
|
|
||||||
i += 4
|
|
||||||
|
|
|
@ -32,12 +32,12 @@ timeIt "isOpaque":
|
||||||
reset()
|
reset()
|
||||||
|
|
||||||
timeIt "subImage":
|
timeIt "subImage":
|
||||||
keep image.subImage(0, 0, 256, 256)
|
discard image.subImage(0, 0, 256, 256)
|
||||||
|
|
||||||
reset()
|
reset()
|
||||||
|
|
||||||
timeIt "superImage":
|
timeIt "superImage":
|
||||||
keep image.superImage(-10, -10, 2580, 1460)
|
discard image.superImage(-10, -10, 2580, 1460)
|
||||||
|
|
||||||
reset()
|
reset()
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,26 @@ block:
|
||||||
|
|
||||||
timeIt "big-on-bigger NormalBlend":
|
timeIt "big-on-bigger NormalBlend":
|
||||||
a.draw(b, translate(vec2(25, 25)), NormalBlend)
|
a.draw(b, translate(vec2(25, 25)), NormalBlend)
|
||||||
keep(b)
|
|
||||||
|
block:
|
||||||
|
let
|
||||||
|
a = newImage(1000, 1000)
|
||||||
|
b = newImage(500, 500)
|
||||||
|
a.fill(rgba(255, 0, 0, 255))
|
||||||
|
b.fill(rgba(0, 255, 0, 255))
|
||||||
|
|
||||||
|
timeIt "big-on-bigger MaskBlend":
|
||||||
|
a.draw(b, translate(vec2(25, 25)), MaskBlend)
|
||||||
|
|
||||||
|
block:
|
||||||
|
let
|
||||||
|
a = newImage(1000, 1000)
|
||||||
|
b = newImage(500, 500)
|
||||||
|
a.fill(rgba(255, 0, 0, 255))
|
||||||
|
b.fill(rgba(0, 255, 0, 255))
|
||||||
|
|
||||||
|
timeIt "big-on-bigger OverwriteBlend":
|
||||||
|
a.draw(b, translate(vec2(25, 25)), OverwriteBlend)
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
|
@ -20,7 +39,6 @@ block:
|
||||||
|
|
||||||
timeIt "scale x0.5":
|
timeIt "scale x0.5":
|
||||||
a.draw(b, translate(vec2(25, 25)) * scale(vec2(0.5, 0.5)), NormalBlend)
|
a.draw(b, translate(vec2(25, 25)) * scale(vec2(0.5, 0.5)), NormalBlend)
|
||||||
keep(b)
|
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
|
@ -31,7 +49,6 @@ block:
|
||||||
|
|
||||||
timeIt "scale x2":
|
timeIt "scale x2":
|
||||||
a.draw(b, translate(vec2(25, 25)) * scale(vec2(2, 2)), NormalBlend)
|
a.draw(b, translate(vec2(25, 25)) * scale(vec2(2, 2)), NormalBlend)
|
||||||
keep(b)
|
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
|
@ -42,7 +59,6 @@ block:
|
||||||
|
|
||||||
timeIt "smooth x-translate":
|
timeIt "smooth x-translate":
|
||||||
a.draw(b, translate(vec2(25.2, 0)), NormalBlend)
|
a.draw(b, translate(vec2(25.2, 0)), NormalBlend)
|
||||||
keep(b)
|
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
|
@ -53,7 +69,6 @@ block:
|
||||||
|
|
||||||
timeIt "smooth y-translate":
|
timeIt "smooth y-translate":
|
||||||
a.draw(b, translate(vec2(0, 25.2)), NormalBlend)
|
a.draw(b, translate(vec2(0, 25.2)), NormalBlend)
|
||||||
keep(b)
|
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
|
@ -64,7 +79,6 @@ block:
|
||||||
|
|
||||||
timeIt "smooth translate":
|
timeIt "smooth translate":
|
||||||
a.draw(b, translate(vec2(25.2, 25.2)), NormalBlend)
|
a.draw(b, translate(vec2(25.2, 25.2)), NormalBlend)
|
||||||
keep(b)
|
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
|
@ -75,7 +89,6 @@ block:
|
||||||
|
|
||||||
timeIt "smooth rotate 45":
|
timeIt "smooth rotate 45":
|
||||||
a.draw(b, translate(vec2(0, 500)) * rotate(toRadians(45)), NormalBlend)
|
a.draw(b, translate(vec2(0, 500)) * rotate(toRadians(45)), NormalBlend)
|
||||||
keep(b)
|
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
|
@ -85,14 +98,12 @@ block:
|
||||||
timeIt "shadow no offset":
|
timeIt "shadow no offset":
|
||||||
b.fill(rgba(0, 0, 0, 255))
|
b.fill(rgba(0, 0, 0, 255))
|
||||||
a.draw(b, translate(vec2(25, 25)))
|
a.draw(b, translate(vec2(25, 25)))
|
||||||
|
discard a.shadow(
|
||||||
let shadow = a.shadow(
|
|
||||||
offset = vec2(0, 0),
|
offset = vec2(0, 0),
|
||||||
spread = 10,
|
spread = 10,
|
||||||
blur = 10,
|
blur = 10,
|
||||||
color = rgba(0, 0, 0, 255)
|
color = rgba(0, 0, 0, 255)
|
||||||
)
|
)
|
||||||
keep(shadow)
|
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
|
@ -102,11 +113,9 @@ block:
|
||||||
timeIt "shadow with offset":
|
timeIt "shadow with offset":
|
||||||
b.fill(rgba(0, 0, 0, 255))
|
b.fill(rgba(0, 0, 0, 255))
|
||||||
a.draw(b, translate(vec2(25, 25)))
|
a.draw(b, translate(vec2(25, 25)))
|
||||||
|
discard a.shadow(
|
||||||
let shadow = a.shadow(
|
|
||||||
offset = vec2(10, 10),
|
offset = vec2(10, 10),
|
||||||
spread = 10,
|
spread = 10,
|
||||||
blur = 10,
|
blur = 10,
|
||||||
color = rgba(0, 0, 0, 255)
|
color = rgba(0, 0, 0, 255)
|
||||||
)
|
)
|
||||||
keep(shadow)
|
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
import benchy, pixie, pixie/images {.all.}, strformat, xrays
|
|
||||||
|
|
||||||
block:
|
|
||||||
let
|
|
||||||
a = newImage(1000, 1000)
|
|
||||||
b = newImage(500, 500)
|
|
||||||
a.fill(rgba(255, 0, 0, 255))
|
|
||||||
b.fill(rgba(0, 255, 0, 255))
|
|
||||||
|
|
||||||
a.drawCorrect(b, translate(vec2(250, 250)), blendMode = OverwriteBlend)
|
|
||||||
a.writeFile("tests/images/rotate0.png")
|
|
||||||
|
|
||||||
block:
|
|
||||||
let
|
|
||||||
a = newImage(1000, 1000)
|
|
||||||
b = newImage(500, 500)
|
|
||||||
|
|
||||||
timeIt "drawCorrect":
|
|
||||||
a.fill(rgba(255, 0, 0, 255))
|
|
||||||
b.fill(rgba(0, 255, 0, 255))
|
|
||||||
|
|
||||||
a.drawCorrect(b, translate(vec2(250, 250)), blendMode = OverwriteBlend)
|
|
||||||
|
|
||||||
block:
|
|
||||||
let
|
|
||||||
a = newImage(1000, 1000)
|
|
||||||
b = newImage(500, 500)
|
|
||||||
|
|
||||||
timeIt "draw":
|
|
||||||
a.fill(rgba(255, 0, 0, 255))
|
|
||||||
b.fill(rgba(0, 255, 0, 255))
|
|
||||||
|
|
||||||
a.draw(b, translate(vec2(250, 250)), blendMode = OverwriteBlend)
|
|
|
@ -3,7 +3,7 @@ import benchy, pixie
|
||||||
let pathStr = "m57.611-8.591c-1.487,1.851-4.899,4.42-1.982,6.348,0.194,0.129,0.564,0.133,0.737-0.001,2.021-1.565,4.024-2.468,6.46-3.05,0.124-0.029,0.398,0.438,0.767,0.277,1.613-0.703,3.623-0.645,4.807-1.983,3.767,0.224,7.332-0.892,10.723-2.2,1.161-0.448,2.431-1.007,3.632-1.509,1.376-0.576,2.58-1.504,3.692-2.645,0.133-0.136,0.487-0.046,0.754-0.046-0.04-0.863,0.922-0.99,1.169-1.612,0.092-0.232-0.058-0.628,0.075-0.73,2.138-1.63,3.058-3.648,1.889-6.025-0.285-0.578-0.534-1.196-1.1-1.672-1.085-0.911-2.187-0.057-3.234-0.361-0.159,0.628-0.888,0.456-1.274,0.654-0.859,0.439-2.192-0.146-3.051,0.292-1.362,0.695-2.603,0.864-4.025,1.241-0.312,0.082-1.09-0.014-1.25,0.613-0.134-0.134-0.282-0.368-0.388-0.346-1.908,0.396-3.168,0.61-4.469,2.302-0.103,0.133-0.545-0.046-0.704,0.089-0.957,0.808-1.362,2.042-2.463,2.714-0.201,0.123-0.553-0.045-0.747,0.084-0.646,0.431-1.013,1.072-1.655,1.519-0.329,0.229-0.729-0.096-0.697-0.352,0.245-1.947,0.898-3.734,0.323-5.61,2.077-2.52,4.594-4.469,6.4-7.2,0.015-2.166,0.707-4.312,0.594-6.389-0.01-0.193-0.298-0.926-0.424-1.273-0.312-0.854,0.594-1.92-0.25-2.644-1.404-1.203-2.696-0.327-3.52,1.106-1.838,0.39-3.904,1.083-5.482-0.151-1.007-0.787-1.585-1.693-2.384-2.749-0.985-1.302-0.65-2.738-0.58-4.302,0.006-0.128-0.309-0.264-0.309-0.398,0.001-0.135,0.221-0.266,0.355-0.4-0.706-0.626-0.981-1.684-2-2,0.305-1.092-0.371-1.976-1.242-2.278-1.995-0.691-3.672,1.221-5.564,1.294-0.514,0.019-0.981-1.019-1.63-1.344-0.432-0.216-1.136-0.249-1.498,0.017-0.688,0.504-1.277,0.618-2.035,0.823-1.617,0.436-2.895,1.53-4.375,2.385-1.485,0.857-2.44,2.294-3.52,3.614-0.941,1.152-1.077,3.566,0.343,4.066,1.843,0.65,3.147-2.053,5.113-1.727,0.312,0.051,0.518,0.362,0.408,0.75,0.389,0.109,0.607-0.12,0.8-0.4,0.858,1.019,2.022,1.356,2.96,2.229,0.97,0.904,2.716,0.486,3.731,1.483,1.529,1.502,0.97,4.183,2.909,5.488-0.586,1.313-1.193,2.59-1.528,4.017-0.282,1.206,0.712,2.403,1.923,2.312,1.258-0.094,1.52-0.853,2.005-1.929,0.267,0.267,0.736,0.564,0.695,0.78-0.457,2.387-1.484,4.38-1.942,6.811-0.059,0.317-0.364,0.519-0.753,0.409-0.468,4.149-4.52,6.543-7.065,9.708-0.403,0.502-0.407,1.751,0.002,2.154,1.403,1.387,3.363-0.159,5.063-0.662,0.213-1.206,1.072-2.148,2.404-2.092,0.256,0.01,0.491-0.532,0.815-0.662,0.348-0.138,0.85,0.086,1.136-0.112,1.729-1.195,3.137-2.301,4.875-3.49,0.192-0.131,0.536,0.028,0.752-0.08,0.325-0.162,0.512-0.549,0.835-0.734,0.348-0.2,0.59,0.09,0.783,0.37-0.646,0.349-0.65,1.306-1.232,1.508-0.775,0.268-1.336,0.781-2.01,1.228-0.292,0.193-0.951-0.055-1.055,0.124-0.598,1.028-1.782,1.466-2.492,2.349z"
|
let pathStr = "m57.611-8.591c-1.487,1.851-4.899,4.42-1.982,6.348,0.194,0.129,0.564,0.133,0.737-0.001,2.021-1.565,4.024-2.468,6.46-3.05,0.124-0.029,0.398,0.438,0.767,0.277,1.613-0.703,3.623-0.645,4.807-1.983,3.767,0.224,7.332-0.892,10.723-2.2,1.161-0.448,2.431-1.007,3.632-1.509,1.376-0.576,2.58-1.504,3.692-2.645,0.133-0.136,0.487-0.046,0.754-0.046-0.04-0.863,0.922-0.99,1.169-1.612,0.092-0.232-0.058-0.628,0.075-0.73,2.138-1.63,3.058-3.648,1.889-6.025-0.285-0.578-0.534-1.196-1.1-1.672-1.085-0.911-2.187-0.057-3.234-0.361-0.159,0.628-0.888,0.456-1.274,0.654-0.859,0.439-2.192-0.146-3.051,0.292-1.362,0.695-2.603,0.864-4.025,1.241-0.312,0.082-1.09-0.014-1.25,0.613-0.134-0.134-0.282-0.368-0.388-0.346-1.908,0.396-3.168,0.61-4.469,2.302-0.103,0.133-0.545-0.046-0.704,0.089-0.957,0.808-1.362,2.042-2.463,2.714-0.201,0.123-0.553-0.045-0.747,0.084-0.646,0.431-1.013,1.072-1.655,1.519-0.329,0.229-0.729-0.096-0.697-0.352,0.245-1.947,0.898-3.734,0.323-5.61,2.077-2.52,4.594-4.469,6.4-7.2,0.015-2.166,0.707-4.312,0.594-6.389-0.01-0.193-0.298-0.926-0.424-1.273-0.312-0.854,0.594-1.92-0.25-2.644-1.404-1.203-2.696-0.327-3.52,1.106-1.838,0.39-3.904,1.083-5.482-0.151-1.007-0.787-1.585-1.693-2.384-2.749-0.985-1.302-0.65-2.738-0.58-4.302,0.006-0.128-0.309-0.264-0.309-0.398,0.001-0.135,0.221-0.266,0.355-0.4-0.706-0.626-0.981-1.684-2-2,0.305-1.092-0.371-1.976-1.242-2.278-1.995-0.691-3.672,1.221-5.564,1.294-0.514,0.019-0.981-1.019-1.63-1.344-0.432-0.216-1.136-0.249-1.498,0.017-0.688,0.504-1.277,0.618-2.035,0.823-1.617,0.436-2.895,1.53-4.375,2.385-1.485,0.857-2.44,2.294-3.52,3.614-0.941,1.152-1.077,3.566,0.343,4.066,1.843,0.65,3.147-2.053,5.113-1.727,0.312,0.051,0.518,0.362,0.408,0.75,0.389,0.109,0.607-0.12,0.8-0.4,0.858,1.019,2.022,1.356,2.96,2.229,0.97,0.904,2.716,0.486,3.731,1.483,1.529,1.502,0.97,4.183,2.909,5.488-0.586,1.313-1.193,2.59-1.528,4.017-0.282,1.206,0.712,2.403,1.923,2.312,1.258-0.094,1.52-0.853,2.005-1.929,0.267,0.267,0.736,0.564,0.695,0.78-0.457,2.387-1.484,4.38-1.942,6.811-0.059,0.317-0.364,0.519-0.753,0.409-0.468,4.149-4.52,6.543-7.065,9.708-0.403,0.502-0.407,1.751,0.002,2.154,1.403,1.387,3.363-0.159,5.063-0.662,0.213-1.206,1.072-2.148,2.404-2.092,0.256,0.01,0.491-0.532,0.815-0.662,0.348-0.138,0.85,0.086,1.136-0.112,1.729-1.195,3.137-2.301,4.875-3.49,0.192-0.131,0.536,0.028,0.752-0.08,0.325-0.162,0.512-0.549,0.835-0.734,0.348-0.2,0.59,0.09,0.783,0.37-0.646,0.349-0.65,1.306-1.232,1.508-0.775,0.268-1.336,0.781-2.01,1.228-0.292,0.193-0.951-0.055-1.055,0.124-0.598,1.028-1.782,1.466-2.492,2.349z"
|
||||||
|
|
||||||
timeIt "parsePath":
|
timeIt "parsePath":
|
||||||
keep parsePath(pathStr)
|
discard parsePath(pathStr)
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let path = parsePath("""
|
let path = parsePath("""
|
||||||
|
|
|
@ -3,4 +3,4 @@ import benchy, pixie/fileformats/qoi
|
||||||
let data = readFile("tests/fileformats/qoi/testcard_rgba.qoi")
|
let data = readFile("tests/fileformats/qoi/testcard_rgba.qoi")
|
||||||
|
|
||||||
timeIt "pixie decode":
|
timeIt "pixie decode":
|
||||||
keep decodeQoi(data)
|
discard decodeQoi(data)
|
||||||
|
|
|
@ -2,30 +2,26 @@ import pixie, random
|
||||||
|
|
||||||
randomize()
|
randomize()
|
||||||
|
|
||||||
for i in 0 ..< 25:
|
for i in 0 ..< 250:
|
||||||
let a = newImage(rand(1 .. 20), rand(1 .. 20))
|
let a = newImage(rand(1 .. 20), rand(1 .. 20))
|
||||||
for j in 0 ..< 25:
|
for j in 0 ..< 25:
|
||||||
let b = newImage(rand(1 .. 20), rand(1 .. 20))
|
let b = newImage(rand(1 .. 20), rand(1 .. 20))
|
||||||
|
|
||||||
let
|
let translation = vec2(rand(-25..25).float32, rand(-25..25).float32)
|
||||||
translation = vec2(rand(25.0), rand(25.0)) - vec2(5, 5)
|
|
||||||
rotation = rand(2 * PI).float32
|
|
||||||
|
|
||||||
echo a, " ", b, " ", translation, " ", rotation
|
echo a, " ", b, " ", translation
|
||||||
|
|
||||||
a.draw(b, translate(vec2(translation.x.trunc, translation.y.trunc)))
|
a.draw(b, translate(vec2(translation.x.trunc, translation.y.trunc)))
|
||||||
a.draw(b, translate(translation) * rotate(rotation))
|
a.draw(b, translate(translation))
|
||||||
|
|
||||||
for i in 0 ..< 25:
|
for i in 0 ..< 250:
|
||||||
let a = newImage(rand(1 .. 2000), rand(1 .. 2000))
|
let a = newImage(rand(1 .. 2000), rand(1 .. 2000))
|
||||||
for j in 0 ..< 25:
|
for j in 0 ..< 25:
|
||||||
let b = newImage(rand(1 .. 1000), rand(1 .. 1000))
|
let b = newImage(rand(1 .. 1000), rand(1 .. 1000))
|
||||||
|
|
||||||
let
|
let translation = vec2(rand(-2500..2500).float32, rand(-2500..2500).float32)
|
||||||
translation = vec2(rand(2500.0), rand(2500.0)) - vec2(500, 500)
|
|
||||||
rotation = rand(2 * PI).float32
|
|
||||||
|
|
||||||
echo a, " ", b, " ", translation, " ", rotation
|
echo a, " ", b, " ", translation
|
||||||
|
|
||||||
a.draw(b, translate(vec2(translation.x.trunc, translation.y.trunc)))
|
a.draw(b, translate(vec2(translation.x.trunc, translation.y.trunc)))
|
||||||
a.draw(b, translate(translation) * rotate(rotation))
|
a.draw(b, translate(translation))
|
||||||
|
|
31
tests/fuzz_image_draw_smooth.nim
Normal file
31
tests/fuzz_image_draw_smooth.nim
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import pixie, random
|
||||||
|
|
||||||
|
randomize()
|
||||||
|
|
||||||
|
for i in 0 ..< 25:
|
||||||
|
let a = newImage(rand(1 .. 20), rand(1 .. 20))
|
||||||
|
for j in 0 ..< 25:
|
||||||
|
let b = newImage(rand(1 .. 20), rand(1 .. 20))
|
||||||
|
|
||||||
|
let
|
||||||
|
translation = vec2(rand(25.0), rand(25.0)) - vec2(5, 5)
|
||||||
|
rotation = rand(2 * PI).float32
|
||||||
|
|
||||||
|
echo a, " ", b, " ", translation, " ", rotation
|
||||||
|
|
||||||
|
a.draw(b, translate(vec2(translation.x.trunc, translation.y.trunc)))
|
||||||
|
a.draw(b, translate(translation) * rotate(rotation))
|
||||||
|
|
||||||
|
for i in 0 ..< 25:
|
||||||
|
let a = newImage(rand(1 .. 2000), rand(1 .. 2000))
|
||||||
|
for j in 0 ..< 25:
|
||||||
|
let b = newImage(rand(1 .. 1000), rand(1 .. 1000))
|
||||||
|
|
||||||
|
let
|
||||||
|
translation = vec2(rand(2500.0), rand(2500.0)) - vec2(500, 500)
|
||||||
|
rotation = rand(2 * PI).float32
|
||||||
|
|
||||||
|
echo a, " ", b, " ", translation, " ", rotation
|
||||||
|
|
||||||
|
a.draw(b, translate(vec2(translation.x.trunc, translation.y.trunc)))
|
||||||
|
a.draw(b, translate(translation) * rotate(rotation))
|
BIN
tests/images/maskClearsOnDraw0.png
Normal file
BIN
tests/images/maskClearsOnDraw0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 332 B |
BIN
tests/images/maskClearsOnDraw1.png
Normal file
BIN
tests/images/maskClearsOnDraw1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 324 B |
BIN
tests/images/maskClearsOnDraw2.png
Normal file
BIN
tests/images/maskClearsOnDraw2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 318 B |
BIN
tests/images/maskClearsOnDraw3.png
Normal file
BIN
tests/images/maskClearsOnDraw3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 319 B |
BIN
tests/images/maskClearsOnDraw4.png
Normal file
BIN
tests/images/maskClearsOnDraw4.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 324 B |
|
@ -307,3 +307,54 @@ block:
|
||||||
a.draw(b, translate(translation))
|
a.draw(b, translate(translation))
|
||||||
|
|
||||||
a.xray("tests/images/fillOptimization2.png")
|
a.xray("tests/images/fillOptimization2.png")
|
||||||
|
|
||||||
|
block:
|
||||||
|
let transforms = [
|
||||||
|
translate(vec2(0, 0)),
|
||||||
|
translate(vec2(50, -50)),
|
||||||
|
translate(vec2(50, 50)),
|
||||||
|
translate(vec2(-50, 50)),
|
||||||
|
translate(vec2(-50, -50))
|
||||||
|
]
|
||||||
|
|
||||||
|
let path = newPath()
|
||||||
|
path.rect(10, 10, 80, 80)
|
||||||
|
|
||||||
|
let mask = newImage(100, 100)
|
||||||
|
mask.fillPath(path, color(1, 1, 1, 1))
|
||||||
|
|
||||||
|
let a = newImage(100, 100)
|
||||||
|
|
||||||
|
for i, transform in transforms:
|
||||||
|
a.fill(color(0, 0, 1, 1))
|
||||||
|
a.draw(mask, transform, blendMode = MaskBlend)
|
||||||
|
a.xray("tests/images/maskClearsOnDraw" & $i & ".png")
|
||||||
|
|
||||||
|
block:
|
||||||
|
let transforms = [
|
||||||
|
translate(vec2(100, 100)),
|
||||||
|
translate(vec2(100, -100)),
|
||||||
|
translate(vec2(-100, 100)),
|
||||||
|
translate(vec2(-100, -100)),
|
||||||
|
translate(vec2(0, 1000)),
|
||||||
|
translate(vec2(0, -1000)),
|
||||||
|
translate(vec2(1000, 0)),
|
||||||
|
translate(vec2(-1000, 0)),
|
||||||
|
translate(vec2(1000, 1000)),
|
||||||
|
translate(vec2(1000, -1000)),
|
||||||
|
translate(vec2(-1000, 1000)),
|
||||||
|
translate(vec2(-1000, -1000)),
|
||||||
|
]
|
||||||
|
|
||||||
|
let path = newPath()
|
||||||
|
path.rect(10, 10, 80, 80)
|
||||||
|
|
||||||
|
let mask = newImage(100, 100)
|
||||||
|
mask.fillPath(path, color(1, 1, 1, 1))
|
||||||
|
|
||||||
|
let a = newImage(100, 100)
|
||||||
|
|
||||||
|
for i, transform in transforms:
|
||||||
|
a.fill(color(0, 0, 1, 1))
|
||||||
|
a.draw(mask, transform, blendMode = MaskBlend)
|
||||||
|
doAssert a.isTransparent()
|
||||||
|
|
Loading…
Reference in a new issue