Graphical glitch with setStencilImage

Drawing a filled shape into a stencilled context sometimes causes graphical glitches, as though tiles towards the right side are being horizontally flipped (shown in the gif below). This seems to vary depending on the size of the image passed to setStencilImage.

setStencilImage_bug

repro:

import 'CoreLibs/graphics'

local gfx <const> = playdate.graphics

local cy <const> = 120
local cx <const> = 200
local r <const> = 40
local s <const> = r * 2
local y = 0
local d = 1

-- create a mask image with a circle
local mask = gfx.image.new(s, s, gfx.kColorBlack)
gfx.pushContext(mask)
gfx.setColor(gfx.kColorWhite)
gfx.fillCircleInRect(0, 0, s, s)
gfx.popContext()

playdate.display.setRefreshRate(50)

function playdate.update()
  -- draw another circle using the mask image as a stencil
  gfx.clear()
  gfx.setStencilImage(mask)
  gfx.fillCircleInRect(cx - r, cy - r - y, s, s)
  -- alt:
  -- gfx.fillRect(cx - r, cy - r - y, s, s)

  -- alternate circle back and forth
  if y > s then
    d = -1
  elseif y < -s then
    d = 1
  end
  y += d
 
end

ah! okay, the problem is that the stencil's width isn't a multiple of 32. :confused: Before 1.7 stencils were expected to be full-screen, but a really common use of stencils is for fades where the stencil image is a small pattern repeated across the screen, so I added tiling--but only at 32-pixel widths, to avoid a lot of really ugly (and slow) bit wrangling. In your case you can do the tiling manually. It only runs once, so the overhead is negligible.

local mask = gfx.image.new(400, 240, gfx.kColorBlack)
gfx.pushContext(mask)
gfx.setColor(gfx.kColorWhite)

for y=0,240,s do
	for x=0,400,s do
		gfx.fillEllipseInRect(x, y, s, s)
	end
end

gfx.popContext()

It's mentioned it in the docs, but I should add a runtime error if you set a stencil with the wrong size.

Sorry if I wasn't clear -- I don't actually care about tiling here! It's the strange glitch that occurs at certain offsets in the stencil, where it looks like small sections of it become flipped:
Screenshot 2022-01-10 at 22.40.56

For context, I'm making an app where users can customise the dithering used in the place of certain colors when watching Flipnote Studio animations. The dither swatches are represented as circles, and when the dither pattern is changed I wanted to swipe in the new pattern over the last one. I'm using a stencil image with a circle drawn to it as a way of masking the two patterns during the swipe transition, but this glitch is causing slight artefacts towards the bottom-right side of the swatch:

dither

It was more obvious in motion, which is why I made the circle gif :slight_smile:

Ah! Got it. Before 1.7 stencils didn't tile, so in your demo you wouldn't have seen anything at all: the circle on the stencil would be up in the top left corner of the screen away from where you're drawing. I need to make it clearer in the docs that stencils are "attached to" the screen, in the sense they're applied at the end of the drawing pipeline as pixels are written to the frame buffer.

Here's one way to build your color selector: Create a full-screen stencil with circles for all 6 color swatches, then when animating the color change set that stencil and also set a clip rect around the swatch to keep that animated circle from drawing into another swatch.

Thanks! I ended up solving it by using a 64x64 mask and another 64x64 image, and drawing the
40x40 color selector to the image (via the mask) first, then drawing that to the screen. Seems to work like a charm!