libpayload: cbgfx: Support drawing a box with rounded corners
A function draw_rounded_box() is added to draw a box with rounded corners. In addition, this function is different from draw_box() in 2 ways: - The position and size arguments are relative to the canvas. - This function supports drawing only the border of a box (linear time complexity when the thickness is fixed). BRANCH=none BUG=b:146105976 TEST=emerge-nami libpayload Change-Id: Ie480410d2fd8316462d5ff874999ae2317de04f9 Signed-off-by: Yu-Ping Wu <yupingso@chromium.org> Reviewed-on: https://review.coreboot.org/c/coreboot/+/37757 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Hung-Te Lin <hungte@chromium.org>
This commit is contained in:
parent
8eef3bf3ca
commit
a26986e1a7
|
@ -243,6 +243,162 @@ int draw_box(const struct rect *box, const struct rgb_color *rgb)
|
|||
return CBGFX_SUCCESS;
|
||||
}
|
||||
|
||||
int draw_rounded_box(const struct scale *pos_rel, const struct scale *dim_rel,
|
||||
const struct rgb_color *rgb,
|
||||
const struct fraction *thickness,
|
||||
const struct fraction *radius)
|
||||
{
|
||||
struct vector top_left;
|
||||
struct vector size;
|
||||
struct vector p, t;
|
||||
|
||||
if (cbgfx_init())
|
||||
return CBGFX_ERROR_INIT;
|
||||
|
||||
const uint32_t color = calculate_color(rgb, 0);
|
||||
|
||||
transform_vector(&top_left, &canvas.size, pos_rel, &canvas.offset);
|
||||
transform_vector(&size, &canvas.size, dim_rel, &vzero);
|
||||
add_vectors(&t, &top_left, &size);
|
||||
if (within_box(&t, &canvas) < 0) {
|
||||
LOG("Box exceeds canvas boundary\n");
|
||||
return CBGFX_ERROR_BOUNDARY;
|
||||
}
|
||||
|
||||
if (!is_valid_fraction(thickness) || !is_valid_fraction(radius))
|
||||
return CBGFX_ERROR_INVALID_PARAMETER;
|
||||
|
||||
struct scale thickness_scale = {
|
||||
.x = { .n = thickness->n, .d = thickness->d },
|
||||
.y = { .n = thickness->n, .d = thickness->d },
|
||||
};
|
||||
struct scale radius_scale = {
|
||||
.x = { .n = radius->n, .d = radius->d },
|
||||
.y = { .n = radius->n, .d = radius->d },
|
||||
};
|
||||
struct vector d, r, s;
|
||||
transform_vector(&d, &canvas.size, &thickness_scale, &vzero);
|
||||
transform_vector(&r, &canvas.size, &radius_scale, &vzero);
|
||||
const uint8_t has_thickness = d.x > 0 && d.y > 0;
|
||||
if (thickness->n != 0 && !has_thickness)
|
||||
LOG("Thickness truncated to 0\n");
|
||||
const uint8_t has_radius = r.x > 0 && r.y > 0;
|
||||
if (radius->n != 0 && !has_radius)
|
||||
LOG("Radius truncated to 0\n");
|
||||
if (has_radius) {
|
||||
if (d.x > r.x || d.y > r.y) {
|
||||
LOG("Thickness cannot be greater than radius\n");
|
||||
return CBGFX_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
if (r.x * 2 > t.x - top_left.x || r.y * 2 > t.y - top_left.y) {
|
||||
LOG("Radius cannot be greater than half of the box\n");
|
||||
return CBGFX_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 1: Draw edges */
|
||||
int32_t x_begin, x_end;
|
||||
if (has_thickness) {
|
||||
/* top */
|
||||
for (p.y = top_left.y; p.y < top_left.y + d.y; p.y++)
|
||||
for (p.x = top_left.x + r.x; p.x < t.x - r.x; p.x++)
|
||||
set_pixel(&p, color);
|
||||
/* bottom */
|
||||
for (p.y = t.y - d.y; p.y < t.y; p.y++)
|
||||
for (p.x = top_left.x + r.x; p.x < t.x - r.x; p.x++)
|
||||
set_pixel(&p, color);
|
||||
for (p.y = top_left.y + r.y; p.y < t.y - r.y; p.y++) {
|
||||
/* left */
|
||||
for (p.x = top_left.x; p.x < top_left.x + d.x; p.x++)
|
||||
set_pixel(&p, color);
|
||||
/* right */
|
||||
for (p.x = t.x - d.x; p.x < t.x; p.x++)
|
||||
set_pixel(&p, color);
|
||||
}
|
||||
} else {
|
||||
/* Fill the regions except circular sectors */
|
||||
for (p.y = top_left.y; p.y < t.y; p.y++) {
|
||||
if (p.y >= top_left.y + r.y && p.y < t.y - r.y) {
|
||||
x_begin = top_left.x;
|
||||
x_end = t.x;
|
||||
} else {
|
||||
x_begin = top_left.x + r.x;
|
||||
x_end = t.x - r.x;
|
||||
}
|
||||
for (p.x = x_begin; p.x < x_end; p.x++)
|
||||
set_pixel(&p, color);
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_radius)
|
||||
return CBGFX_SUCCESS;
|
||||
|
||||
/*
|
||||
* Step 2: Draw rounded corners
|
||||
* When has_thickness, only the border is drawn. With fixed thickness,
|
||||
* the time complexity is linear to the size of the box.
|
||||
*/
|
||||
if (has_thickness) {
|
||||
s.x = r.x - d.x;
|
||||
s.y = r.y - d.y;
|
||||
} else {
|
||||
s.x = 0;
|
||||
s.y = 0;
|
||||
}
|
||||
|
||||
/* Use 64 bits to avoid overflow */
|
||||
int32_t x, y;
|
||||
uint64_t yy;
|
||||
const uint64_t rrx = r.x * r.x, rry = r.y * r.y;
|
||||
const uint64_t ssx = s.x * s.x, ssy = s.y * s.y;
|
||||
x_begin = 0;
|
||||
x_end = 0;
|
||||
for (y = r.y - 1; y >= 0; y--) {
|
||||
/*
|
||||
* The inequality is valid in the beginning of each iteration:
|
||||
* y^2 + x_end^2 < r^2
|
||||
*/
|
||||
yy = y * y;
|
||||
/* Check yy/ssy + xx/ssx < 1 */
|
||||
while (yy * ssx + x_begin * x_begin * ssy < ssx * ssy)
|
||||
x_begin++;
|
||||
/* The inequality must be valid now: y^2 + x_begin >= s^2 */
|
||||
x = x_begin;
|
||||
/* Check yy/rry + xx/rrx < 1 */
|
||||
while (x < x_end || yy * rrx + x * x * rry < rrx * rry) {
|
||||
/*
|
||||
* Example sequence of (y, x) when s = (4, 4) and
|
||||
* r = (5, 5):
|
||||
* [(4, 0), (4, 1), (4, 2), (3, 3), (2, 4),
|
||||
* (1, 4), (0, 4)].
|
||||
* If s.x==s.y r.x==r.y, then the sequence will be
|
||||
* symmetric, and x and y will range from 0 to (r-1).
|
||||
*/
|
||||
/* top left */
|
||||
p.y = top_left.y + r.y - 1 - y;
|
||||
p.x = top_left.x + r.x - 1 - x;
|
||||
set_pixel(&p, color);
|
||||
/* top right */
|
||||
p.y = top_left.y + r.y - 1 - y;
|
||||
p.x = t.x - r.x + x;
|
||||
set_pixel(&p, color);
|
||||
/* bottom left */
|
||||
p.y = t.y - r.y + y;
|
||||
p.x = top_left.x + r.x - 1 - x;
|
||||
set_pixel(&p, color);
|
||||
/* bottom right */
|
||||
p.y = t.y - r.y + y;
|
||||
p.x = t.x - r.x + x;
|
||||
set_pixel(&p, color);
|
||||
x++;
|
||||
}
|
||||
x_end = x;
|
||||
/* (x_begin <= x_end) must hold now */
|
||||
}
|
||||
|
||||
return CBGFX_SUCCESS;
|
||||
}
|
||||
|
||||
int clear_canvas(const struct rgb_color *rgb)
|
||||
{
|
||||
const struct rect box = {
|
||||
|
|
|
@ -114,6 +114,25 @@ struct rgb_color {
|
|||
*/
|
||||
int draw_box(const struct rect *box, const struct rgb_color *rgb);
|
||||
|
||||
/**
|
||||
* Draw a box with rounded corners on screen.
|
||||
*
|
||||
* @param[in] pos_rel Coordinate of the top left corner of the box relative to
|
||||
* the canvas.
|
||||
* @param[in] dim_rel Width and height of the image relative to the canvas.
|
||||
* @param[in] rgb Color of the border of the box.
|
||||
* @param[in] thickness Thickness of the border relative to the canvas. If zero
|
||||
* is given, the box will be filled with the rgb color.
|
||||
* @param[in] radius Radius of the rounded corners relative to the canvas. A
|
||||
* zero value indicates sharp corners will be drawn.
|
||||
*
|
||||
* @return CBGFX_* error codes
|
||||
*/
|
||||
int draw_rounded_box(const struct scale *pos_rel, const struct scale *dim_rel,
|
||||
const struct rgb_color *rgb,
|
||||
const struct fraction *thickness,
|
||||
const struct fraction *radius);
|
||||
|
||||
/**
|
||||
* Clear the canvas
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue