Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
ae5fae8
fixed zero tiles in a tilemap causing error
Jan 29, 2026
ad1511c
fixed tilemap _height not being initialized from setTiles()
Jan 29, 2026
d3f5571
fixed invalid tile index calculation in getTileAtPosition()
Jan 29, 2026
f3b7b1a
fixed getTileAtPosition() not returning nil when x or y are out of bo…
Jan 29, 2026
94b504c
fixed setTileAtPosition() not working properly
Jan 29, 2026
932beaf
implemented tilemap getSize() and getPixelSize()
Jan 29, 2026
e959ebb
optimize drawing loop
Jan 29, 2026
7bbcab4
fixed tilemap draw() not using x, y
Jan 29, 2026
e382c3d
checked tile is not nil before drawing
Jan 29, 2026
a387a41
fix error when tile index is out of range
Feb 1, 2026
2aa7e89
implemented image:copy() and image:scaledImage()
Feb 2, 2026
5a3f53d
fixed drawing functions to support scaled images
Feb 2, 2026
f4a492f
partial geometry implementation
Feb 3, 2026
a10d9ae
affineTransform implementation
Feb 5, 2026
7253572
polygon implementation
Feb 6, 2026
0b59962
fix arc.pointOnArc
Feb 6, 2026
ee01aa9
animator implementation
Feb 6, 2026
9cd337d
implemented input handlers
Feb 3, 2026
696a354
running playdate.update as a coroutine
Feb 7, 2026
0458bb9
implemented math
Feb 7, 2026
d0d91a2
added graphics stubs
Feb 10, 2026
71a7a29
Merge branch 'feature/input-handlers' into 123
Feb 10, 2026
1d816a8
Merge branch 'bugfix/fix-issue-62' into 123
Feb 10, 2026
ec93571
Merge branch 'feature/scaled-image' into 123
Feb 10, 2026
d6bd071
Merge branch 'bugfix/fix-issue-81' into 123
Feb 10, 2026
b351a41
Merge branch 'feature/math' into 123
Feb 10, 2026
64b9a1f
Merge branch 'feature/graphics-stubs' into 123
Feb 10, 2026
ec99b42
Merge branch 'feature/geometry-api' into 123
Feb 10, 2026
67bcc66
fixed graphics.drawArc()
Feb 1, 2026
7a35fff
add support for geometry objects as parameters in drawing functions
Feb 7, 2026
299da20
fixed image.new() not trying to use provided path as is
Feb 7, 2026
6b8dac2
fixed button functions being case-sensitive
Feb 10, 2026
0b0d568
added playdate stubs
Feb 10, 2026
72ffd7c
fix typo
Feb 10, 2026
291afe6
Merge branch 'bugfix/fix-issue-76'
Feb 11, 2026
f8965f8
Merge branch 'feature/animator'
Feb 11, 2026
6eb24b6
Merge branch 'bugfix/fix-issue-86'
Feb 11, 2026
f15b007
Merge branch 'bugfix/fix-issue-88'
Feb 11, 2026
7c65f7e
Merge branch 'feature/playdate-stubs'
Feb 11, 2026
9770360
added new shaders
Feb 10, 2026
38e3b1c
pass drawing color directly to the color shader
Feb 10, 2026
466423c
added three drawing modes: line, fill, image
Feb 10, 2026
6c1e058
implemented better pushContext()/popContext()
Feb 10, 2026
ff52e5d
remove updateContext() calls
Feb 10, 2026
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
27 changes: 16 additions & 11 deletions header.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
!if LOVE2D then
require("playbit.graphics")

