From dd49eccb50b99ca34d3cecc844e38266a0f6a6cf Mon Sep 17 00:00:00 2001 From: Daisuke Nojiri Date: Wed, 26 Aug 2015 14:47:06 -0700 Subject: cbgfx: allow draw_bitmap to render outside canvas This change allows draw_bitmap to draw an image outside the canvas with the original size if the scale parameter is zero. This is used for example when drawing a splash screen which has to be positioned at a pixel perfect location. BUG=none BRANCH=master TEST=Draw pictures and boxes on Samus and Ryu Change-Id: Ia2d8799184d1aa192e2c50850e248bee8f234006 Signed-off-by: Patrick Georgi Original-Commit-Id: 45d4717fe5c3e3554bd79b63ade490d88cf00bbe Original-Change-Id: I48aa21122cfc2ee43bcb1b8f87b00c66abdc230e Original-Signed-off-by: Daisuke Nojiri Original-Reviewed-on: https://chromium-review.googlesource.com/295961 Original-Reviewed-by: Aaron Durbin Reviewed-on: http://review.coreboot.org/11923 Tested-by: build bot (Jenkins) --- payloads/libpayload/drivers/video/graphics.c | 196 +++++++++++++++++---------- payloads/libpayload/include/cbgfx.h | 50 ++++--- 2 files changed, 159 insertions(+), 87 deletions(-) diff --git a/payloads/libpayload/drivers/video/graphics.c b/payloads/libpayload/drivers/video/graphics.c index 5f1dab7308..d9c731b064 100644 --- a/payloads/libpayload/drivers/video/graphics.c +++ b/payloads/libpayload/drivers/video/graphics.c @@ -14,8 +14,8 @@ * non-drawing areas on the left and right. The screen is assumed to be * landscape. */ -static struct vector canvas; -static uint32_t canvas_offset; /* horizontal position of canvas */ +static struct rect canvas; +static struct rect screen; /* * Framebuffer is assumed to assign a higher coordinate (larger x, y) to @@ -34,9 +34,14 @@ static char initialized = 0; */ #define BITMAP_SCALE_BASE 256 -#define ROUNDUP(x, y) ((x) + ((y) - ((x) % (y)))) +#define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) #define ABS(x) ((x) < 0 ? -(x) : (x)) +static const struct vector vzero = { + .x = 0, + .y = 0, +}; + static void add_vectors(struct vector *out, const struct vector *v1, const struct vector *v2) { @@ -44,27 +49,31 @@ static void add_vectors(struct vector *out, out->y = v1->y + v2->y; } -static void scale_vector(struct vector *out, const struct vector *in, - size_t scale, size_t base) -{ - out->x = in->x * scale / base; - out->y = in->y * scale / base; -} - -static void to_canvas(const struct vector *relative, struct vector *absolute) +/* + * Transform a vector: + * x' = x * a_x + offset_x + * y' = y * a_y + offset_y + */ +static void transform_vector(struct vector *out, + const struct vector *in, + const struct scale *a, + const struct vector *offset) { - absolute->x = canvas.width * relative->x / CANVAS_SCALE; - absolute->y = canvas.height * relative->y / CANVAS_SCALE; + out->x = a->x.nume * in->x / a->x.deno + offset->x; + out->y = a->y.nume * in->y / a->y.deno + offset->y; } /* - * Returns 1 if exclusively within canvas, or 0 if inclusively within canvas. + * Returns 1 if v is exclusively within box, 0 if v is inclusively within box, + * or -1 otherwise. Note that only the left and bottom edges are considered. */ -static int within_canvas(const struct vector *v) +static int within_box(const struct vector *v, const struct rect *bound) { - if (v->x < canvas.width && v->y < canvas.height) + if (v->x < bound->offset.x + bound->size.width && + v->y < bound->offset.y + bound->size.height) return 1; - else if (v->x <= canvas.width && v->y <= canvas.height) + else if (v->x <= bound->offset.x + bound->size.width && + v->y <= bound->offset.y + bound->size.height) return 0; else return -1; @@ -90,7 +99,7 @@ static inline void set_pixel(struct vector *coord, uint32_t color) { const int bpp = fbinfo->bits_per_pixel; int i; - uint8_t * const pixel = fbaddr + (coord->x + canvas_offset + + uint8_t * const pixel = fbaddr + (coord->x + coord->y * fbinfo->x_resolution) * bpp / 8; for (i = 0; i < bpp / 8; i++) pixel[i] = (color >> (i * 8)); @@ -113,40 +122,53 @@ static int cbgfx_init(void) if (!fbaddr) return -1; - /* calculate canvas size, assuming the screen is landscape */ - canvas.height = fbinfo->y_resolution; - canvas.width = canvas.height; - canvas_offset = (fbinfo->x_resolution - canvas.width) / 2; - if (canvas_offset < 0) { - LOG("Portrait screens are not supported\n"); + screen.size.width = fbinfo->x_resolution; + screen.size.height = fbinfo->y_resolution; + screen.offset.x = 0; + screen.offset.y = 0; + + /* Calculate canvas size & offset, assuming the screen is landscape */ + if (screen.size.height > screen.size.width) { + LOG("Portrait screen not supported\n"); return -1; } + canvas.size.height = screen.size.height; + canvas.size.width = canvas.size.height; + canvas.offset.x = (screen.size.width - canvas.size.width) / 2; + canvas.offset.y = 0; initialized = 1; - LOG("cbgfx initialized: canvas width=%d, height=%d, offset=%d\n", - canvas.width, canvas.height, canvas_offset); + LOG("cbgfx initialized: screen:width=%d, height=%d, offset=%d canvas:width=%d, height=%d, offset=%d\n", + screen.size.width, screen.size.height, screen.offset.x, + canvas.size.width, canvas.size.height, canvas.offset.x); return 0; } -int draw_box(const struct vector *top_left_rel, - const struct vector *size_rel, - const struct rgb_color *rgb) +int draw_box(const struct rect *box, const struct rgb_color *rgb) { struct vector top_left; struct vector size; struct vector p, t; const uint32_t color = calculate_color(rgb); + const struct scale top_left_s = { + .x = { .nume = box->offset.x, .deno = CANVAS_SCALE, }, + .y = { .nume = box->offset.y, .deno = CANVAS_SCALE, } + }; + const struct scale size_s = { + .x = { .nume = box->size.x, .deno = CANVAS_SCALE, }, + .y = { .nume = box->size.y, .deno = CANVAS_SCALE, } + }; if (cbgfx_init()) return CBGFX_ERROR_INIT; - to_canvas(top_left_rel, &top_left); - to_canvas(size_rel, &size); + transform_vector(&top_left, &canvas.size, &top_left_s, &canvas.offset); + transform_vector(&size, &canvas.size, &size_s, &vzero); add_vectors(&t, &top_left, &size); - if (within_canvas(&t) < 0) { + if (within_box(&t, &canvas) < 0) { LOG("Box exceeds canvas boundary\n"); - return CBGFX_ERROR_BOUNDARY; + return CBGFX_ERROR_CANVAS_BOUNDARY; } for (p.y = top_left.y; p.y < t.y; p.y++) @@ -158,23 +180,40 @@ int draw_box(const struct vector *top_left_rel, int clear_canvas(struct rgb_color *rgb) { - const struct vector coord = { - .x = 0, - .y = 0, - }; - const struct vector size = { - .width = CANVAS_SCALE, - .height = CANVAS_SCALE, + const struct rect box = { + vzero, + .size = { + .width = CANVAS_SCALE, + .height = CANVAS_SCALE, + }, }; if (cbgfx_init()) return CBGFX_ERROR_INIT; - return draw_box(&coord, &size, rgb); + return draw_box(&box, rgb); +} + +/* + * This check guarantees we will not try to read outside pixel data. + */ +static int check_bound(const struct vector *image, + const struct bitmap_header_v3 *header, + const struct scale *scale) +{ + struct vector p = { + .x = (image->width - 1) * scale->x.deno / scale->x.nume, + .y = (image->height -1) * scale->y.deno / scale->y.nume, + }; + struct rect bound = { + .offset = vzero, + .size = { .width = header->width, .height = header->height, }, + }; + return within_box(&p, &bound) < 0; } static int draw_bitmap_v3(const struct vector *top_left, - size_t scale, + const struct scale *scale, const struct vector *image, const struct bitmap_header_v3 *header, const struct bitmap_palette_element_v3 *palette, @@ -196,11 +235,14 @@ static int draw_bitmap_v3(const struct vector *top_left, LOG("Unsupported bits per pixel: %d\n", bpp); return CBGFX_ERROR_BITMAP_FORMAT; } - if (scale == 0) { + if (scale->x.nume == 0 || scale->y.nume == 0) { LOG("Scaling out of range\n"); return CBGFX_ERROR_SCALE_OUT_OF_RANGE; } + if (check_bound(image, header, scale)) + return CBGFX_ERROR_SCALE_OUT_OF_RANGE; + const int32_t y_stride = ROUNDUP(header->width * bpp / 8, 4); /* * header->height can be positive or negative. @@ -225,18 +267,11 @@ static int draw_bitmap_v3(const struct vector *top_left, */ struct vector s, d; for (d.y = 0; d.y < image->height; d.y++, p.y += dir) { - s.y = d.y * BITMAP_SCALE_BASE / scale; + s.y = d.y * scale->y.deno / scale->y.nume; const uint8_t *data = pixel_array + s.y * y_stride; p.x = top_left->x; for (d.x = 0; d.x < image->width; d.x++, p.x++) { - s.x = d.x * BITMAP_SCALE_BASE / scale; - if (s.y * y_stride + s.x > header->size) - /* - * Because we're handling integers rounded by - * divisions, we might get here legitimately - * when rendering the last row of a sane image. - */ - return CBGFX_SUCCESS; + s.x = d.x * scale->x.deno / scale->x.nume; uint8_t index = data[s.x]; if (index >= header->colors_used) { LOG("Color index exceeds palette boundary\n"); @@ -279,11 +314,11 @@ static int get_bitmap_file_header(const void *bitmap, size_t size, } static int parse_bitmap_header_v3(const uint8_t *bitmap, - const struct bitmap_file_header *file_header, - /* ^--- IN / OUT ---v */ - struct bitmap_header_v3 *header, - const struct bitmap_palette_element_v3 **palette, - const uint8_t **pixel_array) + const struct bitmap_file_header *file_header, + /* ^--- IN / OUT ---v */ + struct bitmap_header_v3 *header, + const struct bitmap_palette_element_v3 **palette, + const uint8_t **pixel_array) { struct bitmap_header_v3 *h; size_t header_offset = sizeof(struct bitmap_file_header); @@ -339,8 +374,9 @@ int draw_bitmap(const struct vector *top_left_rel, struct bitmap_header_v3 header; const struct bitmap_palette_element_v3 *palette; const uint8_t *pixel_array; - struct vector top_left, t, image; - size_t scale; + struct vector top_left, image; + struct scale scale; + struct vector t; int rv; if (cbgfx_init()) @@ -356,25 +392,43 @@ int draw_bitmap(const struct vector *top_left_rel, if (rv) return rv; - /* convert relative coordinate to canvas coordinate */ - to_canvas(top_left_rel, &top_left); - - /* convert canvas scale to self scale (relative to image width) */ - scale = scale_rel * canvas.width * BITMAP_SCALE_BASE / - (CANVAS_SCALE * header.width); + /* + * Calculate absolute coordinate and self-scale (scale relative to image + * size). If relative scale is zero, the image is displayed at the + * original scale and tol_left_rel is treated as absolute coordinate. + */ + if (scale_rel) { + const struct scale s = { + .x = { .nume = top_left_rel->x, .deno = CANVAS_SCALE, }, + .y = { .nume = top_left_rel->y, .deno = CANVAS_SCALE, }, + }; + transform_vector(&top_left, &canvas.size, &s, &canvas.offset); + scale.x.nume = scale_rel * canvas.size.width; + scale.x.deno = header.width * CANVAS_SCALE; + } else { + add_vectors(&top_left, top_left_rel, &vzero); + scale.x.nume = 1; + scale.x.deno = 1; + } + scale.y.nume = scale.x.nume; + scale.y.deno = scale.x.deno; - /* calculate height and width of the image on canvas */ + /* Calculate height and width of the image on screen */ image.width = header.width; image.height = ABS(header.height); - scale_vector(&image, &image, scale, BITMAP_SCALE_BASE); + transform_vector(&image, &image, &scale, &vzero); - /* check whether right bottom corner exceeds canvas boundaries or not */ + /* Check whether the right bottom corner is within screen and canvas */ add_vectors(&t, &image, &top_left); - if (within_canvas(&t) < 0) { + if (scale_rel && within_box(&t, &canvas) < 0) { LOG("Bitmap image exceeds canvas boundary\n"); - return CBGFX_ERROR_BOUNDARY; + return CBGFX_ERROR_CANVAS_BOUNDARY; + } + if (within_box(&t, &screen) < 0) { + LOG("Bitmap image exceeds screen boundary\n"); + return CBGFX_ERROR_SCREEN_BOUNDARY; } - return draw_bitmap_v3(&top_left, scale, &image, + return draw_bitmap_v3(&top_left, &scale, &image, &header, palette, pixel_array); } diff --git a/payloads/libpayload/include/cbgfx.h b/payloads/libpayload/include/cbgfx.h index 074a42d408..2d6b6e938a 100644 --- a/payloads/libpayload/include/cbgfx.h +++ b/payloads/libpayload/include/cbgfx.h @@ -11,13 +11,15 @@ /* * API error codes */ -#define CBGFX_SUCCESS 0 +#define CBGFX_SUCCESS 0 /* unknown error */ -#define CBGFX_ERROR_UNKNOWN 1 +#define CBGFX_ERROR_UNKNOWN 1 /* failed to initialize cbgfx library */ -#define CBGFX_ERROR_INIT 2 +#define CBGFX_ERROR_INIT 2 +/* drawing beyond screen boundary */ +#define CBGFX_ERROR_SCREEN_BOUNDARY 3 /* drawing beyond canvas boundary */ -#define CBGFX_ERROR_BOUNDARY 3 +#define CBGFX_ERROR_CANVAS_BOUNDARY 4 /* bitmap error: signature mismatch */ #define CBGFX_ERROR_BITMAP_SIGNATURE 0x10 /* bitmap error: unsupported format */ @@ -27,17 +29,32 @@ /* bitmap error: scaling out of range */ #define CBGFX_ERROR_SCALE_OUT_OF_RANGE 0x13 +struct fraction { + int32_t nume; + int32_t deno; +}; + +struct scale { + struct fraction x; + struct fraction y; +}; + struct vector { union { - uint32_t x; - uint32_t width; + int32_t x; + int32_t width; }; union { - uint32_t y; - uint32_t height; + int32_t y; + int32_t height; }; }; +struct rect { + struct vector offset; + struct vector size; +}; + struct rgb_color { uint8_t red; uint8_t green; @@ -59,16 +76,14 @@ struct rgb_color { /* * draw a box filled with a color on screen * - * top_left_rel: coordinate of top left corner of the box, relative to canvas. - * (0 - CANVAS_SCALE). - * size_rel: width and height of the box, relative to canvas. (0 - CANVAS_SCALE) + * box: .offset points the coordinate of the top left corner and .size specifies + * width and height of the box. Both are relative to the canvas size thus scale + * from 0 to CANVAS_SCALE (0 to 100%). * rgb: RGB color of the box. * * return: CBGFX_* error codes */ -int draw_box(const struct vector *top_left_rel, - const struct vector *size_rel, - const struct rgb_color *rgb); +int draw_box(const struct rect *box, const struct rgb_color *rgb); /* * Clear the canvas @@ -79,8 +94,11 @@ int clear_canvas(struct rgb_color *rgb); * Draw a bitmap image on screen. * * top_left_rel: coordinate of the top left corner of the image relative to the - * canvas (0 - CANVAS_SCALE). - * scale_rel: scale factor relative to the canvas width (0 - CANVAS_SCALE). + * canvas (0 - CANVAS_SCALE). If scale_rel is zero, this is treated as absolute + * coordinate. + * scale_rel: scale factor relative to the canvas width (0 - CANVAS_SCALE). If + * this is zero, scaling is turned off and the image is rendered with the + * original size. * bitmap: pointer to the bitmap data, starting from the file header. * size: size of the bitmap data * -- cgit v1.2.3