diff --git a/changes.txt b/changes.txt index 6ac3a5921..83966c68f 100644 --- a/changes.txt +++ b/changes.txt @@ -68,7 +68,8 @@ Released: N/A * Added APIs to override the default orthographic projection: love.graphics.setOrthoProjection, setPerspectiveProjection, and resetProjection. * Added ability to set point size within a vertex shader by setting the 'love_PointSize' variable. * Added love.graphics.setBlendState, which gives lower level control over blend operations than setBlendMode. -* Added love.graphics.setStencilMode and getStencilMode. Replaces love.graphics.stencil as well as setStencilTest. +* Added high level love.graphics.setStencilMode and getStencilMode functions. Replaces love.graphics.stencil and setStencilTest. +* Added lower level love.graphics.setStencilState and getStencilState functions. * Added a variant of love.graphics.setColorMask which accepts a single boolean. * Added new 'clampone' wrap mode. * Added 'clampone', 'texelbuffer', 'indexbuffer32bit', 'mipmaprange', and 'indirectdraw' graphics feature enums. @@ -113,8 +114,8 @@ Released: N/A * Deprecated love.graphics.setNewFont (use love.graphics.newFont and love.graphics.setFont instead). * Deprecated love.graphics.newText (renamed to love.graphics.newTextBatch). * Deprecated love.graphics.getImageFormats and love.graphics.getCanvasFormats (replaced by getTextureFormats). -* Deprecated love.graphics.stencil (replaced by love.graphics.setStencilMode). -* Deprecated love.graphics.setStencilTest and love.graphics.getStencilTest (replaced by love.graphics.setStencilMode and getStencilMode). +* Deprecated love.graphics.stencil (replaced by love.graphics.setStencilMode or love.graphics.setStencilState). +* Deprecated love.graphics.setStencilTest and love.graphics.getStencilTest (replaced by love.graphics.setStencilMode or setStencilState). * Deprecated t.window.highdpi in love.conf and the highdpi flag of love.window.setMode (replaced by t.highdpi in love.conf). * Deprecated t.accelerometerjoystick in love.conf (replaced by love.sensor module). * Deprecated the variants of Mesh:attachAttribute and SpriteBatch:attachAttribute which accept a Mesh (replaced by variants which accept a Buffer). diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index b451bc301..808262e27 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -700,7 +700,7 @@ void Graphics::restoreState(const DisplayState &s) setShader(s.shader.get()); setRenderTargets(s.renderTargets); - setStencilMode(s.stencil.action, s.stencil.compare, s.stencil.value, s.stencil.readMask, s.stencil.writeMask); + setStencilState(s.stencil.action, s.stencil.compare, s.stencil.value, s.stencil.readMask, s.stencil.writeMask); setDepthMode(s.depthTest, s.depthWrite); setColorMask(s.colorMask); @@ -776,7 +776,7 @@ void Graphics::restoreStateChecked(const DisplayState &s) setRenderTargets(s.renderTargets); if (!(s.stencil == cur.stencil)) - setStencilMode(s.stencil.action, s.stencil.compare, s.stencil.value, s.stencil.readMask, s.stencil.writeMask); + setStencilState(s.stencil.action, s.stencil.compare, s.stencil.value, s.stencil.readMask, s.stencil.writeMask); if (s.depthTest != cur.depthTest || s.depthWrite != cur.depthWrite) setDepthMode(s.depthTest, s.depthWrite); @@ -1309,12 +1309,37 @@ bool Graphics::getScissor(Rect &rect) const return state.scissor; } +void Graphics::setStencilMode(StencilMode mode, int value) +{ + StencilState s = computeStencilState(mode, value); + setStencilState(s.action, s.compare, s.value, s.readMask, s.writeMask); + if (mode == STENCIL_MODE_DRAW) + setColorMask({ false, false, false, false }); + else + setColorMask({ true, true, true, true }); +} + void Graphics::setStencilMode() { - setStencilMode(STENCIL_KEEP, COMPARE_ALWAYS, 0, LOVE_UINT32_MAX, LOVE_UINT32_MAX); + StencilState s = computeStencilState(STENCIL_MODE_OFF, 0); + setStencilState(s.action, s.compare, s.value, s.readMask, s.writeMask); + setColorMask({ true, true, true, true }); +} + +StencilMode Graphics::getStencilMode(int &value) const +{ + const DisplayState& state = states.back(); + StencilMode mode = computeStencilMode(state.stencil); + value = state.stencil.value; + return mode; +} + +void Graphics::setStencilState() +{ + setStencilState(STENCIL_KEEP, COMPARE_ALWAYS, 0, LOVE_UINT32_MAX, LOVE_UINT32_MAX); } -void Graphics::getStencilMode(StencilAction &action, CompareMode &compare, int &value, uint32 &readmask, uint32 &writemask) const +void Graphics::getStencilState(StencilAction &action, CompareMode &compare, int &value, uint32 &readmask, uint32 &writemask) const { const DisplayState &state = states.back(); action = state.stencil.action; diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index e668b6925..61800a72f 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -613,9 +613,13 @@ class Graphics : public Module */ bool getScissor(Rect &rect) const; - virtual void setStencilMode(StencilAction action, CompareMode compare, int value, uint32 readmask, uint32 writemask) = 0; + void setStencilMode(StencilMode mode, int value); void setStencilMode(); - void getStencilMode(StencilAction &action, CompareMode &compare, int &value, uint32 &readmask, uint32 &writemask) const; + StencilMode getStencilMode(int &value) const; + + virtual void setStencilState(StencilAction action, CompareMode compare, int value, uint32 readmask, uint32 writemask) = 0; + void setStencilState(); + void getStencilState(StencilAction &action, CompareMode &compare, int &value, uint32 &readmask, uint32 &writemask) const; virtual void setDepthMode(CompareMode compare, bool write) = 0; void setDepthMode(); diff --git a/src/modules/graphics/metal/Graphics.h b/src/modules/graphics/metal/Graphics.h index a949aa250..936be0792 100644 --- a/src/modules/graphics/metal/Graphics.h +++ b/src/modules/graphics/metal/Graphics.h @@ -96,7 +96,7 @@ class Graphics final : public love::graphics::Graphics void setScissor(const Rect &rect) override; void setScissor() override; - void setStencilMode(StencilAction action, CompareMode compare, int value, uint32 readmask, uint32 writemask) override; + void setStencilState(StencilAction action, CompareMode compare, int value, uint32 readmask, uint32 writemask) override; void setDepthMode(CompareMode compare, bool write) override; diff --git a/src/modules/graphics/metal/Graphics.mm b/src/modules/graphics/metal/Graphics.mm index a89c6d5fe..daffad45e 100644 --- a/src/modules/graphics/metal/Graphics.mm +++ b/src/modules/graphics/metal/Graphics.mm @@ -846,7 +846,7 @@ static bool isClampOne(SamplerState::WrapMode w) * example, if the compare function is GREATER then the stencil test will * pass if the reference value is greater than the value in the stencil * buffer. With our API it's more intuitive to assume that - * setStencilMode(STENCIL_KEEP, COMPARE_GREATER, 4) will make it pass if the + * setStencilState(STENCIL_KEEP, COMPARE_GREATER, 4) will make it pass if the * stencil buffer has a value greater than 4. **/ stencildesc.stencilCompareFunction = getMTLCompareFunction(getReversedCompareMode(stencil.compare)); @@ -1760,7 +1760,7 @@ static inline void advanceVertexOffsets(const VertexAttributes &attributes, Buff } } -void Graphics::setStencilMode(StencilAction action, CompareMode compare, int value, uint32 readmask, uint32 writemask) +void Graphics::setStencilState(StencilAction action, CompareMode compare, int value, uint32 readmask, uint32 writemask) { DisplayState &state = states.back(); diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index e2b863049..728918aa6 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -1434,7 +1434,7 @@ void Graphics::setScissor() gl.setEnableState(OpenGL::ENABLE_SCISSOR_TEST, false); } -void Graphics::setStencilMode(StencilAction action, CompareMode compare, int value, uint32 readmask, uint32 writemask) +void Graphics::setStencilState(StencilAction action, CompareMode compare, int value, uint32 readmask, uint32 writemask) { DisplayState &state = states.back(); @@ -1493,7 +1493,7 @@ void Graphics::setStencilMode(StencilAction action, CompareMode compare, int val * example, if the compare function is GREATER then the stencil test will * pass if the reference value is greater than the value in the stencil * buffer. With our API it's more intuitive to assume that - * setStencilMode(STENCIL_KEEP, COMPARE_GREATER, 4) will make it pass if the + * setStencilState(STENCIL_KEEP, COMPARE_GREATER, 4) will make it pass if the * stencil buffer has a value greater than 4. **/ GLenum glcompare = OpenGL::getGLCompareMode(getReversedCompareMode(compare)); diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 2b3525b0d..ee8a940cb 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -92,7 +92,7 @@ class Graphics final : public love::graphics::Graphics void setScissor(const Rect &rect) override; void setScissor() override; - void setStencilMode(StencilAction action, CompareMode compare, int value, uint32 readmask, uint32 writemask) override; + void setStencilState(StencilAction action, CompareMode compare, int value, uint32 readmask, uint32 writemask) override; void setDepthMode(CompareMode compare, bool write) override; diff --git a/src/modules/graphics/renderstate.cpp b/src/modules/graphics/renderstate.cpp index 133db2bb0..171103de0 100644 --- a/src/modules/graphics/renderstate.cpp +++ b/src/modules/graphics/renderstate.cpp @@ -28,7 +28,7 @@ namespace graphics // These are all with premultiplied alpha. computeBlendState adjusts for // alpha-multiply if needed. -static const BlendState states[BLEND_MAX_ENUM] = +static const BlendState blendStates[BLEND_MAX_ENUM] = { // BLEND_ALPHA {BLENDOP_ADD, BLENDOP_ADD, BLENDFACTOR_ONE, BLENDFACTOR_ONE, BLENDFACTOR_ONE_MINUS_SRC_ALPHA, BLENDFACTOR_ONE_MINUS_SRC_ALPHA}, @@ -63,7 +63,7 @@ static const BlendState states[BLEND_MAX_ENUM] = BlendState computeBlendState(BlendMode mode, BlendAlpha alphamode) { - BlendState s = states[mode]; + BlendState s = blendStates[mode]; // We can only do alpha-multiplication when srcRGB would have been unmodified. if (s.srcFactorRGB == BLENDFACTOR_ONE && alphamode == BLENDALPHA_MULTIPLY && mode != BLEND_NONE) @@ -87,7 +87,7 @@ BlendMode computeBlendMode(BlendState s, BlendAlpha &alphamode) for (int i = 0; i < (int) BLEND_MAX_ENUM; i++) { - if (i != (int) BLEND_CUSTOM && states[i] == s) + if (i != (int) BLEND_CUSTOM && blendStates[i] == s) { alphamode = alphamultiply ? BLENDALPHA_MULTIPLY : BLENDALPHA_PREMULTIPLIED; return (BlendMode) i; @@ -111,6 +111,39 @@ bool isAlphaMultiplyBlendSupported(BlendMode mode) } } +static const StencilState stencilStates[STENCIL_MODE_MAX_ENUM] = +{ + // STENCIL_MODE_OFF + {}, + + // STENCIL_MODE_DRAW + {COMPARE_ALWAYS, STENCIL_REPLACE}, + + // STENCIL_MODE_TEST + {COMPARE_EQUAL, STENCIL_KEEP}, + + // STENCIL_MODE_CUSTOM - N/A + {}, +}; + +StencilState computeStencilState(StencilMode mode, int value) +{ + StencilState s = stencilStates[mode]; + s.value = value; + return s; +} + +StencilMode computeStencilMode(const StencilState &s) +{ + for (int i = 0; i < (int)STENCIL_MODE_MAX_ENUM; i++) + { + if (stencilStates[i].action == s.action && stencilStates[i].compare == s.compare) + return (StencilMode) i; + } + + return STENCIL_MODE_CUSTOM; +} + CompareMode getReversedCompareMode(CompareMode mode) { switch (mode) @@ -171,6 +204,15 @@ STRINGMAP_BEGIN(BlendOperation, BLENDOP_MAX_ENUM, blendOperation) } STRINGMAP_END(BlendOperation, BLENDOP_MAX_ENUM, blendOperation) +STRINGMAP_BEGIN(StencilMode, STENCIL_MODE_MAX_ENUM, stencilMode) +{ + { "off", STENCIL_MODE_OFF }, + { "draw", STENCIL_MODE_DRAW }, + { "test", STENCIL_MODE_TEST }, + { "custom", STENCIL_MODE_CUSTOM }, +} +STRINGMAP_END(StencilMode, STENCIL_MODE_MAX_ENUM, stencilMode) + STRINGMAP_BEGIN(StencilAction, STENCIL_MAX_ENUM, stencilAction) { { "keep", STENCIL_KEEP }, diff --git a/src/modules/graphics/renderstate.h b/src/modules/graphics/renderstate.h index fb86f0489..193dc567c 100644 --- a/src/modules/graphics/renderstate.h +++ b/src/modules/graphics/renderstate.h @@ -83,6 +83,15 @@ enum BlendOperation BLENDOP_MAX_ENUM }; +enum StencilMode // High level wrappers. +{ + STENCIL_MODE_OFF, + STENCIL_MODE_DRAW, + STENCIL_MODE_TEST, + STENCIL_MODE_CUSTOM, + STENCIL_MODE_MAX_ENUM +}; + enum StencilAction { STENCIL_KEEP, @@ -194,6 +203,9 @@ BlendState computeBlendState(BlendMode mode, BlendAlpha alphamode); BlendMode computeBlendMode(BlendState s, BlendAlpha &alphamode); bool isAlphaMultiplyBlendSupported(BlendMode mode); +StencilState computeStencilState(StencilMode mode, int value); +StencilMode computeStencilMode(const StencilState &s); + /** * GPU APIs do the comparison in the opposite way of what makes sense for some * of love's APIs. For example in OpenGL if the compare function is GL_GREATER, @@ -208,6 +220,7 @@ STRINGMAP_DECLARE(BlendMode); STRINGMAP_DECLARE(BlendAlpha); STRINGMAP_DECLARE(BlendFactor); STRINGMAP_DECLARE(BlendOperation); +STRINGMAP_DECLARE(StencilMode); STRINGMAP_DECLARE(StencilAction); STRINGMAP_DECLARE(CompareMode); diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index b6886fe96..ff4b9aef2 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -1001,7 +1001,7 @@ void Graphics::setScissor() vkCmdSetScissor(commandBuffers.at(currentFrame), 0, 1, &scissor); } -void Graphics::setStencilMode(StencilAction action, CompareMode compare, int value, love::uint32 readmask, love::uint32 writemask) +void Graphics::setStencilState(StencilAction action, CompareMode compare, int value, love::uint32 readmask, love::uint32 writemask) { if (action != STENCIL_KEEP) { diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index 62a6cb075..89d1ba8b0 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -293,7 +293,7 @@ class Graphics final : public love::graphics::Graphics void setColor(Colorf c) override; void setScissor(const Rect &rect) override; void setScissor() override; - void setStencilMode(StencilAction action, CompareMode compare, int value, love::uint32 readmask, love::uint32 writemask) override; + void setStencilState(StencilAction action, CompareMode compare, int value, love::uint32 readmask, love::uint32 writemask) override; void setDepthMode(CompareMode compare, bool write) override; void setFrontFaceWinding(Winding winding) override; void setColorMask(ColorChannelMask mask) override; diff --git a/src/modules/graphics/wrap_Graphics.cpp b/src/modules/graphics/wrap_Graphics.cpp index 5bbeba3d9..3f5558ab4 100644 --- a/src/modules/graphics/wrap_Graphics.cpp +++ b/src/modules/graphics/wrap_Graphics.cpp @@ -608,7 +608,40 @@ int w_setStencilMode(lua_State *L) { if (lua_gettop(L) <= 1 && lua_isnoneornil(L, 1)) { - luax_catchexcept(L, [&](){ instance()->setStencilMode(); }); + luax_catchexcept(L, [&]() { instance()->setStencilMode(); }); + return 0; + } + + StencilMode mode = STENCIL_MODE_OFF; + const char *modestr = luaL_checkstring(L, 1); + if (!getConstant(modestr, mode)) + return luax_enumerror(L, "stencil mode", getConstants(mode), modestr); + + int value = (int) luaL_optinteger(L, 3, 1); + + luax_catchexcept(L, [&]() { instance()->setStencilMode(mode, value); }); + return 0; +} + +int w_getStencilMode(lua_State *L) +{ + int value = 0; + StencilMode mode = instance()->getStencilMode(value); + + const char *modestr; + if (!getConstant(mode, modestr)) + return luaL_error(L, "Unknown stencil mode."); + + lua_pushstring(L, modestr); + lua_pushinteger(L, value); + return 2; +} + +int w_setStencilState(lua_State *L) +{ + if (lua_gettop(L) <= 1 && lua_isnoneornil(L, 1)) + { + luax_catchexcept(L, [&](){ instance()->setStencilState(); }); return 0; } @@ -627,11 +660,11 @@ int w_setStencilMode(lua_State *L) uint32 readmask = (uint32) luaL_optnumber(L, 4, LOVE_UINT32_MAX); uint32 writemask = (uint32) luaL_optnumber(L, 5, LOVE_UINT32_MAX); - luax_catchexcept(L, [&](){ instance()->setStencilMode(action, compare, value, readmask, writemask); }); + luax_catchexcept(L, [&](){ instance()->setStencilState(action, compare, value, readmask, writemask); }); return 0; } -int w_getStencilMode(lua_State *L) +int w_getStencilState(lua_State *L) { StencilAction action = STENCIL_KEEP; CompareMode compare = COMPARE_ALWAYS; @@ -639,7 +672,7 @@ int w_getStencilMode(lua_State *L) uint32 readmask = LOVE_UINT32_MAX; uint32 writemask = LOVE_UINT32_MAX; - instance()->getStencilMode(action, compare, value, readmask, writemask); + instance()->getStencilState(action, compare, value, readmask, writemask); const char *actionstr; if (!getConstant(action, actionstr)) @@ -651,7 +684,7 @@ int w_getStencilMode(lua_State *L) lua_pushstring(L, actionstr); lua_pushstring(L, comparestr); - lua_pushnumber(L, value); + lua_pushinteger(L, value); lua_pushnumber(L, readmask); lua_pushnumber(L, writemask); return 5; @@ -3936,6 +3969,8 @@ static const luaL_Reg functions[] = { "setStencilMode", w_setStencilMode }, { "getStencilMode", w_getStencilMode }, + { "setStencilState", w_setStencilState }, + { "getStencilState", w_getStencilState }, { "points", w_points }, { "line", w_line }, diff --git a/src/modules/graphics/wrap_Graphics.lua b/src/modules/graphics/wrap_Graphics.lua index 6448f048f..3d6a3b259 100644 --- a/src/modules/graphics/wrap_Graphics.lua +++ b/src/modules/graphics/wrap_Graphics.lua @@ -53,7 +53,7 @@ function graphics.newVideo(file, settings) end function graphics.stencil(func, action, value, keepvalues) - love.markDeprecated(2, "love.graphics.stencil", "function", "replaced", "love.graphics.setStencilMode") + love.markDeprecated(2, "love.graphics.stencil", "function", "replaced", "love.graphics.setStencilMode or setStencilState") if not keepvalues then graphics.clear(false, true, false) @@ -61,15 +61,15 @@ function graphics.stencil(func, action, value, keepvalues) if value == nil then value = 1 end - local action2, mode2, value2, readmask2, writemask2 = graphics.getStencilMode() + local action2, mode2, value2, readmask2, writemask2 = graphics.getStencilState() local mr, mg, mb, ma = graphics.getColorMask() - graphics.setStencilMode(action, "always", value) + graphics.setStencilState(action, "always", value) graphics.setColorMask(false) local success, err = pcall(func) - graphics.setStencilMode(action2, mode2, value2, readmask2, writemask2) + graphics.setStencilState(action2, mode2, value2, readmask2, writemask2) graphics.setColorMask(mr, mg, mb, ma) if not success then @@ -78,19 +78,19 @@ function graphics.stencil(func, action, value, keepvalues) end function graphics.setStencilTest(mode, value) - love.markDeprecated(2, "love.graphics.setStencilTest", "function", "replaced", "love.graphics.setStencilMode") + love.markDeprecated(2, "love.graphics.setStencilTest", "function", "replaced", "love.graphics.setStencilMode or setStencilState") if mode ~= nil then - graphics.setStencilMode("keep", mode, value) + graphics.setStencilState("keep", mode, value) else - graphics.setStencilMode() + graphics.setStencilState() end end function graphics.getStencilTest() - love.markDeprecated(2, "love.graphics.getStencilTest", "function", "replaced", "love.graphics.getStencilMode") + love.markDeprecated(2, "love.graphics.getStencilTest", "function", "replaced", "love.graphics.getStencilMode or getStencilState") - local action, mode, value = graphics.getStencilMode() + local action, mode, value = graphics.getStencilState() return mode, value end diff --git a/testing/output/expected/love.test.graphics.setStencilMode-1.png b/testing/output/expected/love.test.graphics.setStencilState-1.png similarity index 100% rename from testing/output/expected/love.test.graphics.setStencilMode-1.png rename to testing/output/expected/love.test.graphics.setStencilState-1.png diff --git a/testing/tests/graphics.lua b/testing/tests/graphics.lua index ff6476265..ec20edb01 100644 --- a/testing/tests/graphics.lua +++ b/testing/tests/graphics.lua @@ -1929,20 +1929,20 @@ love.test.graphics.getStackDepth = function(test) end --- love.graphics.getStencilMode -love.test.graphics.getStencilMode = function(test) +-- love.graphics.getStencilState +love.test.graphics.getStencilState = function(test) -- check default vals - local action, comparemode, value = love.graphics.getStencilMode( ) + local action, comparemode, value = love.graphics.getStencilState( ) test:assertEquals('keep', action, 'check default stencil action') test:assertEquals('always', comparemode, 'check default stencil compare') test:assertEquals(0, value, 'check default stencil value') -- check set stencil values is returned - love.graphics.setStencilMode('replace', 'less', 255) - local action, comparemode, value = love.graphics.getStencilMode() + love.graphics.setStencilState('replace', 'less', 255) + local action, comparemode, value = love.graphics.getStencilState() test:assertEquals('replace', action, 'check changed stencil action') test:assertEquals('less', comparemode, 'check changed stencil compare') test:assertEquals(255, value, 'check changed stencil value') - love.graphics.setStencilMode() -- reset + love.graphics.setStencilState() -- reset end @@ -2387,18 +2387,18 @@ love.test.graphics.setShader = function(test) end --- love.graphics.setStencilMode -love.test.graphics.setStencilMode = function(test) +-- love.graphics.setStencilState +love.test.graphics.setStencilState = function(test) local canvas = love.graphics.newCanvas(16, 16) love.graphics.setCanvas({canvas, stencil=true}) love.graphics.clear(0, 0, 0, 1) - love.graphics.setStencilMode('replace', 'always', 1) + love.graphics.setStencilState('replace', 'always', 1) love.graphics.circle('fill', 8, 8, 6) - love.graphics.setStencilMode('keep', 'greater', 0) + love.graphics.setStencilState('keep', 'greater', 0) love.graphics.setColor(1, 0, 0, 1) love.graphics.rectangle('fill', 0, 0, 16, 16) love.graphics.setColor(1, 1, 1, 1) - love.graphics.setStencilMode() + love.graphics.setStencilState() love.graphics.setCanvas() local imgdata = love.graphics.readbackTexture(canvas) test:assertPixels(imgdata, {