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 <pgeorgi@google.com>
Original-Commit-Id: 45d4717fe5c3e3554bd79b63ade490d88cf00bbe
Original-Change-Id: I48aa21122cfc2ee43bcb1b8f87b00c66abdc230e
Original-Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/295961
Original-Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: http://review.coreboot.org/11923
Tested-by: build bot (Jenkins)
This commit is contained in:
Daisuke Nojiri 2015-08-26 14:47:06 -07:00 committed by Patrick Georgi
parent 09ad206cda
commit dd49eccb50
2 changed files with 159 additions and 87 deletions

View File

@ -14,8 +14,8 @@
* non-drawing areas on the left and right. The screen is assumed to be * non-drawing areas on the left and right. The screen is assumed to be
* landscape. * landscape.
*/ */
static struct vector canvas; static struct rect canvas;
static uint32_t canvas_offset; /* horizontal position of canvas */ static struct rect screen;
/* /*
* Framebuffer is assumed to assign a higher coordinate (larger x, y) to * 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 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)) #define ABS(x) ((x) < 0 ? -(x) : (x))
static const struct vector vzero = {
.x = 0,
.y = 0,
};
static void add_vectors(struct vector *out, static void add_vectors(struct vector *out,
const struct vector *v1, const struct vector *v2) 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; out->y = v1->y + v2->y;
} }
static void scale_vector(struct vector *out, const struct vector *in, /*
size_t scale, size_t base) * 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)
{ {
out->x = in->x * scale / base; out->x = a->x.nume * in->x / a->x.deno + offset->x;
out->y = in->y * scale / base; out->y = a->y.nume * in->y / a->y.deno + offset->y;
}
static void to_canvas(const struct vector *relative, struct vector *absolute)
{
absolute->x = canvas.width * relative->x / CANVAS_SCALE;
absolute->y = canvas.height * relative->y / CANVAS_SCALE;
} }
/* /*
* 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; 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; return 0;
else else
return -1; return -1;
@ -90,7 +99,7 @@ static inline void set_pixel(struct vector *coord, uint32_t color)
{ {
const int bpp = fbinfo->bits_per_pixel; const int bpp = fbinfo->bits_per_pixel;
int i; 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; coord->y * fbinfo->x_resolution) * bpp / 8;
for (i = 0; i < bpp / 8; i++) for (i = 0; i < bpp / 8; i++)
pixel[i] = (color >> (i * 8)); pixel[i] = (color >> (i * 8));
@ -113,40 +122,53 @@ static int cbgfx_init(void)
if (!fbaddr) if (!fbaddr)
return -1; return -1;
/* calculate canvas size, assuming the screen is landscape */ screen.size.width = fbinfo->x_resolution;
canvas.height = fbinfo->y_resolution; screen.size.height = fbinfo->y_resolution;
canvas.width = canvas.height; screen.offset.x = 0;
canvas_offset = (fbinfo->x_resolution - canvas.width) / 2; screen.offset.y = 0;
if (canvas_offset < 0) {
LOG("Portrait screens are not supported\n"); /* Calculate canvas size & offset, assuming the screen is landscape */
if (screen.size.height > screen.size.width) {
LOG("Portrait screen not supported\n");
return -1; 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; initialized = 1;
LOG("cbgfx initialized: canvas width=%d, height=%d, offset=%d\n", LOG("cbgfx initialized: screen:width=%d, height=%d, offset=%d canvas:width=%d, height=%d, offset=%d\n",
canvas.width, canvas.height, canvas_offset); screen.size.width, screen.size.height, screen.offset.x,
canvas.size.width, canvas.size.height, canvas.offset.x);
return 0; return 0;
} }
int draw_box(const struct vector *top_left_rel, int draw_box(const struct rect *box, const struct rgb_color *rgb)
const struct vector *size_rel,
const struct rgb_color *rgb)
{ {
struct vector top_left; struct vector top_left;
struct vector size; struct vector size;
struct vector p, t; struct vector p, t;
const uint32_t color = calculate_color(rgb); 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()) if (cbgfx_init())
return CBGFX_ERROR_INIT; return CBGFX_ERROR_INIT;
to_canvas(top_left_rel, &top_left); transform_vector(&top_left, &canvas.size, &top_left_s, &canvas.offset);
to_canvas(size_rel, &size); transform_vector(&size, &canvas.size, &size_s, &vzero);
add_vectors(&t, &top_left, &size); add_vectors(&t, &top_left, &size);
if (within_canvas(&t) < 0) { if (within_box(&t, &canvas) < 0) {
LOG("Box exceeds canvas boundary\n"); 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++) 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) int clear_canvas(struct rgb_color *rgb)
{ {
const struct vector coord = { const struct rect box = {
.x = 0, vzero,
.y = 0, .size = {
}; .width = CANVAS_SCALE,
const struct vector size = { .height = CANVAS_SCALE,
.width = CANVAS_SCALE, },
.height = CANVAS_SCALE,
}; };
if (cbgfx_init()) if (cbgfx_init())
return CBGFX_ERROR_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, static int draw_bitmap_v3(const struct vector *top_left,
size_t scale, const struct scale *scale,
const struct vector *image, const struct vector *image,
const struct bitmap_header_v3 *header, const struct bitmap_header_v3 *header,
const struct bitmap_palette_element_v3 *palette, 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); LOG("Unsupported bits per pixel: %d\n", bpp);
return CBGFX_ERROR_BITMAP_FORMAT; return CBGFX_ERROR_BITMAP_FORMAT;
} }
if (scale == 0) { if (scale->x.nume == 0 || scale->y.nume == 0) {
LOG("Scaling out of range\n"); LOG("Scaling out of range\n");
return CBGFX_ERROR_SCALE_OUT_OF_RANGE; 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); const int32_t y_stride = ROUNDUP(header->width * bpp / 8, 4);
/* /*
* header->height can be positive or negative. * 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; struct vector s, d;
for (d.y = 0; d.y < image->height; d.y++, p.y += dir) { 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; const uint8_t *data = pixel_array + s.y * y_stride;
p.x = top_left->x; p.x = top_left->x;
for (d.x = 0; d.x < image->width; d.x++, p.x++) { for (d.x = 0; d.x < image->width; d.x++, p.x++) {
s.x = d.x * BITMAP_SCALE_BASE / scale; s.x = d.x * scale->x.deno / scale->x.nume;
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;
uint8_t index = data[s.x]; uint8_t index = data[s.x];
if (index >= header->colors_used) { if (index >= header->colors_used) {
LOG("Color index exceeds palette boundary\n"); 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, static int parse_bitmap_header_v3(const uint8_t *bitmap,
const struct bitmap_file_header *file_header, const struct bitmap_file_header *file_header,
/* ^--- IN / OUT ---v */ /* ^--- IN / OUT ---v */
struct bitmap_header_v3 *header, struct bitmap_header_v3 *header,
const struct bitmap_palette_element_v3 **palette, const struct bitmap_palette_element_v3 **palette,
const uint8_t **pixel_array) const uint8_t **pixel_array)
{ {
struct bitmap_header_v3 *h; struct bitmap_header_v3 *h;
size_t header_offset = sizeof(struct bitmap_file_header); 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; struct bitmap_header_v3 header;
const struct bitmap_palette_element_v3 *palette; const struct bitmap_palette_element_v3 *palette;
const uint8_t *pixel_array; const uint8_t *pixel_array;
struct vector top_left, t, image; struct vector top_left, image;
size_t scale; struct scale scale;
struct vector t;
int rv; int rv;
if (cbgfx_init()) if (cbgfx_init())
@ -356,25 +392,43 @@ int draw_bitmap(const struct vector *top_left_rel,
if (rv) if (rv)
return rv; return rv;
/* convert relative coordinate to canvas coordinate */ /*
to_canvas(top_left_rel, &top_left); * 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;
/* convert canvas scale to self scale (relative to image width) */ /* Calculate height and width of the image on screen */
scale = scale_rel * canvas.width * BITMAP_SCALE_BASE /
(CANVAS_SCALE * header.width);
/* calculate height and width of the image on canvas */
image.width = header.width; image.width = header.width;
image.height = ABS(header.height); 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); 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"); 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); &header, palette, pixel_array);
} }

