Fix rounded rect drawing with max corner radius

This commit is contained in:
Martín Capello 2025-09-12 17:39:36 -03:00
parent 8ff57810a9
commit 7e8ccb55b8
6 changed files with 67 additions and 70 deletions

View File

@ -149,7 +149,7 @@ public:
private: private:
static int maxRadius(const Stroke& stroke) 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; bool m_modifying = false;

View File

@ -160,13 +160,19 @@ doc::AlgoLineWithAlgoPixel Intertwine::getLineAlgo(ToolLoop* loop,
} }
// static // 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) { if (fill) {
algo_circlefill(xm, ym, sx, sy, r, loop, (AlgoHLine)doPointshapeHline); algo_sliced_circlefill(x1, y1, x2, y2, r, loop, (AlgoHLine)doPointshapeHline);
} }
else { else {
algo_circle(xm, ym, sx, sy, r, loop, (AlgoPixel)doPointshapePoint); algo_sliced_circle(x1, y1, x2, y2, r, loop, (AlgoPixel)doPointshapePoint);
} }
} }

View File

@ -61,8 +61,13 @@ protected:
const Stroke::Pt& a, const Stroke::Pt& a,
const Stroke::Pt& b); const Stroke::Pt& b);
static void static void doPointshapeSlicedCircle(int x1,
doPointshapeCircle(int xm, int ym, int r, int sx, int sy, ToolLoop* loop, bool fill = false); 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); static void doPointshapeArc(int xm, int ym, double sa, double ea, int r, ToolLoop* loop);
}; };

View File

@ -197,12 +197,10 @@ public:
if (ABS(angle) < 0.001) { if (ABS(angle) < 0.001) {
int r = 0; int r = 0;
if (cornerRadius > 0) { if (cornerRadius > 0) {
int w = x2 - x1; int w = x2 - x1 + 1;
int h = y2 - y1; int h = y2 - y1 + 1;
int xm = x1 + w / 2;
int ym = y1 + h / 2;
r = std::min(w, std::min(h, 2 * cornerRadius)) / 2; 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); doPointshapeLineWithoutDynamics(x1 + r, y1, x2 - r, y1, loop);
@ -223,8 +221,8 @@ public:
doPointshapeLine(p[n - 1], p[0], loop); doPointshapeLine(p[n - 1], p[0], loop);
} }
else { else {
int w = x2 - x1; int w = x2 - x1 + 1;
int h = y2 - y1; int h = y2 - y1 + 1;
int r = std::min(w, std::min(h, 2 * cornerRadius)) / 2; int r = std::min(w, std::min(h, 2 * cornerRadius)) / 2;
Stroke p = rotateRectangle(x1, y1, x2, y2, angle, r); Stroke p = rotateRectangle(x1, y1, x2, y2, angle, r);
int n = p.size(); int n = p.size();
@ -268,12 +266,10 @@ public:
if (ABS(angle) < 0.001) { if (ABS(angle) < 0.001) {
int r = 0; int r = 0;
if (cornerRadius > 0) { if (cornerRadius > 0) {
int w = x2 - x1; int w = x2 - x1 + 1;
int h = y2 - y1; int h = y2 - y1 + 1;
int xm = x1 + w / 2;
int ym = y1 + h / 2;
r = std::min(w, std::min(h, 2 * cornerRadius)) / 2; 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++) for (y = y1; y < y1 + r; y++)
doPointshapeLineWithoutDynamics(x1 + r, y, x2 - r, y, loop); doPointshapeLineWithoutDynamics(x1 + r, y, x2 - r, y, loop);
@ -291,16 +287,22 @@ public:
doc::algorithm::polygon(v.size() / 2, &v[0], loop, (AlgoHLine)doPointshapeHline); doc::algorithm::polygon(v.size() / 2, &v[0], loop, (AlgoHLine)doPointshapeHline);
} }
else { else {
int w = x2 - x1; int w = x2 - x1 + 1;
int h = y2 - y1; int h = y2 - y1 + 1;
int r = std::min(w, std::min(h, 2 * cornerRadius)) / 2; int r = std::min(w, std::min(h, 2 * cornerRadius)) / 2;
Stroke p = rotateRectangle(x1, y1, x2, y2, angle, cornerRadius); Stroke p = rotateRectangle(x1, y1, x2, y2, angle, cornerRadius);
auto v = p.toXYInts(); auto v = p.toXYInts();
doc::algorithm::polygon(v.size() / 2, &v[0], loop, (AlgoHLine)doPointshapeHline); doc::algorithm::polygon(v.size() / 2, &v[0], loop, (AlgoHLine)doPointshapeHline);
doPointshapeCircle(p[2].x, p[2].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);
doPointshapeCircle(p[5].x, p[5].y, r, 0, 0, loop, true); doPointshapeSlicedCircle(p[5].x - r, p[5].y - r, p[5].x + r, p[5].y + r, r, loop, true);
doPointshapeCircle(p[8].x, p[8].y, r, 0, 0, loop, true); doPointshapeSlicedCircle(p[8].x - r, p[8].y - r, p[8].x + r, p[8].y + r, r, loop, true);
doPointshapeCircle(p[11].x, p[11].y, r, 0, 0, loop, true); doPointshapeSlicedCircle(p[11].x - r,
p[11].y - r,
p[11].x + r,
p[11].y + r,
r,
loop,
true);
} }
} }
} }

