From 142ce8733512e93c5f401726f1447b07e1e56d15 Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Sun, 21 Jun 2026 14:58:22 -0300 Subject: [PATCH] graphics: improve setScissor's handling of fractional DPI-scaled coordinates. --- src/common/math.h | 11 +++++++++ src/modules/graphics/Deprecations.cpp | 12 +++++----- src/modules/graphics/Graphics.cpp | 16 ++++++------- src/modules/graphics/Graphics.h | 8 +++---- src/modules/graphics/metal/Graphics.h | 2 +- src/modules/graphics/metal/Graphics.mm | 8 +++---- src/modules/graphics/opengl/Graphics.cpp | 12 +++++----- src/modules/graphics/opengl/Graphics.h | 4 ++-- src/modules/graphics/vulkan/Graphics.cpp | 12 +++++----- src/modules/graphics/vulkan/Graphics.h | 2 +- src/modules/graphics/wrap_Graphics.cpp | 30 ++++++++++++------------ 11 files changed, 64 insertions(+), 53 deletions(-) diff --git a/src/common/math.h b/src/common/math.h index 20d70a213..efc13fb44 100644 --- a/src/common/math.h +++ b/src/common/math.h @@ -72,6 +72,17 @@ struct Rect } }; +struct FRect +{ + float x, y; + float w, h; + + bool operator == (const FRect &rhs) const + { + return x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h; + } +}; + inline int nextP2(int x) { x += (x == 0); diff --git a/src/modules/graphics/Deprecations.cpp b/src/modules/graphics/Deprecations.cpp index 29942d0d3..d1bbaa07f 100644 --- a/src/modules/graphics/Deprecations.cpp +++ b/src/modules/graphics/Deprecations.cpp @@ -107,27 +107,27 @@ void Deprecations::draw(Graphics *gfx) strings.push_back({"\n(And " + std::to_string(remaining) + " more)", white}); int padding = 5; - int width = 600; + float width = 600; for (const auto &coloredstr : strings) - width = std::max(width, font->getWidth(coloredstr.str) + padding * 2); + width = std::max(width, (float)font->getWidth(coloredstr.str) + padding * 2); - float wraplimit = std::min(gfx->getWidth(), width - padding * 2); + float wraplimit = std::min((float)gfx->getWidth(), width - padding * 2); std::vector wrappedlines; font->getWrap(strings, wraplimit, wrappedlines); int linecount = std::min((int) wrappedlines.size(), maxcount); - int height = font->getHeight() * linecount + padding * 2; + float height = font->getHeight() * linecount + padding * 2; int x = 0; - int y = std::max(gfx->getHeight() - height, 0); + int y = std::max(gfx->getHeight() - height, 0.0f); gfx->setColor(Colorf(0, 0, 0, 0.85 * alpha)); gfx->rectangle(Graphics::DRAW_FILL, x, y, width, height); gfx->setColor(Colorf(1, 0.9, 0.8, 1 * alpha)); - gfx->setScissor({x, y, width, height}); + gfx->setScissor({(float)x, (float)y, width, height}); Matrix4 textm(x + padding, y + padding, 0, 1, 1, 0, 0, 0, 0); gfx->printf(strings, font.get(), wraplimit, Font::ALIGN_LEFT, textm); diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index cd6a62ec5..dbdfedb71 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -1489,9 +1489,9 @@ bool Graphics::findVertexAttributes(VertexAttributesID id, VertexAttributes &att return true; } -void Graphics::intersectScissor(const Rect &rect) +void Graphics::intersectScissor(const FRect &rect) { - Rect currect = states.back().scissorRect; + FRect currect = states.back().scissorRect; if (!states.back().scissor) { @@ -1501,17 +1501,17 @@ void Graphics::intersectScissor(const Rect &rect) currect.h = std::numeric_limits::max(); } - int x1 = std::max(currect.x, rect.x); - int y1 = std::max(currect.y, rect.y); + float x1 = std::max(currect.x, rect.x); + float y1 = std::max(currect.y, rect.y); - int x2 = std::min(currect.x + currect.w, rect.x + rect.w); - int y2 = std::min(currect.y + currect.h, rect.y + rect.h); + float x2 = std::min(currect.x + currect.w, rect.x + rect.w); + float y2 = std::min(currect.y + currect.h, rect.y + rect.h); - Rect newrect = {x1, y1, std::max(0, x2 - x1), std::max(0, y2 - y1)}; + FRect newrect = {x1, y1, std::max(0.0f, x2 - x1), std::max(0.0f, y2 - y1)}; setScissor(newrect); } -bool Graphics::getScissor(Rect &rect) const +bool Graphics::getScissor(FRect &rect) const { const DisplayState &state = states.back(); rect = state.scissorRect; diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index 0942e9ed1..0e44c3356 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -619,8 +619,8 @@ public: * and not drawn. Scissoring is automatically enabled. * @param rect The rectangle defining the scissor area. **/ - virtual void setScissor(const Rect &rect) = 0; - void intersectScissor(const Rect &rect); + virtual void setScissor(const FRect &rect) = 0; + void intersectScissor(const FRect &rect); /** * Clears any scissor that has been created. @@ -631,7 +631,7 @@ public: * Gets the current scissor box. * @return Whether the scissor is enabled. */ - bool getScissor(Rect &rect) const; + bool getScissor(FRect &rect) const; void setStencilMode(StencilMode mode, int value); void setStencilMode(); @@ -966,7 +966,7 @@ protected: float pointSize = 1.0f; bool scissor = false; - Rect scissorRect = Rect(); + FRect scissorRect = FRect(); StencilState stencil; diff --git a/src/modules/graphics/metal/Graphics.h b/src/modules/graphics/metal/Graphics.h index 10cc862db..c57684b6c 100644 --- a/src/modules/graphics/metal/Graphics.h +++ b/src/modules/graphics/metal/Graphics.h @@ -88,7 +88,7 @@ public: void setColor(Colorf c) override; - void setScissor(const Rect &rect) override; + void setScissor(const FRect &rect) override; void setScissor() override; void setStencilState(const StencilState &s) override; diff --git a/src/modules/graphics/metal/Graphics.mm b/src/modules/graphics/metal/Graphics.mm index ac63e87fa..34adb86cf 100644 --- a/src/modules/graphics/metal/Graphics.mm +++ b/src/modules/graphics/metal/Graphics.mm @@ -961,10 +961,10 @@ void Graphics::applyRenderState(id encoder, VertexAttri if (state.scissor) { double dpiscale = getCurrentDPIScale(); - rect.x = (NSUInteger)(state.scissorRect.x*dpiscale); - rect.y = (NSUInteger)(state.scissorRect.y*dpiscale); - rect.width = (NSUInteger)(state.scissorRect.w*dpiscale); - rect.height = (NSUInteger)(state.scissorRect.h*dpiscale); + rect.x = (NSUInteger)roundf(state.scissorRect.x*dpiscale); + rect.y = (NSUInteger)roundf(state.scissorRect.y*dpiscale); + rect.width = (NSUInteger)roundf(state.scissorRect.w*dpiscale); + rect.height = (NSUInteger)roundf(state.scissorRect.h*dpiscale); if (rtw > 0 && (int)rect.x >= rtw) rect.x = rtw - 1; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index b2097468f..5ba3060f8 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -1306,7 +1306,7 @@ int Graphics::getBackbufferMSAA() const return internalBackbuffer.get() ? internalBackbuffer->getMSAA() : 0; } -void Graphics::setScissor(const Rect &rect, bool rtActive) +void Graphics::setScissor(const FRect &rect, bool rtActive) { flushBatchedDraws(); @@ -1318,10 +1318,10 @@ void Graphics::setScissor(const Rect &rect, bool rtActive) double dpiscale = getCurrentDPIScale(); Rect glrect; - glrect.x = (int) (rect.x * dpiscale); - glrect.y = (int) (rect.y * dpiscale); - glrect.w = (int) (rect.w * dpiscale); - glrect.h = (int) (rect.h * dpiscale); + glrect.x = (int) roundf(rect.x * dpiscale); + glrect.y = (int) roundf(rect.y * dpiscale); + glrect.w = (int) roundf(rect.w * dpiscale); + glrect.h = (int) roundf(rect.h * dpiscale); // OpenGL's reversed y-coordinate is compensated for in OpenGL::setScissor. gl.setScissor(glrect, rtActive); @@ -1330,7 +1330,7 @@ void Graphics::setScissor(const Rect &rect, bool rtActive) state.scissorRect = rect; } -void Graphics::setScissor(const Rect &rect) +void Graphics::setScissor(const FRect &rect) { setScissor(rect, isRenderTargetActive()); } diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 08bee3853..3e3982ff3 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -84,7 +84,7 @@ public: void setColor(Colorf c) override; - void setScissor(const Rect &rect) override; + void setScissor(const FRect &rect) override; void setScissor() override; void setStencilState(const StencilState &s) override; @@ -154,7 +154,7 @@ private: void setDebug(bool enable); - void setScissor(const Rect &rect, bool rtActive); + void setScissor(const FRect &rect, bool rtActive); uint32 computePixelFormatUsage(PixelFormat format, bool readable); diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index 009614e09..5767d78d7 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -997,14 +997,14 @@ void Graphics::applyScissor() if (states.back().scissor) { - const Rect &rect = states.back().scissorRect; + const FRect &rect = states.back().scissorRect; double dpiScale = getCurrentDPIScale(); - int minScissorX = (int)(rect.x * dpiScale); - int minScissorY = (int)(rect.y * dpiScale); + int minScissorX = (int)roundf(rect.x * dpiScale); + int minScissorY = (int)roundf(rect.y * dpiScale); - int maxScissorX = minScissorX + (int)(rect.w * dpiScale) - 1; - int maxScissorY = minScissorY + (int)(rect.h * dpiScale) - 1; + int maxScissorX = minScissorX + (int)roundf(rect.w * dpiScale) - 1; + int maxScissorY = minScissorY + (int)roundf(rect.h * dpiScale) - 1; // Avoid negative offsets. int minX = std::max(scissor.offset.x, minScissorX); @@ -1030,7 +1030,7 @@ void Graphics::applyScissor() vkCmdSetScissor(commandBuffers.at(currentFrame), 0, 1, &scissor); } -void Graphics::setScissor(const Rect &rect) +void Graphics::setScissor(const FRect &rect) { flushBatchedDraws(); diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index 21226e923..33f887379 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -246,7 +246,7 @@ public: void setActive(bool active) override; int getBackbufferMSAA() const override; void setColor(Colorf c) override; - void setScissor(const Rect &rect) override; + void setScissor(const FRect &rect) override; void setScissor() override; void setStencilState(const StencilState &s) override; void setDepthMode(CompareMode compare, bool write) override; diff --git a/src/modules/graphics/wrap_Graphics.cpp b/src/modules/graphics/wrap_Graphics.cpp index ca435ae37..1822a12d8 100644 --- a/src/modules/graphics/wrap_Graphics.cpp +++ b/src/modules/graphics/wrap_Graphics.cpp @@ -568,11 +568,11 @@ int w_setScissor(lua_State *L) return 0; } - Rect rect; - rect.x = (int) luaL_checkinteger(L, 1); - rect.y = (int) luaL_checkinteger(L, 2); - rect.w = (int) luaL_checkinteger(L, 3); - rect.h = (int) luaL_checkinteger(L, 4); + FRect rect; + rect.x = (float) luaL_checknumber(L, 1); + rect.y = (float) luaL_checknumber(L, 2); + rect.w = (float) luaL_checknumber(L, 3); + rect.h = (float) luaL_checknumber(L, 4); if (rect.w < 0 || rect.h < 0) return luaL_error(L, "Can't set scissor with negative width and/or height."); @@ -583,11 +583,11 @@ int w_setScissor(lua_State *L) int w_intersectScissor(lua_State *L) { - Rect rect; - rect.x = (int) luaL_checkinteger(L, 1); - rect.y = (int) luaL_checkinteger(L, 2); - rect.w = (int) luaL_checkinteger(L, 3); - rect.h = (int) luaL_checkinteger(L, 4); + FRect rect; + rect.x = (float) luaL_checknumber(L, 1); + rect.y = (float) luaL_checknumber(L, 2); + rect.w = (float) luaL_checknumber(L, 3); + rect.h = (float) luaL_checknumber(L, 4); if (rect.w < 0 || rect.h < 0) return luaL_error(L, "Can't set scissor with negative width and/or height."); @@ -598,14 +598,14 @@ int w_intersectScissor(lua_State *L) int w_getScissor(lua_State *L) { - Rect rect; + FRect rect; if (!instance()->getScissor(rect)) return 0; - lua_pushinteger(L, rect.x); - lua_pushinteger(L, rect.y); - lua_pushinteger(L, rect.w); - lua_pushinteger(L, rect.h); + lua_pushnumber(L, rect.x); + lua_pushnumber(L, rect.y); + lua_pushnumber(L, rect.w); + lua_pushnumber(L, rect.h); return 4; }