Skip to content
Open
Show file tree
Hide file tree
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
9 changes: 7 additions & 2 deletions src/pixie/paths.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1604,15 +1604,20 @@ proc fillShapes(
segments = shapes.shapesToSegments()
bounds = computeBounds(segments).snapToPixels()
startX = max(0, bounds.x.int)
startY = max(0, bounds.y.int)
startY = clamp(bounds.y.int, 0, image.height)
pathWidth =
if startX < image.width:
min(bounds.w.int, image.width - startX)
else:
0
pathHeight = min(image.height, (bounds.y + bounds.h).int)
pathHeight = clamp((bounds.y + bounds.h).int, 0, image.height)

if pathWidth == 0:
if blendMode == MaskBlend:
# The path is entirely outside the image horizontally.
# A mask fill still needs to record that, otherwise the
# previous mask survives and the new clip has no effect.
image.clearUnsafe(0, 0, 0, image.height)
return

if pathWidth < 0:
Expand Down
35 changes: 35 additions & 0 deletions tests/test_contexts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -675,3 +675,38 @@ block:
ctx.fillText("Abcdefghijklmnop (" & $baseline & ")", 0, y)

ctx.image.xray("tests/contexts/textBaseline_1.png")

block:
# Regression: a clip region outside the image must not write outside the image.
const
width = 420
height = 460

proc paintedUnderClip(clipRect: Rect): int =
## Pixels painted by a full-canvas fill under `clipRect`.
let
image = newImage(width, height)
ctx = image.newContext()
first = newPath()
first.rect(0, 0, width.float32, height.float32)
ctx.clip(first) # mask == nil, so this one takes the OverwriteBlend path

let second = newPath()
second.rect(clipRect)
ctx.clip(second) # mask != nil, so this one takes the MaskBlend path

ctx.fillStyle = rgba(255, 0, 0, 255)
ctx.fillRect(rect(0, 0, width.float32, height.float32))

for px in image.data:
if px.a != 0:
inc result

# a clip region off each edge excludes everything
doAssert paintedUnderClip(rect(0, 500, 200, 100)) == 0 # below
doAssert paintedUnderClip(rect(0, -600, 200, 100)) == 0 # above
doAssert paintedUnderClip(rect(500, 0, 100, 200)) == 0 # right
doAssert paintedUnderClip(rect(-600, 0, 100, 200)) == 0 # left

# a clip region on the canvas is unaffected
doAssert paintedUnderClip(rect(10, 10, 30, 30)) == 900