Best-perorming fade between 2 images (Lua)?

I'm back to work on my next title (Outside Parties, a horror game) and I'm wondering... what's the highest-framerate way to fade (nearly) the entire screen from one image to another? (Not one image to/from black—two images.)

My early experiments using image:blendWithImage have been super slow and choppy on-device, regardless of dither choice. Maybe it can't be done? (At least not in Lua?)

Luckily, there's NOTHING else going on on the screen, so I have lots of flexibility to use any approach!

15 fps would be good... 20+ would be awesome! I'm getting more like 6 to 8.

TIA!

I was going to suggest looking at the "fast_fade" example in Single File Examples, but it looks like that could use an update. So, I would suggest using stencils, something like this:

import "CoreLibs/graphics"
import "CoreLibs/animator"

local gfx <const> = playdate.graphics
playdate.display.setRefreshRate(50)

local image1 = gfx.image.new("one")
local image2 = gfx.image.new("two")
local alpha = 0
local duration = 1000
local animator = playdate.graphics.animator.new(duration, 0, 1)

function playdate.AButtonDown()
	if alpha == 1 then
		animator = playdate.graphics.animator.new(duration, alpha, 0)
	end
end

function playdate.BButtonDown()
	if alpha == 0 then
		animator = playdate.graphics.animator.new(duration, alpha, 1)
	end
end

function playdate.update()
	
	image1:draw(0, 0)
	
	alpha = animator:currentValue()
	gfx.setStencilPattern(alpha, gfx.image.kDitherTypeBayer8x8)
	image2:draw(0, 0)
	
	gfx.clearStencil()
	playdate.drawFPS(1, 1)
	
end

ImageFade.zip (6.3 KB)

4 Likes

That's great—thanks! I will be playing with that.

The stencil approach also lends itself well to other things I want to do!

Very cool! I really need to get a grip on stencils so this is a good start.

I was trying to do this fade, but with using sprites, and I was banging my head against the wall, because I could only get it to fade in (increasing the alpha level to 1), but couldn't get it to fade down again! Well several hours later I finally found out why!
I had to set the sprite not to be opaque!!! I think that should be added to the documentation to sprite:setStencilPattern(level) - at least it could have saved me some hours! :smiley:

For anyone interested here is Dave's code, but using sprites:

import "CoreLibs/graphics"
import "CoreLibs/animator"
import "CoreLibs/sprites"

local gfx <const> = playdate.graphics
playdate.display.setRefreshRate(50)

local image1 = gfx.image.new("one")
local image2 = gfx.image.new("two")
local sprite1 = gfx.sprite.new(image1)
sprite1:setCenter(0, 0)
sprite1:setZIndex(1)
sprite1:add()
local sprite2 = gfx.sprite.new(image2)
sprite2:setCenter(0, 0)
sprite2:setZIndex(2)
sprite2:add()
sprite2:setOpaque(false)
local alpha = 0
local duration = 1000
local animator = playdate.graphics.animator.new(duration, 0, 1)

function playdate.AButtonDown()
	if alpha == 1 then
		animator = playdate.graphics.animator.new(duration, alpha, 0)
	end
end

function playdate.BButtonDown()
	if alpha == 0 then
		animator = playdate.graphics.animator.new(duration, alpha, 1)
	end
end

sprite2.update = function(self)
	self:setStencilPattern(alpha, gfx.image.kDitherTypeBayer8x8)
end

function playdate.update()
	alpha = animator:currentValue()
	gfx.sprite.update()
end
1 Like