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:
parent
09ad206cda
commit
dd49eccb50
|
@ -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)
|
||||
/*
|
||||
* 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->y = in->y * scale / base;
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
/*
|
||||
* 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) */
|
||||
scale = scale_rel * canvas.width * BITMAP_SCALE_BASE /
|
||||
(CANVAS_SCALE * header.width);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue