From 70ab79b607d33c8046c33eed7402281868ee760e Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Thu, 11 Feb 2021 15:29:11 -0600 Subject: [PATCH] draw uber takes Image | Mask + works w/o simd so far --- src/pixie/images.nim | 211 ++++++++++++++++++++++++------------------- 1 file changed, 118 insertions(+), 93 deletions(-) diff --git a/src/pixie/images.nim b/src/pixie/images.nim index b86fce2..1a27c50 100644 --- a/src/pixie/images.nim +++ b/src/pixie/images.nim @@ -533,9 +533,7 @@ proc getRgbaSmooth*(image: Image, x, y: float32): ColorRGBA = lerp(bottomMix, topMix, diffY) -proc drawCorrect( - a: Image | Mask, b: Image | Mask, mat = mat3(), blendMode = bmNormal -) = +proc drawCorrect(a, b: Image | Mask, mat = mat3(), blendMode = bmNormal) = ## Draws one image onto another using matrix with color blending. when type(a) is Image: @@ -587,36 +585,49 @@ proc drawCorrect( let sample = b.getValueSmooth(xFloat, yFloat) a.setValueUnsafe(x, y, masker(backdrop, sample)) -proc draw*(image: Image, mask: Mask, mat: Mat3, blendMode = bmMask) = - image.drawCorrect(mask, mat, blendMode) +proc drawUber(a, b: Image | Mask, mat = mat3(), blendMode = bmNormal) = + let + corners = [ + mat * vec2(0, 0), + mat * vec2(b.width.float32, 0), + mat * vec2(b.width.float32, b.height.float32), + mat * 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]) + ] -proc draw*( - image: Image, mask: Mask, pos = vec2(0, 0), blendMode = bmMask -) {.inline.} = - image.drawCorrect(mask, translate(pos), blendMode) + var + matInv = mat.inverse() + # Compute movement vectors + p = matInv * vec2(0 + h, 0 + h) + dx = matInv * vec2(1 + h, 0 + h) - p + dy = matInv * vec2(0 + h, 1 + h) - p + minFilterBy2 = max(dx.length, dy.length) + b = b -proc draw*(a, b: Mask, mat: Mat3, blendMode = bmMask) = - a.drawCorrect(b, mat, blendMode) + while minFilterBy2 > 2.0: + b = b.minifyBy2() + p /= 2 + dx /= 2 + dy /= 2 + minFilterBy2 /= 2 + matInv = matInv * scale(vec2(0.5, 0.5)) -proc draw*(a, b: Mask, pos = vec2(0, 0), blendMode = bmMask) {.inline.} = - a.draw(b, translate(pos), blendMode) + let smooth = not( + dx.length == 1.0 and + dy.length == 1.0 and + mat[2, 0].fractional == 0.0 and + mat[2, 1].fractional == 0.0 + ) -proc draw*(mask: Mask, image: Image, mat: Mat3, blendMode = bmMask) = - mask.drawCorrect(image, mat, blendMode) - -proc draw*( - mask: Mask, image: Image, pos = vec2(0, 0), blendMode = bmMask -) {.inline.} = - mask.draw(image, translate(pos), blendMode) - -proc drawUber( - a, b: Image, - p, dx, dy: Vec2, - perimeter: array[0..3, Segment], - blendMode: BlendMode, - smooth: bool -) = - let blender = blendMode.blender() + when type(a) is Image: + let blender = blendMode.blender() + else: # a is a Mask + let masker = blendMode.masker() # Determine where we should start and stop drawing in the y dimension var yMin, yMax: int @@ -662,89 +673,103 @@ proc drawUber( srcPos = p + dx * x.float32 + dy * y.float32 xFloat = srcPos.x - h yFloat = srcPos.y - h - backdrop = a.getRgbaUnsafe(x, y) - source = b.getRgbaSmooth(xFloat, yFloat) - a.setRgbaUnsafe(x, y, blender(backdrop, source)) + when type(a) is Image: + let backdrop = a.getRgbaUnsafe(x, y) + when type(b) is Image: + let + sample = b.getRgbaSmooth(xFloat, yFloat) + blended = blender(backdrop, sample) + else: # b is a Mask + let + sample = b.getValueSmooth(xFloat, yFloat) + blended = blender(backdrop, rgba(0, 0, 0, sample)) + a.setRgbaUnsafe(x, y, blended) + else: # a is a Mask + let backdrop = a.getValueUnsafe(x, y) + when type(b) is Image: + let sample = b.getRgbaSmooth(xFloat, yFloat).a + else: # b is a Mask + let sample = b.getValueSmooth(xFloat, yFloat) + a.setValueUnsafe(x, y, masker(backdrop, sample)) else: var x = xMin - when defined(amd64) and not defined(pixieNoSimd): - if blendMode.hasSimdBlender(): - if dx.x == 1 and dx.y == 0 and dy.x == 0 and dy.y == 1: - # Check we are not rotated before using SIMD blends - let blenderSimd = blendMode.blenderSimd() - for _ in countup(x, xMax - 4, 4): - let - srcPos = p + dx * x.float32 + dy * y.float32 - sx = srcPos.x.int - sy = srcPos.y.int - backdrop = mm_loadu_si128(a.data[a.dataIndex(x, y)].addr) - source = mm_loadu_si128(b.data[b.dataIndex(sx, sy)].addr) - mm_storeu_si128( - a.data[a.dataIndex(x, y)].addr, - blenderSimd(backdrop, source) - ) - x += 4 + # when defined(amd64) and not defined(pixieNoSimd): + # if blendMode.hasSimdBlender(): + # if dx.x == 1 and dx.y == 0 and dy.x == 0 and dy.y == 1: + # # Check we are not rotated before using SIMD blends + # let blenderSimd = blendMode.blenderSimd() + # for _ in countup(x, xMax - 4, 4): + # let + # srcPos = p + dx * x.float32 + dy * y.float32 + # sx = srcPos.x.int + # sy = srcPos.y.int + # backdrop = mm_loadu_si128(a.data[a.dataIndex(x, y)].addr) + # source = mm_loadu_si128(b.data[b.dataIndex(sx, sy)].addr) + # mm_storeu_si128( + # a.data[a.dataIndex(x, y)].addr, + # blenderSimd(backdrop, source) + # ) + # x += 4 for _ in x ..< xMax: let srcPos = p + dx * x.float32 + dy * y.float32 xFloat = srcPos.x - h yFloat = srcPos.y - h - backdrop = a.getRgbaUnsafe(x, y) - source = b.getRgbaUnsafe(xFloat.int, yFloat.int) - a.setRgbaUnsafe(x, y, blender(backdrop, source)) + + when type(a) is Image: + let backdrop = a.getRgbaUnsafe(x, y) + when type(b) is Image: + let + sample = b.getRgbaUnsafe(xFloat.int, yFloat.int) + blended = blender(backdrop, sample) + else: # b is a Mask + let + sample = b.getValueUnsafe(xFloat.int, yFloat.int) + blended = blender(backdrop, rgba(0, 0, 0, sample)) + a.setRgbaUnsafe(x, y, blended) + else: # a is a Mask + let backdrop = a.getValueUnsafe(x, y) + when type(b) is Image: + let sample = b.getRgbaUnsafe(xFloat.int, yFloat.int).a + else: # b is a Mask + let sample = b.getValueUnsafe(xFloat.int, yFloat.int) + a.setValueUnsafe(x, y, masker(backdrop, sample)) inc x if blendMode == bmIntersectMask: if a.width - xMax > 0: zeroMem(a.data[a.dataIndex(xMax, y)].addr, 4 * (a.width - xMax)) -proc draw*(a, b: Image, mat: Mat3, blendMode = bmNormal) = +proc draw*(a, b: Image, mat: Mat3, blendMode = bmNormal) {.inline.} = ## Draws one image onto another using matrix with color blending. - - let - corners = [ - mat * vec2(0, 0), - mat * vec2(b.width.float32, 0), - mat * vec2(b.width.float32, b.height.float32), - mat * 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 - matInv = mat.inverse() - # Compute movement vectors - p = matInv * vec2(0 + h, 0 + h) - dx = matInv * vec2(1 + h, 0 + h) - p - dy = matInv * vec2(0 + h, 1 + h) - p - minFilterBy2 = max(dx.length, dy.length) - b = b - - while minFilterBy2 > 2.0: - b = b.minifyBy2() - p /= 2 - dx /= 2 - dy /= 2 - minFilterBy2 /= 2 - matInv = matInv * scale(vec2(0.5, 0.5)) - - let smooth = not( - dx.length == 1.0 and - dy.length == 1.0 and - mat[2, 0].fractional == 0.0 and - mat[2, 1].fractional == 0.0 - ) - - a.drawUber(b, p, dx, dy, perimeter, blendMode, smooth) + a.drawUber(b, mat, blendMode) proc draw*(a, b: Image, pos = vec2(0, 0), blendMode = bmNormal) {.inline.} = a.draw(b, translate(pos), blendMode) +proc draw*(image: Image, mask: Mask, mat: Mat3, blendMode = bmMask) {.inline.} = + image.drawUber(mask, mat, blendMode) + +proc draw*( + image: Image, mask: Mask, pos = vec2(0, 0), blendMode = bmMask +) {.inline.} = + image.drawUber(mask, translate(pos), blendMode) + +proc draw*(a, b: Mask, mat: Mat3, blendMode = bmMask) {.inline.} = + a.drawUber(b, mat, blendMode) + +proc draw*(a, b: Mask, pos = vec2(0, 0), blendMode = bmMask) {.inline.} = + a.draw(b, translate(pos), blendMode) + +proc draw*(mask: Mask, image: Image, mat: Mat3, blendMode = bmMask) {.inline.} = + mask.drawUber(image, mat, blendMode) + +proc draw*( + mask: Mask, image: Image, pos = vec2(0, 0), blendMode = bmMask +) {.inline.} = + mask.draw(image, translate(pos), blendMode) + proc resize*(srcImage: Image, width, height: int): Image = if width == srcImage.width and height == srcImage.height: result = srcImage.copy()