summaryrefslogtreecommitdiff
path: root/payloads/libpayload/drivers/video
diff options
context:
space:
mode:
Diffstat (limited to 'payloads/libpayload/drivers/video')
-rw-r--r--payloads/libpayload/drivers/video/graphics.c156
1 files changed, 156 insertions, 0 deletions
diff --git a/payloads/libpayload/drivers/video/graphics.c b/payloads/libpayload/drivers/video/graphics.c
index 6b5664bbcf..d346e4b733 100644
--- a/payloads/libpayload/drivers/video/graphics.c
+++ b/payloads/libpayload/drivers/video/graphics.c
@@ -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 = {