Blit strategy
This commit is contained in:
parent
3add6bb3f0
commit
e48a191174
|
@ -454,15 +454,63 @@ proc getRgbaSmooth*(
|
|||
else:
|
||||
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(
|
||||
a, b: Image | Mask, transform = mat3(), blendMode = NormalBlend, tiled = false
|
||||
) {.raises: [PixieError].} =
|
||||
## Draws one image onto another using matrix with color blending.
|
||||
|
||||
when type(a) is Image:
|
||||
let blender = blendMode.blender()
|
||||
else: # a is a Mask
|
||||
let masker = blendMode.masker()
|
||||
|
||||
var
|
||||
inverseTransform = transform.inverse()
|
||||
|
@ -489,6 +537,25 @@ proc drawCorrect(
|
|||
filterBy2 *= 2
|
||||
inverseTransform = scale(vec2(2, 2)) * inverseTransform
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
when type(a) is Image and type(b) is Image:
|
||||
if not hasRotationOrScaling and not smooth and not tiled:
|
||||
blitRect(a, b, ivec2(transform[2, 0].int32, transform[2, 1].int32), blendMode)
|
||||
return
|
||||
|
||||
when type(a) is Image:
|
||||
let blender = blendMode.blender()
|
||||
else: # a is a Mask
|
||||
let masker = blendMode.masker()
|
||||
|
||||
for y in 0 ..< a.height:
|
||||
for x in 0 ..< a.width:
|
||||
let
|
||||
|
|
|
@ -417,5 +417,50 @@ proc applyOpacitySse2*(mask: Mask, opacity: float32) {.simd.} =
|
|||
for i in i ..< mask.data.len:
|
||||
mask.data[i] = ((mask.data[i] * opacity) div 255).uint8
|
||||
|
||||
proc blitLineNormalSse2*(a, b: ptr UncheckedArray[ColorRGBX], len: int) {.simd.} =
|
||||
|
||||
# TODO align to 16
|
||||
|
||||
var i = 0
|
||||
while i < len - 4:
|
||||
|
||||
let
|
||||
source = mm_loadu_si128(b[i].addr)
|
||||
backdrop = mm_loadu_si128(a[i].addr)
|
||||
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)
|
||||
|
||||
let done = mm_add_epi8(
|
||||
source,
|
||||
mm_or_si128(backdropEven, mm_slli_epi16(backdropOdd, 8))
|
||||
)
|
||||
|
||||
mm_storeu_si128(a[i].addr, done)
|
||||
|
||||
i += 4
|
||||
|
||||
# TODO last 1-3 pixels
|
||||
# for i in i ..< len:
|
||||
# a[i] = blendNormal(a[i], b[i])
|
||||
|
||||
when defined(release):
|
||||
{.pop.}
|
||||
|
|
37
tests/test_images_draw_correct.nim
Normal file
37
tests/test_images_draw_correct.nim
Normal file
|
@ -0,0 +1,37 @@
|
|||
import pixie, strformat, xrays
|
||||
import pixie/images {.all.}
|
||||
|
||||
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")
|
||||
|
||||
import benchy
|
||||
|
||||
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)
|
Loading…
Reference in a new issue