Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 80 additions & 3 deletions src/pixie/images.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -541,15 +618,15 @@ 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()
p *= 2
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))
Expand All @@ -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)

Expand Down