image:rotatedImage(90) offsets/crops pixels by (-1,-1)… new in SDK 1.12?

image:rotatedImage(90) delivers a result offset up and left by 1 pixel, with the top and left lines of pixels deleted, resulting in a smaller image when it should be the same size. Image below.

This seems to be new recently—probably in SDK 1.12—or I would have noticed before now. Only 90° rotations seem to be affected.

To reproduce

This main.lua makes a 10px circle and a 90° rotated copy. They should look identical, but one is offset up/left and cropped. (The third circle tests a 45° rotation, which looks fine—it's just offset a bit since its bounding rect is larger.)

import "CoreLibs/graphics"

local pd <const> = playdate
local gfx <const> = pd.graphics

function pd.update()
end

original = gfx.image.new(10, 10)
gfx.pushContext(original)
	gfx.fillCircleInRect(0, 0, 10, 10) 
gfx.popContext()

rotated90 = original:copy()
rotated45 = original:copy()

rotated90 = rotated90:rotatedImage(90)
rotated45 = rotated45:rotatedImage(45)


original:draw(10, 10)
rotated90:draw(30, 10) --Should look identical but doesn't
rotated45:draw(50, 10)

rotatedImage90

(It happens regardless of whether the image is a perfect square or rectangular, but a square makes it easier to see.)

1 Like

Feels familiar, maybe related to Image edge clipped when using affine transforms

This bug seems active in SDK 1.13.7 (edit: and Version 2.0.0-beta.2)
image


import 'CoreLibs/graphics'

local gfx = playdate.graphics

-- draw the base image
local img_tile = gfx.image.new(32, 32, gfx.kColorWhite)
gfx.lockFocus(img_tile)
    -- direction marker
    gfx.drawCircleAtPoint(24, 24, 6)
    -- Left Edge
    gfx.drawLine(0,0,0,31)
    --Bottom Edge
    gfx.drawLine(0,31,31,31)
    -- Right Edge
    gfx.drawLine(31,31,31,0)
    --Top Edge
    gfx.drawLine(31,0,0,0)
gfx.unlockFocus()

-- make 4 copies using :rotatedImage
local ri_img0 = img_tile:rotatedImage(0)
local ri_img90 = img_tile:rotatedImage(90)
local ri_img180 = img_tile:rotatedImage(180)
local ri_img270 = img_tile:rotatedImage(270)

-- make 4 copies using :drawRotated
local img_temp = gfx.image.new(32, 32, gfx.kColorWhite)
gfx.lockFocus(img_temp)
img_tile:drawRotated(16, 16, 0)
local dr_img0 = img_temp:copy()
img_tile:drawRotated(16, 16, 90)
local dr_img90 = img_temp:copy()
img_tile:drawRotated(16, 16, 180)
local dr_img180 = img_temp:copy()
img_tile:drawRotated(16, 16, 270)
local dr_img270 = img_temp:copy()
gfx.unlockFocus()

Also affects sprite:setRotation:

https://discord.com/channels/675983554655551509/821661913393004565/1103679752142864514

4 Likes

So uh, this is a pretty freaking big issue. I'm using the latest SDK and this is still happening. I'm trying to make a pinball game with a portrait mode which involves rotating all the sprites 90 degrees. I'm using sprite:setRotation. Any plans to fix this? Any workarounds?

Test image. Notice the solid lines on the edges.

When rotated, the left side and bottom side (presumably, the camera is showing the top of the playfield) is cropped.

I use a workaround that's adapted from TheMediocritist's post earlier:

local USE_ROTATION_WORKAROUND <const> = true

function rotated_image(image, angle)
	if angle == 0 then
		return image
	end

	if USE_ROTATION_WORKAROUND then
		local width <const>, height <const> = image:getSize()
		local transformed_image = gfx.image.new(width, height, gfx.kColorClear)
		gfx.pushContext(transformed_image)
		image:drawRotated(width / 2, height / 2, angle)
		gfx.popContext()
		return transformed_image
	end
	return image:rotatedImage(angle)
end
4 Likes

Also noticed this in my own project, is there any planned fix?

Thank you so much for this workaround!