View File

@ -11,13 +11,15 @@
/* /*
* API error codes * API error codes
*/ */
#define CBGFX_SUCCESS 0 #define CBGFX_SUCCESS 0
/* unknown error */ /* unknown error */
#define CBGFX_ERROR_UNKNOWN 1 #define CBGFX_ERROR_UNKNOWN 1
/* failed to initialize cbgfx library */ /* 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 */ /* drawing beyond canvas boundary */
#define CBGFX_ERROR_BOUNDARY 3 #define CBGFX_ERROR_CANVAS_BOUNDARY 4
/* bitmap error: signature mismatch */ /* bitmap error: signature mismatch */
#define CBGFX_ERROR_BITMAP_SIGNATURE 0x10 #define CBGFX_ERROR_BITMAP_SIGNATURE 0x10
/* bitmap error: unsupported format */ /* bitmap error: unsupported format */
@ -27,17 +29,32 @@
/* bitmap error: scaling out of range */ /* bitmap error: scaling out of range */
#define CBGFX_ERROR_SCALE_OUT_OF_RANGE 0x13 #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 { struct vector {
union { union {
uint32_t x; int32_t x;
uint32_t width; int32_t width;
}; };
union { union {
uint32_t y; int32_t y;
uint32_t height; int32_t height;
}; };
}; };
struct rect {
struct vector offset;
struct vector size;
};
struct rgb_color { struct rgb_color {
uint8_t red; uint8_t red;
uint8_t green; uint8_t green;
@ -59,16 +76,14 @@ struct rgb_color {
/* /*
* draw a box filled with a color on screen * draw a box filled with a color on screen
* *
* top_left_rel: coordinate of top left corner of the box, relative to canvas. * box: .offset points the coordinate of the top left corner and .size specifies
* (0 - CANVAS_SCALE). * width and height of the box. Both are relative to the canvas size thus scale
* size_rel: width and height of the box, relative to canvas. (0 - CANVAS_SCALE) * from 0 to CANVAS_SCALE (0 to 100%).
* rgb: RGB color of the box. * rgb: RGB color of the box.
* *
* return: CBGFX_* error codes * return: CBGFX_* error codes
*/ */
int draw_box(const struct vector *top_left_rel, int draw_box(const struct rect *box, const struct rgb_color *rgb);
const struct vector *size_rel,
const struct rgb_color *rgb);
/* /*
* Clear the canvas * Clear the canvas
@ -79,8 +94,11 @@ int clear_canvas(struct rgb_color *rgb);
* Draw a bitmap image on screen. * Draw a bitmap image on screen.
* *
* top_left_rel: coordinate of the top left corner of the image relative to the * top_left_rel: coordinate of the top left corner of the image relative to the
* canvas (0 - CANVAS_SCALE). * canvas (0 - CANVAS_SCALE). If scale_rel is zero, this is treated as absolute
* scale_rel: scale factor relative to the canvas width (0 - CANVAS_SCALE). * 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. * bitmap: pointer to the bitmap data, starting from the file header.
* size: size of the bitmap data * size: size of the bitmap data
* *