diff --git a/payloads/libpayload/drivers/video/graphics.c b/payloads/libpayload/drivers/video/graphics.c index 54d3dfa2b2..b52bd99103 100644 --- a/payloads/libpayload/drivers/video/graphics.c +++ b/payloads/libpayload/drivers/video/graphics.c @@ -99,6 +99,11 @@ static void add_vectors(struct vector *out, out->y = v1->y + v2->y; } +static int fraction_equal(const struct fraction *f1, const struct fraction *f2) +{ + return (int64_t)f1->n * f2->d == (int64_t)f2->n * f1->d; +} + static int is_valid_fraction(const struct fraction *f) { return f->d != 0; @@ -109,17 +114,31 @@ static int is_valid_scale(const struct scale *s) return is_valid_fraction(&s->x) && is_valid_fraction(&s->y); } +static void reduce_fraction(struct fraction *out, int64_t n, int64_t d) +{ + /* Simplest way to reduce the fraction until fitting in int32_t */ + int shift = log2(MAX(ABS(n), ABS(d)) >> 31) + 1; + out->n = n >> shift; + out->d = d >> shift; +} + +/* out = f1 + f2 */ static void add_fractions(struct fraction *out, const struct fraction *f1, const struct fraction *f2) { - int64_t n, d; - int shift; - n = (int64_t)f1->n * f2->d + (int64_t)f2->n * f1->d; - d = (int64_t)f1->d * f2->d; - /* Simplest way to reduce the fraction until fitting in int32_t */ - shift = log2(MAX(ABS(n), ABS(d)) >> 31) + 1; - out->n = n >> shift; - out->d = d >> shift; + reduce_fraction(out, + (int64_t)f1->n * f2->d + (int64_t)f2->n * f1->d, + (int64_t)f1->d * f2->d); +} + +/* out = f1 - f2 */ +static void subtract_fractions(struct fraction *out, + const struct fraction *f1, + const struct fraction *f2) +{ + reduce_fraction(out, + (int64_t)f1->n * f2->d - (int64_t)f2->n * f1->d, + (int64_t)f1->d * f2->d); } static void add_scales(struct scale *out, @@ -477,6 +496,59 @@ int draw_rounded_box(const struct scale *pos_rel, const struct scale *dim_rel, return CBGFX_SUCCESS; } +int draw_line(const struct scale *pos1, const struct scale *pos2, + const struct fraction *thickness, const struct rgb_color *rgb) +{ + struct fraction len; + 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); + + if (!is_valid_fraction(thickness)) + return CBGFX_ERROR_INVALID_PARAMETER; + + transform_vector(&top_left, &canvas.size, pos1, &canvas.offset); + if (fraction_equal(&pos1->y, &pos2->y)) { + /* Horizontal line */ + subtract_fractions(&len, &pos2->x, &pos1->x); + struct scale dim = { + .x = { .n = len.n, .d = len.d }, + .y = { .n = thickness->n, .d = thickness->d }, + }; + transform_vector(&size, &canvas.size, &dim, &vzero); + size.y = MAX(size.y, 1); + } else if (fraction_equal(&pos1->x, &pos2->x)) { + /* Vertical line */ + subtract_fractions(&len, &pos2->y, &pos1->y); + struct scale dim = { + .x = { .n = thickness->n, .d = thickness->d }, + .y = { .n = len.n, .d = len.d }, + }; + transform_vector(&size, &canvas.size, &dim, &vzero); + size.x = MAX(size.x, 1); + } else { + LOG("Only support horizontal and vertical lines\n"); + return CBGFX_ERROR_INVALID_PARAMETER; + } + + add_vectors(&t, &top_left, &size); + if (within_box(&t, &canvas) < 0) { + LOG("Line exceeds canvas boundary\n"); + return CBGFX_ERROR_BOUNDARY; + } + + for (p.y = top_left.y; p.y < t.y; p.y++) + for (p.x = top_left.x; p.x < t.x; p.x++) + set_pixel(&p, color); + + return CBGFX_SUCCESS; +} + int clear_canvas(const struct rgb_color *rgb) { const struct rect box = { diff --git a/payloads/libpayload/include/cbgfx.h b/payloads/libpayload/include/cbgfx.h index 84e76f26d1..f2883b0c43 100644 --- a/payloads/libpayload/include/cbgfx.h +++ b/payloads/libpayload/include/cbgfx.h @@ -132,6 +132,22 @@ int draw_rounded_box(const struct scale *pos_rel, const struct scale *dim_rel, const struct fraction *thickness, const struct fraction *radius); +/** + * Draw a horizontal or vertical line segment on screen. If horizontal, pos1 + * must be the left endpoint. If vertical, pos1 must be the top endpoint. When + * the specified thickness is zero (or truncated to zero), a line with 1-pixel + * width will be drawn. + * + * @param[in] pos1 Start position of the line relative to the canvas. + * @param[in] pos2 End position of the line relative to the canvas. + * @param[in] thickness Thickness of the line relative to the canvas. + * @param[in] rgb Color of the line. + * + * @return CBGFX_* error codes + */ +int draw_line(const struct scale *pos1, const struct scale *pos2, + const struct fraction *thickness, const struct rgb_color *rgb); + /** * Clear the canvas */