Merge pull request #345 from guzba/master
isOpaque, faster nonsimd draw, drawBasic
This commit is contained in:
commit
a5c2e08000
|
@ -1,4 +1,41 @@
|
|||
import benchy, cairo, pixie
|
||||
import benchy, cairo, pixie, pixie/blends
|
||||
|
||||
when defined(amd64) and not defined(pixieNoSimd):
|
||||
import nimsimd/sse2
|
||||
|
||||
when defined(release):
|
||||
{.push checks: off.}
|
||||
|
||||
proc drawBasic(backdrop, source: Image) =
|
||||
let sourceIsOpaque = source.isOpaque()
|
||||
|
||||
for y in 0 ..< min(backdrop.height, source.height):
|
||||
if sourceIsOpaque:
|
||||
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:
|
||||
let
|
||||
|
@ -7,7 +44,13 @@ block:
|
|||
tmp = imageSurfaceCreate(FORMAT_ARGB32, 1568, 940)
|
||||
ctx = tmp.create()
|
||||
|
||||
timeIt "cairo draw basic":
|
||||
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.paint()
|
||||
ctx.setSource(source, 0, 0)
|
||||
|
@ -22,15 +65,71 @@ block:
|
|||
source = readImage("tests/fileformats/svg/masters/Ghostscript_Tiger.png")
|
||||
tmp = newImage(1568, 940)
|
||||
|
||||
timeIt "isOneColor":
|
||||
doAssert not backdrop.isOneColor()
|
||||
|
||||
timeIt "pixie draw basic":
|
||||
timeIt "pixie draw normal":
|
||||
# tmp.fill(rgbx(127, 127, 127, 255))
|
||||
tmp.draw(backdrop)
|
||||
tmp.draw(source)
|
||||
|
||||
# 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 overwrite":
|
||||
# tmp.fill(rgbx(127, 127, 127, 255))
|
||||
tmp.draw(backdrop, blendMode = bmOverwrite)
|
||||
tmp.draw(source)
|
||||
|
||||
# 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:
|
||||
let
|
||||
backdrop = imageSurfaceCreateFromPng("tests/fileformats/svg/masters/dragon2.png")
|
||||
source = imageSurfaceCreateFromPng("tests/fileformats/svg/masters/Ghostscript_Tiger.png")
|
||||
tmp = imageSurfaceCreate(FORMAT_ARGB32, 1568, 940)
|
||||
ctx = tmp.create()
|
||||
|
||||
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.mask(source, 0, 0)
|
||||
tmp.flush()
|
||||
|
||||
# echo tmp.writeToPng("tmp_masked.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 mask":
|
||||
tmp.draw(backdrop)
|
||||
tmp.draw(source, blendMode = bmMask)
|
||||
|
||||
# tmp.writeFile("tmp_masked2.png")
|
||||
|
||||
block:
|
||||
let
|
||||
backdrop = imageSurfaceCreateFromPng("tests/fileformats/svg/masters/dragon2.png")
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import bumpy, chroma, flatty/binny, os, pixie/blends, pixie/common,
|
||||
pixie/contexts, pixie/fileformats/bmp, pixie/fileformats/gif,
|
||||
pixie/fileformats/jpg, pixie/fileformats/png, pixie/fileformats/svg,
|
||||
pixie/fonts, pixie/images, pixie/masks, pixie/paints, pixie/paths, strutils, vmath
|
||||
import bumpy, chroma, flatty/binny, os, pixie/common, pixie/contexts,
|
||||
pixie/fileformats/bmp, pixie/fileformats/gif, pixie/fileformats/jpg,
|
||||
pixie/fileformats/png, pixie/fileformats/svg, pixie/fonts, pixie/images,
|
||||
pixie/masks, pixie/paints, pixie/paths, strutils, vmath
|
||||
|
||||
export blends, bumpy, chroma, common, contexts, fonts, images, masks, paints,
|
||||
paths, vmath
|
||||
export bumpy, chroma, common, contexts, fonts, images, masks, paints, paths, vmath
|
||||
|
||||
type
|
||||
FileFormat* = enum
|
||||
|
|
|
@ -9,31 +9,6 @@ when defined(amd64) and not defined(pixieNoSimd):
|
|||
# See https://www.khronos.org/registry/OpenGL/extensions/KHR/KHR_blend_equation_advanced.txt
|
||||
|
||||
type
|
||||
BlendMode* = enum
|
||||
bmNormal
|
||||
bmDarken
|
||||
bmMultiply
|
||||
# bmLinearBurn
|
||||
bmColorBurn
|
||||
bmLighten
|
||||
bmScreen
|
||||
# bmLinearDodge
|
||||
bmColorDodge
|
||||
bmOverlay
|
||||
bmSoftLight
|
||||
bmHardLight
|
||||
bmDifference
|
||||
bmExclusion
|
||||
bmHue
|
||||
bmSaturation
|
||||
bmColor
|
||||
bmLuminosity
|
||||
|
||||
bmMask ## Special blend mode that is used for masking
|
||||
bmOverwrite ## Special blend mode that just copies pixels
|
||||
bmSubtractMask ## Inverse mask
|
||||
bmExcludeMask
|
||||
|
||||
Blender* = proc(backdrop, source: ColorRGBX): ColorRGBX {.gcsafe, raises: [].}
|
||||
## Function signature returned by blender.
|
||||
Masker* = proc(backdrop, source: uint8): uint8 {.gcsafe, raises: [].}
|
||||
|
@ -165,7 +140,7 @@ proc SetSat(C: Color, s: float32): Color {.inline.} =
|
|||
if satC > 0:
|
||||
result = (C - min([C.r, C.g, C.b])) * s / satC
|
||||
|
||||
proc blendNormal(backdrop, source: ColorRGBX): ColorRGBX =
|
||||
proc blendNormal*(backdrop, source: ColorRGBX): ColorRGBX =
|
||||
if backdrop.a == 0 or source.a == 255:
|
||||
return source
|
||||
if source.a == 0:
|
||||
|
@ -419,7 +394,7 @@ proc blendSaturation(backdrop, source: ColorRGBX): ColorRGBX =
|
|||
blended = SetLum(SetSat(backdrop, Sat(source)), Lum(backdrop))
|
||||
result = alphaFix(backdrop, source, blended).rgba.rgbx()
|
||||
|
||||
proc blendMask(backdrop, source: ColorRGBX): ColorRGBX =
|
||||
proc blendMask*(backdrop, source: ColorRGBX): ColorRGBX =
|
||||
let k = source.a.uint32
|
||||
result.r = ((backdrop.r * k) div 255).uint8
|
||||
result.g = ((backdrop.g * k) div 255).uint8
|
||||
|
@ -477,10 +452,13 @@ proc maskNormal(backdrop, source: uint8): uint8 =
|
|||
## Blending masks
|
||||
blendAlpha(backdrop, source)
|
||||
|
||||
proc maskMask(backdrop, source: uint8): uint8 =
|
||||
proc maskMaskInline*(backdrop, source: uint8): uint8 {.inline.} =
|
||||
## Masking masks
|
||||
((backdrop.uint32 * source) div 255).uint8
|
||||
|
||||
proc maskMask(backdrop, source: uint8): uint8 =
|
||||
maskMaskInline(backdrop, source)
|
||||
|
||||
proc maskSubtract(backdrop, source: uint8): uint8 =
|
||||
((backdrop.uint32 * (255 - source)) div 255).uint8
|
||||
|
||||
|
|
|
@ -3,6 +3,31 @@ import bumpy, chroma, vmath
|
|||
type
|
||||
PixieError* = object of ValueError ## Raised if an operation fails.
|
||||
|
||||
BlendMode* = enum
|
||||
bmNormal
|
||||
bmDarken
|
||||
bmMultiply
|
||||
# bmLinearBurn
|
||||
bmColorBurn
|
||||
bmLighten
|
||||
bmScreen
|
||||
# bmLinearDodge
|
||||
bmColorDodge
|
||||
bmOverlay
|
||||
bmSoftLight
|
||||
bmHardLight
|
||||
bmDifference
|
||||
bmExclusion
|
||||
bmHue
|
||||
bmSaturation
|
||||
bmColor
|
||||
bmLuminosity
|
||||
|
||||
bmMask ## Special blend mode that is used for masking
|
||||
bmOverwrite ## Special blend mode that just copies pixels
|
||||
bmSubtractMask ## Inverse mask
|
||||
bmExcludeMask
|
||||
|
||||
proc mix*(a, b: uint8, t: float32): uint8 {.inline, raises: [].} =
|
||||
## Linearly interpolate between a and b using t.
|
||||
let t = round(t * 255).uint32
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import bumpy, chroma, pixie/blends, pixie/common, pixie/fonts, pixie/images,
|
||||
pixie/masks, pixie/paints, pixie/paths, tables, vmath
|
||||
import bumpy, chroma, pixie/common, pixie/fonts, pixie/images, pixie/masks,
|
||||
pixie/paints, pixie/paths, tables, vmath
|
||||
|
||||
## This file provides a Nim version of the Canvas 2D API commonly used on the
|
||||
## web. The goal is to make picking up Pixie easy for developers familiar with
|
||||
|
|
|
@ -148,7 +148,7 @@ proc isOneColor*(image: Image): bool {.raises: [].} =
|
|||
values1 = mm_loadu_si128(image.data[i + 4].addr)
|
||||
mask0 = mm_movemask_epi8(mm_cmpeq_epi8(values0, colorVec))
|
||||
mask1 = mm_movemask_epi8(mm_cmpeq_epi8(values1, colorVec))
|
||||
if mask0 != uint16.high.int or mask1 != uint16.high.int:
|
||||
if mask0 != 0xffff or mask1 != 0xffff:
|
||||
return false
|
||||
i += 8
|
||||
|
||||
|
@ -162,7 +162,7 @@ proc isTransparent*(image: Image): bool {.raises: [].} =
|
|||
|
||||
var i: int
|
||||
when defined(amd64) and not defined(pixieNoSimd):
|
||||
let zeroVec = mm_setzero_si128()
|
||||
let vecZero = mm_setzero_si128()
|
||||
for _ in 0 ..< image.data.len div 16:
|
||||
let
|
||||
values0 = mm_loadu_si128(image.data[i + 0].addr)
|
||||
|
@ -172,8 +172,7 @@ proc isTransparent*(image: Image): bool {.raises: [].} =
|
|||
values01 = mm_or_si128(values0, values1)
|
||||
values23 = mm_or_si128(values2, values3)
|
||||
values = mm_or_si128(values01, values23)
|
||||
mask = mm_movemask_epi8(mm_cmpeq_epi8(values, zeroVec))
|
||||
if mask != uint16.high.int:
|
||||
if mm_movemask_epi8(mm_cmpeq_epi8(values, vecZero)) != 0xffff:
|
||||
return false
|
||||
i += 16
|
||||
|
||||
|
@ -181,6 +180,31 @@ proc isTransparent*(image: Image): bool {.raises: [].} =
|
|||
if image.data[j].a != 0:
|
||||
return false
|
||||
|
||||
proc isOpaque*(image: Image): bool {.raises: [].} =
|
||||
result = true
|
||||
|
||||
var i: int
|
||||
when defined(amd64) and not defined(pixieNoSimd):
|
||||
let
|
||||
vec255 = mm_set1_epi32(cast[int32](uint32.high))
|
||||
colorMask = mm_set1_epi32(cast[int32]([255.uint8, 255, 255, 0]))
|
||||
for _ in 0 ..< image.data.len div 16:
|
||||
let
|
||||
values0 = mm_loadu_si128(image.data[i + 0].addr)
|
||||
values1 = mm_loadu_si128(image.data[i + 4].addr)
|
||||
values2 = mm_loadu_si128(image.data[i + 8].addr)
|
||||
values3 = mm_loadu_si128(image.data[i + 12].addr)
|
||||
values01 = mm_and_si128(values0, values1)
|
||||
values23 = mm_and_si128(values2, values3)
|
||||
values = mm_or_si128(mm_and_si128(values01, values23), colorMask)
|
||||
if mm_movemask_epi8(mm_cmpeq_epi8(values, vec255)) != 0xffff:
|
||||
return false
|
||||
i += 16
|
||||
|
||||
for j in i ..< image.data.len:
|
||||
if image.data[j].a != 255:
|
||||
return false
|
||||
|
||||
proc flipHorizontal*(image: Image) {.raises: [].} =
|
||||
## Flips the image around the Y axis.
|
||||
let w = image.width div 2
|
||||
|
@ -636,7 +660,7 @@ proc getRgbaSmooth*(
|
|||
topMix
|
||||
|
||||
proc drawCorrect(
|
||||
a, b: Image | Mask, mat = mat3(), tiled = false, blendMode = bmNormal
|
||||
a, b: Image | Mask, transform = mat3(), tiled = false, blendMode = bmNormal
|
||||
) {.raises: [PixieError].} =
|
||||
## Draws one image onto another using matrix with color blending.
|
||||
|
||||
|
@ -646,11 +670,11 @@ proc drawCorrect(
|
|||
let masker = blendMode.masker()
|
||||
|
||||
var
|
||||
matInv = mat.inverse()
|
||||
inverseTransform = transform.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
|
||||
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
|
||||
|
||||
|
@ -660,7 +684,7 @@ proc drawCorrect(
|
|||
dx /= 2
|
||||
dy /= 2
|
||||
filterBy2 /= 2
|
||||
matInv = scale(vec2(1/2, 1/2)) * matInv
|
||||
inverseTransform = scale(vec2(1/2, 1/2)) * inverseTransform
|
||||
|
||||
while filterBy2 <= 0.5:
|
||||
b = b.magnifyBy2()
|
||||
|
@ -668,12 +692,12 @@ proc drawCorrect(
|
|||
dx *= 2
|
||||
dy *= 2
|
||||
filterBy2 *= 2
|
||||
matInv = scale(vec2(2, 2)) * matInv
|
||||
inverseTransform = scale(vec2(2, 2)) * inverseTransform
|
||||
|
||||
for y in 0 ..< a.height:
|
||||
for x in 0 ..< a.width:
|
||||
let
|
||||
samplePos = matInv * vec2(x.float32 + h, y.float32 + h)
|
||||
samplePos = inverseTransform * vec2(x.float32 + h, y.float32 + h)
|
||||
xFloat = samplePos.x - h
|
||||
yFloat = samplePos.y - h
|
||||
|
||||
|
@ -697,14 +721,14 @@ proc drawCorrect(
|
|||
a.setValueUnsafe(x, y, masker(backdrop, sample))
|
||||
|
||||
proc drawUber(
|
||||
a, b: Image | Mask, mat = mat3(), blendMode = bmNormal
|
||||
a, b: Image | Mask, transform = mat3(), blendMode: BlendMode
|
||||
) {.raises: [PixieError].} =
|
||||
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)
|
||||
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]),
|
||||
|
@ -714,11 +738,11 @@ proc drawUber(
|
|||
]
|
||||
|
||||
var
|
||||
matInv = mat.inverse()
|
||||
inverseTransform = transform.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
|
||||
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
|
||||
|
||||
|
@ -739,29 +763,28 @@ proc drawUber(
|
|||
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
|
||||
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)
|
||||
|
||||
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
|
||||
if blendMode == bmMask:
|
||||
yMin = 0
|
||||
yMax = a.height
|
||||
else:
|
||||
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)
|
||||
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
|
||||
|
@ -889,34 +912,106 @@ proc drawUber(
|
|||
clamp(srcPos.y, 0, b.height.float32)
|
||||
)
|
||||
|
||||
for x in x ..< xMax:
|
||||
let samplePos = ivec2((srcPos.x - h).int32, (srcPos.y - h).int32)
|
||||
|
||||
when type(a) is Image:
|
||||
let backdrop = a.unsafe[x, y]
|
||||
when type(b) is Image:
|
||||
let
|
||||
sample = b.unsafe[samplePos.x, samplePos.y]
|
||||
blended = blender(backdrop, sample)
|
||||
else: # b is a Mask
|
||||
let
|
||||
sample = b.unsafe[samplePos.x, samplePos.y]
|
||||
blended = blender(backdrop, rgbx(0, 0, 0, sample))
|
||||
a.unsafe[x, y] = blended
|
||||
else: # a is a Mask
|
||||
let backdrop = a.unsafe[x, y]
|
||||
when type(b) is Image:
|
||||
let sample = b.unsafe[samplePos.x, samplePos.y].a
|
||||
else: # b is a Mask
|
||||
let sample = b.unsafe[samplePos.x, samplePos.y]
|
||||
a.unsafe[x, y] = masker(backdrop, sample)
|
||||
|
||||
srcPos += dx
|
||||
case blendMode:
|
||||
of bmOverwrite:
|
||||
for x in x ..< xMax:
|
||||
let samplePos = ivec2((srcPos.x - h).int32, (srcPos.y - h).int32)
|
||||
when type(a) is Image:
|
||||
when type(b) is Image:
|
||||
let source = b.unsafe[samplePos.x, samplePos.y]
|
||||
else: # b is a Mask
|
||||
let source = rgbx(0, 0, 0, b.unsafe[samplePos.x, samplePos.y])
|
||||
if source.a > 0:
|
||||
a.unsafe[x, y] = source
|
||||
else: # a is a Mask
|
||||
when type(b) is Image:
|
||||
let source = b.unsafe[samplePos.x, samplePos.y].a
|
||||
else: # b is a Mask
|
||||
let source = b.unsafe[samplePos.x, samplePos.y]
|
||||
if source > 0:
|
||||
a.unsafe[x, y] = source
|
||||
srcPos += dx
|
||||
of bmNormal:
|
||||
for x in x ..< xMax:
|
||||
let samplePos = ivec2((srcPos.x - h).int32, (srcPos.y - h).int32)
|
||||
when type(a) is Image:
|
||||
when type(b) is Image:
|
||||
let source = b.unsafe[samplePos.x, samplePos.y]
|
||||
else: # b is a Mask
|
||||
let source = rgbx(0, 0, 0, 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)
|
||||
else: # a is a Mask
|
||||
when type(b) is Image:
|
||||
let source = b.unsafe[samplePos.x, samplePos.y].a
|
||||
else: # b is a Mask
|
||||
let source = b.unsafe[samplePos.x, samplePos.y]
|
||||
if source > 0:
|
||||
if source == 255:
|
||||
a.unsafe[x, y] = source
|
||||
else:
|
||||
let backdrop = a.unsafe[x, y]
|
||||
a.unsafe[x, y] = blendAlpha(backdrop, source)
|
||||
srcPos += dx
|
||||
of bmMask:
|
||||
for x in x ..< xMax:
|
||||
let samplePos = ivec2((srcPos.x - h).int32, (srcPos.y - h).int32)
|
||||
when type(a) is Image:
|
||||
when type(b) is Image:
|
||||
let source = b.unsafe[samplePos.x, samplePos.y]
|
||||
else: # b is a Mask
|
||||
let source = rgbx(0, 0, 0, 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)
|
||||
else: # a is a Mask
|
||||
when type(b) is Image:
|
||||
let source = b.unsafe[samplePos.x, samplePos.y].a
|
||||
else: # b is a Mask
|
||||
let source = b.unsafe[samplePos.x, samplePos.y]
|
||||
if source == 0:
|
||||
a.unsafe[x, y] = 0
|
||||
elif source != 255:
|
||||
let backdrop = a.unsafe[x, y]
|
||||
a.unsafe[x, y] = maskMaskInline(backdrop, source)
|
||||
srcPos += dx
|
||||
else:
|
||||
for x in x ..< xMax:
|
||||
let samplePos = ivec2((srcPos.x - h).int32, (srcPos.y - h).int32)
|
||||
when type(a) is Image:
|
||||
let backdrop = a.unsafe[x, y]
|
||||
when type(b) is Image:
|
||||
let
|
||||
sample = b.unsafe[samplePos.x, samplePos.y]
|
||||
blended = blender(backdrop, sample)
|
||||
else: # b is a Mask
|
||||
let
|
||||
sample = b.unsafe[samplePos.x, samplePos.y]
|
||||
blended = blender(backdrop, rgbx(0, 0, 0, sample))
|
||||
a.unsafe[x, y] = blended
|
||||
else: # a is a Mask
|
||||
let backdrop = a.unsafe[x, y]
|
||||
when type(b) is Image:
|
||||
let sample = b.unsafe[samplePos.x, samplePos.y].a
|
||||
else: # b is a Mask
|
||||
let sample = b.unsafe[samplePos.x, samplePos.y]
|
||||
a.unsafe[x, y] = masker(backdrop, sample)
|
||||
srcPos += dx
|
||||
|
||||
if blendMode == bmMask:
|
||||
if a.width - xMax > 0:
|
||||
zeroMem(a.data[a.dataIndex(xMax, y)].addr, 4 * (a.width - xMax))
|
||||
|
||||
if blendMode == bmMask:
|
||||
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 = bmNormal
|
||||
) {.inline, raises: [PixieError].} =
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import blends, chroma, common, images, vmath
|
||||
import chroma, common, images, vmath
|
||||
|
||||
when defined(amd64) and not defined(pixieNoSimd):
|
||||
import nimsimd/sse2
|
||||
|
|
|
@ -25,6 +25,10 @@ image.fill(rgba(0, 0, 0, 0))
|
|||
timeIt "isTransparent":
|
||||
doAssert image.isTransparent()
|
||||
|
||||
image.fill(rgba(255, 255, 255, 255))
|
||||
timeIt "isOpaque":
|
||||
doAssert image.isOpaque()
|
||||
|
||||
reset()
|
||||
|
||||
timeIt "subImage":
|
||||
|
|
Loading…
Reference in a new issue