From 7e8ccb55b8d5e8f5301e1b6608ac9d6f1d285362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Fri, 12 Sep 2025 17:39:36 -0300 Subject: [PATCH] Fix rounded rect drawing with max corner radius --- src/app/tools/controllers.h | 2 +- src/app/tools/intertwine.cpp | 12 +++++-- src/app/tools/intertwine.h | 9 +++-- src/app/tools/intertwiners.h | 38 ++++++++++---------- src/doc/algo.cpp | 70 ++++++++++++++---------------------- src/doc/algo.h | 6 ++-- 6 files changed, 67 insertions(+), 70 deletions(-) diff --git a/src/app/tools/controllers.h b/src/app/tools/controllers.h index e14957e5e..fdce32940 100644 --- a/src/app/tools/controllers.h +++ b/src/app/tools/controllers.h @@ -149,7 +149,7 @@ public: private: static int maxRadius(const Stroke& stroke) { - return std::min(ABS(stroke[1].x - stroke[0].x), ABS(stroke[1].y - stroke[0].y)) / 2; + return std::min(ABS(stroke[1].x - stroke[0].x + 1), ABS(stroke[1].y - stroke[0].y + 1)) / 2; } bool m_modifying = false; diff --git a/src/app/tools/intertwine.cpp b/src/app/tools/intertwine.cpp index 45bf853ff..cb3106c7d 100644 --- a/src/app/tools/intertwine.cpp +++ b/src/app/tools/intertwine.cpp @@ -160,13 +160,19 @@ doc::AlgoLineWithAlgoPixel Intertwine::getLineAlgo(ToolLoop* loop, } // static -void Intertwine::doPointshapeCircle(int xm, int ym, int r, int sx, int sy, ToolLoop* loop, bool fill) +void Intertwine::doPointshapeSlicedCircle(int x1, + int y1, + int x2, + int y2, + int r, + ToolLoop* loop, + bool fill) { if (fill) { - algo_circlefill(xm, ym, sx, sy, r, loop, (AlgoHLine)doPointshapeHline); + algo_sliced_circlefill(x1, y1, x2, y2, r, loop, (AlgoHLine)doPointshapeHline); } else { - algo_circle(xm, ym, sx, sy, r, loop, (AlgoPixel)doPointshapePoint); + algo_sliced_circle(x1, y1, x2, y2, r, loop, (AlgoPixel)doPointshapePoint); } } diff --git a/src/app/tools/intertwine.h b/src/app/tools/intertwine.h index 7ada914f8..01a2337d4 100644 --- a/src/app/tools/intertwine.h +++ b/src/app/tools/intertwine.h @@ -61,8 +61,13 @@ protected: const Stroke::Pt& a, const Stroke::Pt& b); - static void - doPointshapeCircle(int xm, int ym, int r, int sx, int sy, ToolLoop* loop, bool fill = false); + static void doPointshapeSlicedCircle(int x1, + int y1, + int x2, + int y2, + int r, + ToolLoop* loop, + bool fill = false); static void doPointshapeArc(int xm, int ym, double sa, double ea, int r, ToolLoop* loop); }; diff --git a/src/app/tools/intertwiners.h b/src/app/tools/intertwiners.h index 7441ed3e3..3c6900beb 100644 --- a/src/app/tools/intertwiners.h +++ b/src/app/tools/intertwiners.h @@ -197,12 +197,10 @@ public: if (ABS(angle) < 0.001) { int r = 0; if (cornerRadius > 0) { - int w = x2 - x1; - int h = y2 - y1; - int xm = x1 + w / 2; - int ym = y1 + h / 2; + int w = x2 - x1 + 1; + int h = y2 - y1 + 1; r = std::min(w, std::min(h, 2 * cornerRadius)) / 2; - doPointshapeCircle(xm, ym, r, w - r * 2, h - r * 2, loop); + doPointshapeSlicedCircle(x1, y1, x2, y2, r, loop); } doPointshapeLineWithoutDynamics(x1 + r, y1, x2 - r, y1, loop); @@ -223,8 +221,8 @@ public: doPointshapeLine(p[n - 1], p[0], loop); } else { - int w = x2 - x1; - int h = y2 - y1; + int w = x2 - x1 + 1; + int h = y2 - y1 + 1; int r = std::min(w, std::min(h, 2 * cornerRadius)) / 2; Stroke p = rotateRectangle(x1, y1, x2, y2, angle, r); int n = p.size(); @@ -268,12 +266,10 @@ public: if (ABS(angle) < 0.001) { int r = 0; if (cornerRadius > 0) { - int w = x2 - x1; - int h = y2 - y1; - int xm = x1 + w / 2; - int ym = y1 + h / 2; + int w = x2 - x1 + 1; + int h = y2 - y1 + 1; r = std::min(w, std::min(h, 2 * cornerRadius)) / 2; - doPointshapeCircle(xm, ym, r, w - r * 2, h - r * 2, loop, true); + doPointshapeSlicedCircle(x1, y1, x2, y2, r, loop, true); for (y = y1; y < y1 + r; y++) doPointshapeLineWithoutDynamics(x1 + r, y, x2 - r, y, loop); @@ -291,16 +287,22 @@ public: doc::algorithm::polygon(v.size() / 2, &v[0], loop, (AlgoHLine)doPointshapeHline); } else { - int w = x2 - x1; - int h = y2 - y1; + int w = x2 - x1 + 1; + int h = y2 - y1 + 1; int r = std::min(w, std::min(h, 2 * cornerRadius)) / 2; Stroke p = rotateRectangle(x1, y1, x2, y2, angle, cornerRadius); auto v = p.toXYInts(); doc::algorithm::polygon(v.size() / 2, &v[0], loop, (AlgoHLine)doPointshapeHline); - doPointshapeCircle(p[2].x, p[2].y, r, 0, 0, loop, true); - doPointshapeCircle(p[5].x, p[5].y, r, 0, 0, loop, true); - doPointshapeCircle(p[8].x, p[8].y, r, 0, 0, loop, true); - doPointshapeCircle(p[11].x, p[11].y, r, 0, 0, loop, true); + doPointshapeSlicedCircle(p[2].x - r, p[2].y - r, p[2].x + r, p[2].y + r, r, loop, true); + doPointshapeSlicedCircle(p[5].x - r, p[5].y - r, p[5].x + r, p[5].y + r, r, loop, true); + doPointshapeSlicedCircle(p[8].x - r, p[8].y - r, p[8].x + r, p[8].y + r, r, loop, true); + doPointshapeSlicedCircle(p[11].x - r, + p[11].y - r, + p[11].x + r, + p[11].y + r, + r, + loop, + true); } } } diff --git a/src/doc/algo.cpp b/src/doc/algo.cpp index 37765b817..0b8d8c373 100644 --- a/src/doc/algo.cpp +++ b/src/doc/algo.cpp @@ -1,5 +1,5 @@ // Aseprite Document Library -// Copyright (c) 2018-2022 Igara Studio S.A. +// Copyright (c) 2018-2025 Igara Studio S.A. // Copyright (c) 2001-2018 David Capello // // This file is released under the terms of the MIT license. @@ -186,40 +186,30 @@ void algo_line_continuous_with_fix_for_line_brush(int x0, // // Adapted for Aseprite by Igara Studio S.A. // -// Draws a circle divided in 4 parts, separated sx pixels horizontally and sy -// pixels vertically: -// |---sx---| +// Draws a circle of the specified radius divided in 4 slices, adjusting each +// slice inside the specified rectangle. +// |--r --| // -// OOO OOO -// O O -// O O -// T -// | -// | -// sy xm,xy -// | -// | -// _ -// O O -// O O -// OOO OOO +// x1,y1 --> * OOO OOO +// O O +// O O // -// If sx and sy are 0, it draws a regular circle. -void algo_circle(int xm, int ym, int sx, int sy, int r, void* data, AlgoPixel proc) +// +// +// T O O +// r | O O +// _ OOO OOO * <-- x2,y2 +// +// If the rectangle is smaller than the circle, it doesn't make any clipping. +void algo_sliced_circle(int x1, int y1, int x2, int y2, int r, void* data, AlgoPixel proc) { int x = -r, y = 0, err = 2 - 2 * r; /* II. Quadrant */ - sx = sx < 0 ? 0 : sx; - sy = sy < 0 ? 0 : sy; - int offsetx = sx / 2; - int offsety = sy / 2; - // Fix the position when sx or sy are not even. - int fixx = sx - 2 * offsetx; - int fixy = sy - 2 * offsety; + const int r0 = r; do { - proc(xm - x + offsetx + fixx, ym + y + offsety + fixy, data); /* I. Quadrant */ - proc(xm - y - offsetx, ym - x + offsety + fixy, data); /* II. Quadrant */ - proc(xm + x - offsetx, ym - y - offsety, data); /* III. Quadrant */ - proc(xm + y + offsetx + fixx, ym + x - offsety, data); /* IV. Quadrant */ + proc(x2 - r0 - x, y2 - r0 + y, data); /* I. Quadrant */ + proc(x1 + r0 - y, y2 - r0 - x, data); /* II. Quadrant */ + proc(x1 + r0 + x, y1 + r0 - y, data); /* III. Quadrant */ + proc(x2 - r0 + y, y1 + r0 + x, data); /* IV. Quadrant */ r = err; if (r <= y) err += ++y * 2 + 1; /* e_xy+e_y < 0 */ @@ -228,22 +218,16 @@ void algo_circle(int xm, int ym, int sx, int sy, int r, void* data, AlgoPixel pr } while (x < 0); } -// Same as algo_circle but with the parts filled. -void algo_circlefill(int xm, int ym, int sx, int sy, int r, void* data, AlgoHLine proc) +// Same as algo_sliced_circle but with the parts filled. +void algo_sliced_circlefill(int x1, int y1, int x2, int y2, int r, void* data, AlgoHLine proc) { int x = -r, y = 0, err = 2 - 2 * r; /* II. Quadrant */ - sx = sx < 0 ? 0 : sx; - sy = sy < 0 ? 0 : sy; - int offsetx = sx / 2; - int offsety = sy / 2; - // Fix the position when sx or sy are not even. - int fixx = sx - 2 * offsetx; - int fixy = sy - 2 * offsety; + const int r0 = r; do { - proc(xm, ym + y + offsety + fixy, xm - x + offsetx + fixx, data); /* I. Quadrant */ - proc(xm - y - offsetx, ym - x + offsety + fixy, xm, data); /* II. Quadrant */ - proc(xm + x - offsetx, ym - y - offsety, xm, data); /* III. Quadrant */ - proc(xm, ym + x - offsety, xm + y + offsetx + fixx, data); /* IV. Quadrant */ + proc(x2 - r0, y2 - r0 + y, x2 - r0 - x, data); /* I. Quadrant */ + proc(x1 + r0 - y, y2 - r0 - x, x1 + r0, data); /* II. Quadrant */ + proc(x1 + r0 + x, y1 + r0 - y, x1 + r0, data); /* III. Quadrant */ + proc(x2 - r0, y1 + r0 + x, x2 - r0 + y, data); /* IV. Quadrant */ r = err; if (r <= y) err += ++y * 2 + 1; /* e_xy+e_y < 0 */ diff --git a/src/doc/algo.h b/src/doc/algo.h index 7093b5644..73072a03c 100644 --- a/src/doc/algo.h +++ b/src/doc/algo.h @@ -1,5 +1,5 @@ // Aseprite Document Library -// Copyright (C) 2018-2021 Igara Studio S.A. +// Copyright (C) 2018-2025 Igara Studio S.A. // Copyright (c) 2001-2018 David Capello // // This file is released under the terms of the MIT license. @@ -48,9 +48,9 @@ void algo_line_continuous_with_fix_for_line_brush(int x1, void* data, AlgoPixel proc); -void algo_circle(int xm, int ym, int sx, int sy, int r, void* data, AlgoPixel proc); +void algo_sliced_circle(int x1, int y1, int x2, int y2, int r, void* data, AlgoPixel proc); -void algo_circlefill(int xm, int ym, int sx, int sy, int r, void* data, AlgoHLine proc); +void algo_sliced_circlefill(int x1, int y1, int x2, int y2, int r, void* data, AlgoHLine proc); void algo_arc(int xm, int ym, double sa, double ea, int r, void* data, AlgoPixel proc);