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
|
* 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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue