faster smooth draw part 1

This commit is contained in:
Ryan Oldenburg 2022-08-01 18:53:22 -05:00
parent 3fbd1da002
commit c8ee10ca16

View file

@ -522,11 +522,88 @@ proc blendRect(a, b: Image, pos: Ivec2, blendMode: BlendMode) =
blender
)
proc drawSmooth(a, b: Image, transform: Mat3, blendMode: BlendMode) =
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])
]
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
# Determine where we should start and stop drawing in the y dimension
var
yStart = a.height
yEnd = 0
for segment in perimeter:
yStart = min(yStart, segment.at.y.floor.int)
yEnd = max(yEnd, segment.at.y.ceil.int)
yStart = yStart.clamp(0, a.height)
yEnd = yEnd.clamp(0, a.height)
if blendMode == MaskBlend and yStart > 0:
zeroMem(a.data[0].addr, yStart * a.width * 4)
let blender = blendMode.blender()
for y in yStart ..< yEnd:
# 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)
let
xStart = clamp(xMin.floor.int, 0, a.width)
xEnd = clamp(xMax.ceil.int, 0, a.width)
if blendMode == MaskBlend and xStart > 0:
zeroMem(a.data[a.dataIndex(0, y)].addr, xStart * 4)
var srcPos = p + dx * xStart.float32 + dy * y.float32
srcPos = vec2(srcPos.x - h, srcPos.y - h)
for x in xStart ..< xEnd:
let
backdrop = a.unsafe[x, y]
sample = b.getRgbaSmooth(srcPos.x, srcPos.y)
blended = blender(backdrop, sample)
a.unsafe[x, y] = blended
srcPos += dx
if blendMode == MaskBlend and a.width - xEnd > 0:
zeroMem(a.data[a.dataIndex(xEnd, y)].addr, (a.width - xEnd) * 4)
if blendMode == MaskBlend and a.height - yEnd > 0:
zeroMem(
a.data[a.dataIndex(0, yEnd)].addr,
a.width * (a.height - yEnd) * 4
)
proc draw*(
a, b: Image, transform = mat3(), blendMode = NormalBlend
) {.raises: [PixieError].} =
## Draws one image onto another using a matrix transform and color blending.
var
transform = transform
inverseTransform = transform.inverse()
# Compute movement vectors
p = inverseTransform * vec2(0 + h, 0 + h)
@ -541,7 +618,7 @@ proc draw*(
dx /= 2
dy /= 2
filterBy2 /= 2
inverseTransform = scale(vec2(0.5, 0.5)) * inverseTransform
transform = transform * scale(vec2(2, 2))
while filterBy2 <= 0.5:
b = b.magnifyBy2()
@ -549,7 +626,7 @@ proc draw*(
dx *= 2
dy *= 2
filterBy2 *= 2
inverseTransform = scale(vec2(2, 2)) * inverseTransform
transform = transform * scale(vec2(1/2, 1/2))
let
hasRotationOrScaling = not(dx == vec2(1, 0) and dy == vec2(0, 1))
@ -561,7 +638,7 @@ proc draw*(
)
if hasRotationOrScaling or smooth:
a.drawCorrect(b, inverseTransform.inverse(), blendMode, false)
a.drawSmooth(b, transform, blendMode)
else:
a.blendRect(b, ivec2(transform[2, 0].int32, transform[2, 1].int32), blendMode)