--[[ since there is no CoreLibs/playdate, this file should always
--[[ since there is no CoreLibs/playdate, this file should always
be included here so the methods are always available ]]--
require("playdate.playdate")
--[[ not really a way around including this one, but probably doesn't really
Expand Down Expand Up @@ -35,6 +35,8 @@ math.randomseed(os.time())
local font = playdate.graphics.font.new("fonts/Phozon/Phozon")
playdate.graphics.setFont(font)

local updateCoroutine

function love.draw()
-- must be changed at start of frame when canvas is not active
local newCanvasWidth, newCanvasHeight = playbit.graphics.getCanvasSize()
Expand Down Expand Up @@ -66,9 +68,8 @@ function love.draw()

-- render to canvas to allow 2x scaling
love.graphics.setCanvas(playbit.graphics.canvas)
love.graphics.setShader(playbit.graphics.shader)

--[[
--[[
Love2d won't allow a canvas to be set outside of the draw function, so we need to do this on the first frame of draw.
Otherwise setting the bg color outside of playdate.update() won't be consistent with PD.
--]]
Expand All @@ -86,7 +87,14 @@ function love.draw()
love.graphics.translate(playbit.graphics.drawOffset.x, playbit.graphics.drawOffset.y)

-- main update
playdate.update()
if not updateCoroutine or coroutine.status(updateCoroutine) == "dead" then
updateCoroutine = coroutine.create(playdate.update)
end

local ok, err = coroutine.resume(updateCoroutine)
if not ok then
error(err)
end

-- debug draw
if playdate.debugDraw then
Expand All @@ -102,19 +110,16 @@ function love.draw()
love.graphics.setCanvas()

-- clear shader so that canvas is rendered normally
love.graphics.setShader()

-- always render pure white so its not tinted
local r, g, b = love.graphics.getColor()
love.graphics.setColor(1, 1, 1, 1)
local shader = love.graphics.getShader()
love.graphics.setShader(playbit.graphics.shaders.final)

-- draw canvas to screen
local currentCanvasScale = playbit.graphics.getCanvasScale()
local x, y = playbit.graphics.getCanvasPosition()
love.graphics.draw(playbit.graphics.canvas, x, y, 0, currentCanvasScale, currentCanvasScale)

-- reset back to set color
love.graphics.setColor(r, g, b, 1)
-- reset back the shader
love.graphics.setShader(shader)

-- update emulated input
playdate.updateInput()
Expand Down
80 changes: 43 additions & 37 deletions playbit/graphics.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,37 @@ module.COLOR_BLACK = { 49 / 255, 47 / 255, 40 / 255, 1 }

module.colorWhite = module.COLOR_WHITE
module.colorBlack = module.COLOR_BLACK

module.shaders =
{
final = love.graphics.newShader("playbit/shaders/final.glsl"),
color = love.graphics.newShader("playbit/shaders/color.glsl"),
pattern = love.graphics.newShader("playbit/shaders/pattern.glsl"),
image = { }
}

local shader = love.filesystem.read("playbit/shaders/image.glsl")
for i = 0, 9 do
local src = "#define DRAW_MODE " .. i .. "\n" .. shader
module.shaders.image[i] = love.graphics.newShader(src)
end

module.shader = love.graphics.newShader("playdate/shader")
module.drawOffset = { x = 0, y = 0}
module.drawColorIndex = 1
module.drawColor = module.colorWhite
module.backgroundColorIndex = 0
module.backgroundColor = module.colorBlack
module.activeFont = {}
module.drawMode = "copy"
module.imageDrawMode = 0
module.drawMode = nil
module.canvas = love.graphics.newCanvas()
module.contextStack = {}
-- shared quad to reduce gc
module.quad = love.graphics.newQuad(0, 0, 1, 1, 1, 1)
module.lastClearColor = module.colorWhite
module.drawPattern = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
module.drawPattern = nil
module.lineWidth = 1

local canvasScale = 1
local canvasWidth = 400
Expand Down Expand Up @@ -107,44 +124,33 @@ end
---@param white table An array of 4 values that correspond to RGBA that range from 0 to 1.
---@param black table An array of 4 values that correspond to RGBA that range from 0 to 1.
function module.setColors(white, black)
if white == nil then
white = module.COLOR_WHITE
end
if black == nil then
black = module.COLOR_BLACK
end

module.colorWhite = white
module.colorBlack = black
module.shader:send("white", white)
module.shader:send("black", black)

if module.backgroundColorIndex == 1 then
module.backgroundColor = module.colorWhite
else
module.backgroundColor = module.colorBlack
end

if module.drawColorIndex == 1 then
module.drawColor = module.colorWhite
else
module.drawColor = module.colorBlack
end
module.colorWhite = white or module.COLOR_WHITE
module.colorBlack = black or module.COLOR_BLACK
module.shaders.final:send("white", white)
module.shaders.final:send("black", black)
end

function module.updateContext()
if #module.contextStack == 0 then
return
end
local function getShader(mode)
if mode == "line" then
return module.shaders.color

local activeContext = module.contextStack[#module.contextStack]
elseif mode == "fill" then
if module.drawPattern then
return module.shaders.pattern
else
return module.shaders.color
end

-- love2d doesn't allow calling newImageData() when canvas is active
love.graphics.setCanvas()
local imageData = activeContext._canvas:newImageData()
love.graphics.setCanvas(activeContext._canvas)
elseif mode == "image" then
return module.shaders.image[module.imageDrawMode]
end
end

-- update image
activeContext.data:replacePixels(imageData)
function module.setDrawMode(mode)
if module.drawMode ~= mode then
module.drawMode = mode
local shader = getShader(mode)
love.graphics.setShader(shader)
end
end
!end
!end
11 changes: 11 additions & 0 deletions playbit/shaders/color.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#pragma language glsl3

extern vec4 drawColor;

vec4 effect(vec4 color, Image tex, vec2 tex_coords, vec2 screen_coords)
{
float outColor = drawColor.r;
float outAlpha = drawColor.a;

return vec4(outColor, outColor, outColor, outAlpha);
}
15 changes: 15 additions & 0 deletions playbit/shaders/final.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma language glsl3

extern vec4 white = vec4(176.0f / 255.0f, 174.0f / 255.0f, 167.0f / 255.0f, 1);
extern vec4 black = vec4( 49.0f / 255.0f, 47.0f / 255.0f, 40.0f / 255.0f, 1);

vec4 effect(vec4 color, Image tex, vec2 tex_coords, vec2 screen_coords)
{
vec4 inColor = Texel(tex, tex_coords) * color;

vec4 outColor;
outColor.rgb = mix(black.rgb, white.rgb, inColor.r);
outColor.a = 1;

return outColor;
}
49 changes: 49 additions & 0 deletions playbit/shaders/image.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#pragma language glsl3

extern Image canvas;

vec4 effect(vec4 color, Image tex, vec2 tex_coords, vec2 screen_coords)
{
vec4 texColor = Texel(tex, tex_coords);
float grayscale = dot(texColor.rgb, vec3(0.2126, 0.7152, 0.0722));
float inColor = step(0.5, grayscale);
float inAlpha = step(0.5, texColor.a);

#if DRAW_MODE == 1 // White Transparent
float outColor = inColor;
float outAlpha = inAlpha * (1.0 - inColor);

#elif DRAW_MODE == 2 // Black Transparent
float outColor = inColor;
float outAlpha = inAlpha * inColor;

#elif DRAW_MODE == 3 // Fill White
float outColor = 1;
float outAlpha = inAlpha;

#elif DRAW_MODE == 4 // Fill Black
float outColor = 0;
float outAlpha = inAlpha;

#elif DRAW_MODE == 5 // XOR
vec4 canvasColor = Texel(canvas, screen_coords / love_ScreenSize.xy);
float outColor = abs(canvasColor.r - inColor);
float outAlpha = inAlpha;

#elif DRAW_MODE == 6 // NXOR
vec4 canvasColor = Texel(canvas, screen_coords / love_ScreenSize.xy);
float outColor = 1.0 - abs(canvasColor.r - inColor);
float outAlpha = inAlpha;

#elif DRAW_MODE == 7 // Inverted
float outColor = 1.0 - inColor;
float outAlpha = inAlpha;

#else // Copy
float outColor = inColor;
float outAlpha = inAlpha;

#endif

return vec4(outColor, outColor, outColor, outAlpha);
}
15 changes: 15 additions & 0 deletions playbit/shaders/pattern.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma language glsl3

extern float pattern[64];

vec4 effect(vec4 color, Image tex, vec2 tex_coords, vec2 screen_coords)
{
// Use mod() to get the position of the current pixel within the 8x8 pattern
int x = int(mod(screen_coords.x, 8.0));
int y = int(mod(screen_coords.y, 8.0));

float outColor = pattern[x + y * 8];
float outAlpha = 1;

return vec4(outColor, outColor, outColor, outAlpha);
}
20 changes: 20 additions & 0 deletions playbit/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,24 @@ function module.sign(a)
else
return 0
end
end

function module.clamp01(x)
if x <= 0 then
return 0
elseif x >= 1 then
return 1
else
return x
end
end

function module.clamp(x, min, max)
if x <= min then
return min
elseif x >= max then
return max
else
return x
end
end
Loading