theme: fix rounded-corner bug

Make top left/right corner pieces with large border thickness pixel
perfect.

Fixes: issue #988
This commit is contained in:
Johan Malm 2023-07-15 21:50:40 +01:00 committed by Johan Malm
parent b816c16701
commit 318d881650

View file

@ -454,7 +454,21 @@ rounded_rect(struct rounded_corner_ctx *ctx)
cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
cairo_paint(cairo);
/* fill */
/*
* Create outline path and fill. Illustration of top-left corner buffer:
*
* _,,ooO"""""""""+
* ,oO"' ^ |
* ,o" | |
* o" |r |
* o' | |
* O r v |
* O<--------->+ |
* O |
* O |
* O |
* +--------------------+
*/
cairo_set_line_width(cairo, 0.0);
cairo_new_sub_path(cairo);
switch (ctx->corner) {
@ -479,28 +493,102 @@ rounded_rect(struct rounded_corner_ctx *ctx)
cairo_fill_preserve(cairo);
cairo_stroke(cairo);
/* border */
cairo_set_line_cap(cairo, CAIRO_LINE_CAP_ROUND);
/*
* Stroke horizontal and vertical borders, shown by Xs and Ys
* respectively in the figure below:
*
* _,,ooO"XXXXXXXXX
* ,oO"' |
* ,o" |
* o" |
* o' |
* O |
* Y |
* Y |
* Y |
* Y |
* Y--------------------+
*/
cairo_set_line_cap(cairo, CAIRO_LINE_CAP_BUTT);
set_cairo_color(cairo, ctx->border_color);
cairo_set_line_width(cairo, ctx->line_width);
double half_line_width = ctx->line_width / 2.0;
switch (ctx->corner) {
case LAB_CORNER_TOP_LEFT:
cairo_move_to(cairo, half_line_width, h);
cairo_line_to(cairo, half_line_width, r + half_line_width);
cairo_arc(cairo, r, r, r - half_line_width, 180 * deg, 270 * deg);
cairo_line_to(cairo, half_line_width, r);
cairo_move_to(cairo, r, half_line_width);
cairo_line_to(cairo, w, half_line_width);
break;
case LAB_CORNER_TOP_RIGHT:
cairo_move_to(cairo, 0, half_line_width);
cairo_line_to(cairo, w - r, half_line_width);
cairo_arc(cairo, w - r, r, r - half_line_width, -90 * deg, 0 * deg);
cairo_move_to(cairo, w - half_line_width, r);
cairo_line_to(cairo, w - half_line_width, h);
break;
default:
wlr_log(WLR_ERROR, "unknown corner type");
}
cairo_stroke(cairo);
/*
* If radius==0 the borders stroked above go right up to (and including)
* the corners, so there is not need to do any more.
*/
if (!r) {
goto out;
}
/*
* Stroke the arc section of the border of the corner piece.
*
* Note: This figure is drawn at a more zoomed in scale compared with
* those above.
*
* ,,ooooO"" ^
* ,ooo""' | |
* ,oOO" | | line-thickness
* ,OO" | |
* ,OO" _,,ooO"" v
* ,O" ,oO"'
* ,O' ,o"
* ,O' o"
* o' o'
* O O
* O---------O +
* <----------------->
* radius
*
* We handle the edge-case where line-thickness > radius by merely
* setting line-thickness = radius and in effect drawing a quadrant of a
* circle. In this case the X and Y borders butt up against the arc and
* overlap each other (as their line-thickessnes are greater than the
* linethickness of the arc). As a result, there is no inner rounded
* corners.
*
* So, in order to have inner rounded corners cornerRadius should be
* greater than border.width.
*
* Also, see diagrams in https://github.com/labwc/labwc/pull/990
*/
double line_width = MIN(ctx->line_width, r);
cairo_set_line_width(cairo, line_width);
half_line_width = line_width / 2.0;
switch (ctx->corner) {
case LAB_CORNER_TOP_LEFT:
cairo_move_to(cairo, half_line_width, r);
cairo_arc(cairo, r, r, r - half_line_width, 180 * deg, 270 * deg);
break;
case LAB_CORNER_TOP_RIGHT:
cairo_move_to(cairo, w - r, half_line_width);
cairo_arc(cairo, w - r, r, r - half_line_width, -90 * deg, 0 * deg);
break;
default:
break;
}
cairo_stroke(cairo);
out:
cairo_surface_flush(surf);
return buffer;