cbgfx: add pivot option to draw_bitmap
This change adds 'pivot' option to draw_bitmap. It controls the point of the image based on which the image is positioned. For example, if a pivot is set to the center of the image horizontally and vertically, the image is positioned using pos_rel as the center of the image. This feature is necessary, for example, to place a text image in the center of the screen because each image has a different width depending on the language. This change also makes draw_bitmap accept both horizontal and vertical size. If either of them is zero, the other non-zero value is used to derive the size to keep the aspect ratio. Specifying the height is necessary to keep font sizes the same when drawing text images of different lengths. draw_bitmap_direct is a variant of draw_bitmap and it draws an image using a native coordinate and the original size (as opposed to the location and the size relative to the canvas). CL:303074 has real use cases. BUG=none BRANCH=tot TEST=Tested on Samus Change-Id: I5fde69fcb5cc9dc53e827dd9fcf001a0a32748d4 Signed-off-by: Patrick Georgi <pgeorgi@google.com> Original-Commit-Id: 82a0a8b60808410652552ed3a888937724111584 Original-Change-Id: I0b0d9113ebecf14e8c70de7a3562b215f69f2d4c Original-Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org> Original-Reviewed-on: https://chromium-review.googlesource.com/302855 Reviewed-on: http://review.coreboot.org/11927 Tested-by: build bot (Jenkins)
This commit is contained in:
parent
f86515244a
commit
a11e3ff160
|
@ -25,19 +25,14 @@ static struct rect screen;
|
|||
static struct cb_framebuffer *fbinfo;
|
||||
static uint8_t *fbaddr;
|
||||
|
||||
static char initialized = 0;
|
||||
#define LOG(x...) printf("CBGFX: " x)
|
||||
|
||||
/*
|
||||
* This is the range used internally to scale bitmap images. (e.g. 128 = 50%,
|
||||
* 512 = 200%). We choose 256 so that division and multiplication become bit
|
||||
* shift operation.
|
||||
*/
|
||||
#define BITMAP_SCALE_BASE 256
|
||||
|
||||
#define PIVOT_H_MASK (PIVOT_H_LEFT|PIVOT_H_CENTER|PIVOT_H_RIGHT)
|
||||
#define PIVOT_V_MASK (PIVOT_V_TOP|PIVOT_V_CENTER|PIVOT_V_BOTTOM)
|
||||
#define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
|
||||
#define ABS(x) ((x) < 0 ? -(x) : (x))
|
||||
|
||||
static char initialized = 0;
|
||||
|
||||
static const struct vector vzero = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
|
@ -66,7 +61,7 @@ static void transform_vector(struct vector *out,
|
|||
|
||||
/*
|
||||
* 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.
|
||||
* or -1 otherwise. Note that only the right and bottom edges are examined.
|
||||
*/
|
||||
static int within_box(const struct vector *v, const struct rect *bound)
|
||||
{
|
||||
|
@ -183,7 +178,7 @@ int draw_box(const struct rect *box, const struct rgb_color *rgb)
|
|||
add_vectors(&t, &top_left, &size);
|
||||
if (within_box(&t, &canvas) < 0) {
|
||||
LOG("Box exceeds canvas boundary\n");
|
||||
return CBGFX_ERROR_CANVAS_BOUNDARY;
|
||||
return CBGFX_ERROR_BOUNDARY;
|
||||
}
|
||||
|
||||
for (p.y = top_left.y; p.y < t.y; p.y++)
|
||||
|
@ -193,7 +188,7 @@ int draw_box(const struct rect *box, const struct rgb_color *rgb)
|
|||
return CBGFX_SUCCESS;
|
||||
}
|
||||
|
||||
int clear_canvas(struct rgb_color *rgb)
|
||||
int clear_canvas(const struct rgb_color *rgb)
|
||||
{
|
||||
const struct rect box = {
|
||||
vzero,
|
||||
|
@ -209,7 +204,7 @@ int clear_canvas(struct rgb_color *rgb)
|
|||
return draw_box(&box, rgb);
|
||||
}
|
||||
|
||||
int clear_screen(struct rgb_color *rgb)
|
||||
int clear_screen(const struct rgb_color *rgb)
|
||||
{
|
||||
uint32_t color;
|
||||
struct vector p;
|
||||
|
@ -225,12 +220,9 @@ int clear_screen(struct rgb_color *rgb)
|
|||
return CBGFX_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
static int check_pixel_boundary(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,
|
||||
|
@ -280,7 +272,8 @@ static int draw_bitmap_v3(const struct vector *top_left,
|
|||
return CBGFX_ERROR_SCALE_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
if (check_bound(image, header, scale))
|
||||
/* This check guarantees we will not try to read outside pixel data. */
|
||||
if (check_pixel_boundary(image, header, scale))
|
||||
return CBGFX_ERROR_SCALE_OUT_OF_RANGE;
|
||||
|
||||
const int32_t y_stride = ROUNDUP(header->width * bpp / 8, 4);
|
||||
|
@ -431,16 +424,134 @@ static int parse_bitmap_header_v3(const uint8_t *bitmap,
|
|||
return CBGFX_SUCCESS;
|
||||
}
|
||||
|
||||
int draw_bitmap(const struct vector *top_left_rel,
|
||||
size_t scale_rel, const void *bitmap, size_t size)
|
||||
static int calculate_scale(const struct bitmap_header_v3 *header,
|
||||
const struct scale *dim_rel,
|
||||
struct scale *scale)
|
||||
{
|
||||
if (dim_rel->x.nume == 0 && dim_rel->y.nume == 0)
|
||||
return CBGFX_ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (dim_rel->x.nume > dim_rel->x.deno
|
||||
|| dim_rel->y.nume > dim_rel->y.deno)
|
||||
return CBGFX_ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (dim_rel->x.nume > 0) {
|
||||
scale->x.nume = dim_rel->x.nume * canvas.size.width;
|
||||
scale->x.deno = header->width * dim_rel->x.deno;
|
||||
}
|
||||
if (dim_rel->y.nume > 0) {
|
||||
scale->y.nume = dim_rel->y.nume * canvas.size.height;
|
||||
scale->y.deno = header->height * dim_rel->y.deno;
|
||||
}
|
||||
|
||||
if (dim_rel->y.nume == 0) {
|
||||
scale->y.nume = scale->x.nume;
|
||||
scale->y.deno = scale->x.deno;
|
||||
}
|
||||
if (dim_rel->x.nume == 0) {
|
||||
scale->x.nume = scale->y.nume;
|
||||
scale->x.deno = scale->y.deno;
|
||||
}
|
||||
|
||||
if (scale->x.deno == 0 || scale->y.deno == 0)
|
||||
return CBGFX_ERROR_INVALID_PARAMETER;
|
||||
|
||||
return CBGFX_SUCCESS;
|
||||
}
|
||||
|
||||
static void calculate_dimention(const struct bitmap_header_v3 *header,
|
||||
const struct scale *scale,
|
||||
struct vector *dim)
|
||||
{
|
||||
dim->width = header->width;
|
||||
dim->height = ABS(header->height);
|
||||
transform_vector(dim, dim, scale, &vzero);
|
||||
}
|
||||
|
||||
static int caclcuate_position(const struct vector *dim,
|
||||
const struct scale *pos_rel, uint8_t pivot,
|
||||
struct vector *top_left)
|
||||
{
|
||||
if (pos_rel->x.deno == 0 || pos_rel->y.deno == 0)
|
||||
return CBGFX_ERROR_INVALID_PARAMETER;
|
||||
|
||||
transform_vector(top_left, &canvas.size, pos_rel, &canvas.offset);
|
||||
|
||||
switch (pivot & PIVOT_H_MASK) {
|
||||
case PIVOT_H_LEFT:
|
||||
break;
|
||||
case PIVOT_H_CENTER:
|
||||
top_left->x -= dim->width / 2;
|
||||
break;
|
||||
case PIVOT_H_RIGHT:
|
||||
top_left->x -= dim->width;
|
||||
break;
|
||||
default:
|
||||
return CBGFX_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
switch (pivot & PIVOT_V_MASK) {
|
||||
case PIVOT_V_TOP:
|
||||
break;
|
||||
case PIVOT_V_CENTER:
|
||||
top_left->y -= dim->height / 2;
|
||||
break;
|
||||
case PIVOT_V_BOTTOM:
|
||||
top_left->y -= dim->height;
|
||||
break;
|
||||
default:
|
||||
return CBGFX_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return CBGFX_SUCCESS;
|
||||
}
|
||||
|
||||
static int setup_image(const struct bitmap_header_v3 *header,
|
||||
const struct scale *dim_rel,
|
||||
const struct scale *pos_rel, uint8_t pivot,
|
||||
/* ^--- IN / OUT ---v */
|
||||
struct scale *scale,
|
||||
struct vector *dim,
|
||||
struct vector *top_left)
|
||||
{
|
||||
int rv;
|
||||
|
||||
/* Calculate self-scale (scale relative to image size) */
|
||||
rv = calculate_scale(header, dim_rel, scale);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
/* Calculate height and width of the image */
|
||||
calculate_dimention(header, scale, dim);
|
||||
|
||||
/* Calculate coordinate */
|
||||
return caclcuate_position(dim, pos_rel, pivot, top_left);
|
||||
}
|
||||
|
||||
static int check_boundary(const struct vector *top_left,
|
||||
const struct vector *dim,
|
||||
const struct rect *bound)
|
||||
{
|
||||
struct vector v;
|
||||
add_vectors(&v, dim, top_left);
|
||||
if (top_left->x < bound->offset.x
|
||||
|| top_left->y < bound->offset.y
|
||||
|| within_box(&v, bound) < 0)
|
||||
return CBGFX_ERROR_BOUNDARY;
|
||||
return CBGFX_SUCCESS;
|
||||
}
|
||||
|
||||
static int do_draw_bitmap(const void *bitmap, size_t size,
|
||||
const struct scale *pos_rel, const uint8_t pivot,
|
||||
const struct vector *position,
|
||||
const struct scale *dim_rel)
|
||||
{
|
||||
struct bitmap_file_header file_header;
|
||||
struct bitmap_header_v3 header;
|
||||
const struct bitmap_palette_element_v3 *palette;
|
||||
const uint8_t *pixel_array;
|
||||
struct vector top_left, image;
|
||||
struct vector top_left, dim;
|
||||
struct scale scale;
|
||||
struct vector t;
|
||||
int rv;
|
||||
|
||||
if (cbgfx_init())
|
||||
|
@ -456,43 +567,41 @@ int draw_bitmap(const struct vector *top_left_rel,
|
|||
if (rv)
|
||||
return rv;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
/* Calculate image scale, dimention, and position */
|
||||
if (position) {
|
||||
scale.x.nume = 1;
|
||||
scale.x.deno = 1;
|
||||
scale.y.nume = 1;
|
||||
scale.y.deno = 1;
|
||||
calculate_dimention(&header, &scale, &dim);
|
||||
add_vectors(&top_left, position, &vzero);
|
||||
rv = check_boundary(&top_left, &dim, &screen);
|
||||
} else {
|
||||
rv = setup_image(&header, dim_rel, pos_rel, pivot,
|
||||
&scale, &dim, &top_left);
|
||||
if (rv)
|
||||
return rv;
|
||||
rv = check_boundary(&top_left, &dim, &canvas);
|
||||
}
|
||||
scale.y.nume = scale.x.nume;
|
||||
scale.y.deno = scale.x.deno;
|
||||
|
||||
/* Calculate height and width of the image on screen */
|
||||
image.width = header.width;
|
||||
image.height = ABS(header.height);
|
||||
transform_vector(&image, &image, &scale, &vzero);
|
||||
|
||||
/* Check whether the right bottom corner is within screen and canvas */
|
||||
add_vectors(&t, &image, &top_left);
|
||||
if (scale_rel && within_box(&t, &canvas) < 0) {
|
||||
LOG("Bitmap image exceeds canvas boundary\n");
|
||||
return CBGFX_ERROR_CANVAS_BOUNDARY;
|
||||
}
|
||||
if (within_box(&t, &screen) < 0) {
|
||||
LOG("Bitmap image exceeds screen boundary\n");
|
||||
return CBGFX_ERROR_SCREEN_BOUNDARY;
|
||||
if (rv) {
|
||||
LOG("Bitmap image exceeds screen or canvas boundary\n");
|
||||
return rv;
|
||||
}
|
||||
|
||||
return draw_bitmap_v3(&top_left, &scale, &image,
|
||||
return draw_bitmap_v3(&top_left, &scale, &dim,
|
||||
&header, palette, pixel_array);
|
||||
}
|
||||
|
||||
int draw_bitmap(const void *bitmap, size_t size,
|
||||
const struct scale *pos_rel, uint8_t pivot,
|
||||
const struct scale *dim_rel)
|
||||
{
|
||||
return do_draw_bitmap(bitmap, size, pos_rel, pivot, NULL, dim_rel);
|
||||
}
|
||||
|
||||
int draw_bitmap_direct(const void *bitmap, size_t size,
|
||||
const struct vector *position)
|
||||
{
|
||||
return do_draw_bitmap(bitmap, size, NULL, 0, position, NULL);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,10 +16,10 @@
|
|||
#define CBGFX_ERROR_UNKNOWN 1
|
||||
/* failed to initialize cbgfx library */
|
||||
#define CBGFX_ERROR_INIT 2
|
||||
/* drawing beyond screen boundary */
|
||||
#define CBGFX_ERROR_SCREEN_BOUNDARY 3
|
||||
/* drawing beyond canvas boundary */
|
||||
#define CBGFX_ERROR_CANVAS_BOUNDARY 4
|
||||
/* drawing beyond screen or canvas boundary */
|
||||
#define CBGFX_ERROR_BOUNDARY 3
|
||||
/* invalid parameter */
|
||||
#define CBGFX_ERROR_INVALID_PARAMETER 4
|
||||
/* bitmap error: signature mismatch */
|
||||
#define CBGFX_ERROR_BITMAP_SIGNATURE 0x10
|
||||
/* bitmap error: unsupported format */
|
||||
|
@ -73,7 +73,7 @@ struct rgb_color {
|
|||
* y values increasing towards bottom of screen.
|
||||
*/
|
||||
|
||||
/*
|
||||
/**
|
||||
* Load a bitmap file from cbfs
|
||||
*
|
||||
* Memory is allocated automatically and it's caller's responsibility to free it
|
||||
|
@ -85,7 +85,7 @@ struct rgb_color {
|
|||
*/
|
||||
void *load_bitmap(const char *name, size_t *size);
|
||||
|
||||
/*
|
||||
/**
|
||||
* draw a box filled with a color on screen
|
||||
*
|
||||
* box: .offset points the coordinate of the top left corner and .size specifies
|
||||
|
@ -97,29 +97,55 @@ void *load_bitmap(const char *name, size_t *size);
|
|||
*/
|
||||
int draw_box(const struct rect *box, const struct rgb_color *rgb);
|
||||
|
||||
/*
|
||||
/**
|
||||
* Clear the canvas
|
||||
*/
|
||||
int clear_canvas(struct rgb_color *rgb);
|
||||
int clear_canvas(const struct rgb_color *rgb);
|
||||
|
||||
/*
|
||||
/**
|
||||
* Clear the screen
|
||||
*/
|
||||
int clear_screen(struct rgb_color *rgb);
|
||||
int clear_screen(const struct rgb_color *rgb);
|
||||
|
||||
/*
|
||||
* Draw a bitmap image on screen.
|
||||
/**
|
||||
* Draw a bitmap image using position and size relative to the canvas
|
||||
*
|
||||
* top_left_rel: coordinate of the top left corner of the image relative to the
|
||||
* 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
|
||||
* @param[in] bitmap Pointer to the bitmap data, starting from file header
|
||||
* @param[in] size Size of the bitmap data
|
||||
* @param[in] pos_rel Coordinate of the pivot relative to the canvas
|
||||
* @param[in] pivot Pivot position. Use PIVOT_H_* and PIVOT_V_* flags.
|
||||
* @param[in] dim_rel Width and height of the image relative to the canvas
|
||||
* width and height. They must not exceed 1 (=100%). If one
|
||||
* is zero, it's derived from the other to keep the aspect
|
||||
* ratio.
|
||||
*
|
||||
* return: CBGFX_* error codes
|
||||
* @return CBGFX_* error codes
|
||||
*
|
||||
* 'Pivot' is a point of the image based on which the image is positioned.
|
||||
* For example, if a pivot is set to PIVOT_H_CENTER|PIVOT_V_CENTER, the image is
|
||||
* positioned so that pos_rel matches the center of the image.
|
||||
*/
|
||||
int draw_bitmap(const struct vector *top_left_rel,
|
||||
size_t scale_rel, const void *bitmap, size_t size);
|
||||
int draw_bitmap(const void *bitmap, size_t size,
|
||||
const struct scale *pos_rel, uint8_t pivot,
|
||||
const struct scale *dim_rel);
|
||||
|
||||
/* Pivot flags. See the draw_bitmap description. */
|
||||
#define PIVOT_H_LEFT (1 << 0)
|
||||
#define PIVOT_H_CENTER (1 << 1)
|
||||
#define PIVOT_H_RIGHT (1 << 2)
|
||||
#define PIVOT_V_TOP (1 << 3)
|
||||
#define PIVOT_V_CENTER (1 << 4)
|
||||
#define PIVOT_V_BOTTOM (1 << 5)
|
||||
|
||||
/**
|
||||
* Draw a bitmap image at screen coordinate with no scaling
|
||||
*
|
||||
* @param[in] bitmap Pointer to the bitmap data, starting from file header
|
||||
* @param[in] size Size of the bitmap data
|
||||
* @param[in] pos Screen coordinate of upper left corner of the image.
|
||||
*
|
||||
* @return CBGFX_* error codes
|
||||
*/
|
||||
int draw_bitmap_direct(const void *bitmap, size_t size,
|
||||
const struct vector *pos);
|
||||
|
||||
|
|
Loading…
Reference in New Issue