View File

@ -1,5 +1,5 @@
// Aseprite Document Library // 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 // Copyright (c) 2001-2018 David Capello
// //
// This file is released under the terms of the MIT license. // 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. // Adapted for Aseprite by Igara Studio S.A.
// //
// Draws a circle divided in 4 parts, separated sx pixels horizontally and sy // Draws a circle of the specified radius divided in 4 slices, adjusting each
// pixels vertically: // slice inside the specified rectangle.
// |---sx---| // |--r --|
// //
// OOO OOO // x1,y1 --> * OOO OOO
// O O // O O
// O O // O O
// T
// |
// |
// sy xm,xy
// |
// |
// _
// O O
// O O
// OOO OOO
// //
// 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 */ int x = -r, y = 0, err = 2 - 2 * r; /* II. Quadrant */
sx = sx < 0 ? 0 : sx; const int r0 = r;
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;
do { do {
proc(xm - x + offsetx + fixx, ym + y + offsety + fixy, data); /* I. Quadrant */ proc(x2 - r0 - x, y2 - r0 + y, data); /* I. Quadrant */
proc(xm - y - offsetx, ym - x + offsety + fixy, data); /* II. Quadrant */ proc(x1 + r0 - y, y2 - r0 - x, data); /* II. Quadrant */
proc(xm + x - offsetx, ym - y - offsety, data); /* III. Quadrant */ proc(x1 + r0 + x, y1 + r0 - y, data); /* III. Quadrant */
proc(xm + y + offsetx + fixx, ym + x - offsety, data); /* IV. Quadrant */ proc(x2 - r0 + y, y1 + r0 + x, data); /* IV. Quadrant */
r = err; r = err;
if (r <= y) if (r <= y)
err += ++y * 2 + 1; /* e_xy+e_y < 0 */ 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); } while (x < 0);
} }
// Same as algo_circle but with the parts filled. // Same as algo_sliced_circle but with the parts filled.
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)
{ {
int x = -r, y = 0, err = 2 - 2 * r; /* II. Quadrant */ int x = -r, y = 0, err = 2 - 2 * r; /* II. Quadrant */
sx = sx < 0 ? 0 : sx; const int r0 = r;
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;
do { do {
proc(xm, ym + y + offsety + fixy, xm - x + offsetx + fixx, data); /* I. Quadrant */ proc(x2 - r0, y2 - r0 + y, x2 - r0 - x, data); /* I. Quadrant */
proc(xm - y - offsetx, ym - x + offsety + fixy, xm, data); /* II. Quadrant */ proc(x1 + r0 - y, y2 - r0 - x, x1 + r0, data); /* II. Quadrant */
proc(xm + x - offsetx, ym - y - offsety, xm, data); /* III. Quadrant */ proc(x1 + r0 + x, y1 + r0 - y, x1 + r0, data); /* III. Quadrant */
proc(xm, ym + x - offsety, xm + y + offsetx + fixx, data); /* IV. Quadrant */ proc(x2 - r0, y1 + r0 + x, x2 - r0 + y, data); /* IV. Quadrant */
r = err; r = err;
if (r <= y) if (r <= y)
err += ++y * 2 + 1; /* e_xy+e_y < 0 */ err += ++y * 2 + 1; /* e_xy+e_y < 0 */

View File

@ -1,5 +1,5 @@
// Aseprite Document Library // 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 // Copyright (c) 2001-2018 David Capello
// //
// This file is released under the terms of the MIT license. // 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, void* data,
AlgoPixel proc); 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); void algo_arc(int xm, int ym, double sa, double ea, int r, void* data, AlgoPixel proc);