Commit 17f6478a authored by schneider's avatar schneider
Browse files

change(blit): Allow to blit images with alpha channel

parent 2acbd18c
Pipeline #5154 passed with stages
in 57 seconds
......@@ -8,13 +8,12 @@
static void bootloader_display_splash(void)
{
gfx_copy_region(
gfx_copy_region_rle_mono(
&display_screen,
0,
0,
160,
80,
GFX_RLE_MONO,
sizeof(splash),
(const void *)(splash)
);
......
......@@ -1652,6 +1652,16 @@ enum disp_font_name {
DISP_FONT24 = 4,
};
/*
* Image data type
*/
enum epic_rgb_format {
EPIC_RGB8 = 0,
EPIC_RGBA8 = 1,
EPIC_RGB565 = 2,
EPIC_RGBA5551 = 3,
};
/**
* Prints a string into the display framebuffer with font type selectable
*
......@@ -1699,22 +1709,22 @@ API(API_DISP_PIXEL, int epic_disp_pixel(
));
/**
* Blit an image buffer to display
* Blits an image buffer to the display
*
* :param x: x position
* :param y: y position
* :param w: image width
* :param h: image height
* :param img: image data (rgb565)
* :param alpha: 8 bit alpha channel. Currently unused.
* :param w: Image width
* :param h: Image height
* :param img: Image data
* :param format: Format of the image data. One of :c:type:`epic_rgb_format`.
*/
API(API_DISP_BLIT, int epic_disp_blit(
int16_t x,
int16_t y,
int16_t w,
int16_t h,
uint16_t *img,
uint8_t *alpha
void *img,
enum epic_rgb_format format
));
/**
......
......@@ -48,14 +48,7 @@ int main(void)
epic_disp_clear(0x0000);
gfx_copy_region(
&display_screen,
0,
0,
160,
80,
GFX_RAW,
sizeof(version_splash),
version_splash
&display_screen, 0, 0, 160, 80, GFX_RGB565, version_splash
);
if (strcmp(CARD10_VERSION, "v1.16") != 0) {
......
......@@ -92,61 +92,81 @@ int epic_disp_blit(
int16_t pos_y,
int16_t width,
int16_t height,
uint16_t *img,
uint8_t *alpha
void *img,
enum epic_rgb_format format
) {
/* TODO: alpha is not supported yet */
int cl = check_lock();
if (cl < 0) {
return cl;
} else {
int16_t offset_x = (pos_x < 0) ? -pos_x : 0;
int16_t count_x = width - offset_x;
int16_t offset_y = (pos_y < 0) ? -pos_y : 0;
int16_t count_y = height - offset_y;
}
if (pos_x + width >= 160) {
count_x -= (pos_x + width) % 160;
}
if (pos_y + height >= 80) {
count_y -= (pos_y + height) % 80;
}
int16_t offset_x = (pos_x < 0) ? -pos_x : 0;
int16_t count_x = width - offset_x;
int16_t offset_y = (pos_y < 0) ? -pos_y : 0;
int16_t count_y = height - offset_y;
if (pos_x + width >= 160) {
count_x -= (pos_x + width) % 160;
}
if (pos_y + height >= 80) {
count_y -= (pos_y + height) % 80;
}
size_t bpp;
enum gfx_encoding encoding;
switch (format) {
case EPIC_RGB565:
bpp = 2;
encoding = GFX_RGB565;
break;
case EPIC_RGBA5551:
bpp = 2;
encoding = GFX_RGBA5551;
break;
case EPIC_RGB8:
bpp = 3;
encoding = GFX_RGB8;
break;
case EPIC_RGBA8:
bpp = 4;
encoding = GFX_RGBA8;
break;
default:
return -1;
break;
}
if (offset_x == 0 && offset_y == 0 && count_x == width &&
count_y == height) {
/* Simply copy full image, no cropping or alpha blending */
if (offset_x == 0 && offset_y == 0 && count_x == width &&
count_y == height) {
/* Copy full image. No cropping.*/
gfx_copy_region(
&display_screen,
pos_x,
pos_y,
width,
height,
encoding,
img
);
} else {
/* Copy cropped image line by line. */
int16_t curr_y;
for (curr_y = offset_y; curr_y < offset_y + count_y; curr_y++) {
uint8_t *line = img + (curr_y * width + offset_x) * bpp;
gfx_copy_region(
&display_screen,
pos_x,
pos_y,
width,
height,
GFX_RAW,
width * height * 2,
img
pos_x + offset_x,
pos_y + curr_y,
count_x,
1,
encoding,
line
);
} else {
/* Copy cropped image line by line */
for (int16_t curr_y = offset_y;
curr_y < offset_y + count_y;
curr_y++) {
uint16_t *curr_img =
img + (curr_y * width + offset_x);
gfx_copy_region(
&display_screen,
pos_x + offset_x,
pos_y + curr_y,
count_x,
1,
GFX_RAW,
count_x * 2,
curr_img
);
}
}
return 0;
}
return 0;
}
int epic_disp_line(
......
......@@ -120,13 +120,12 @@ static void faultsplash(const char *msg)
{
LCD_SetBacklight(100);
gfx_copy_region(
gfx_copy_region_rle_mono(
&display_screen,
0,
0,
160,
80,
GFX_RLE_MONO,
sizeof(faultsplash_rle),
faultsplash_rle
);
......
......@@ -28,8 +28,7 @@ int main(void)
0,
160,
80,
GFX_RAW,
sizeof(Heart),
GFX_RGB565,
(const void *)(Heart)
);
gfx_update(&display_screen);
......
......@@ -36,8 +36,7 @@ int main(void)
0,
160,
80,
GFX_RAW,
sizeof(Heart),
GFX_RGB565,
(const void *)(Heart)
);
gfx_update(&display_screen);
......
......@@ -43,8 +43,7 @@ int main(void)
0,
40,
40,
GFX_RAW,
40 * 40 * 2,
GFX_RGB565,
(const void *)(gImage_40X40)
);
gfx_copy_region(
......@@ -53,8 +52,7 @@ int main(void)
0,
160,
80,
GFX_RAW,
160 * 80 * 2,
GFX_RGB565,
(const void *)(gImage_160X80)
);
gfx_update(&display_screen);
......
......@@ -99,3 +99,21 @@ void fb_setpixel(struct framebuffer *fb, int x, int y, Color c)
pixel[0] = color[1];
}
}
Color fb_getpixel(struct framebuffer *fb, int x, int y)
{
uint8_t *pixel = fb_pixel(fb, x, y);
if (pixel == NULL)
return 0;
Color c;
uint8_t *color = (uint8_t *)(&c);
const size_t bpp = fb_bytes_per_pixel(fb);
switch (bpp) {
default:
case 2:
color[0] = pixel[1];
color[1] = pixel[0];
}
return c;
}
......@@ -27,6 +27,7 @@ struct framebuffer {
size_t fb_bytes_per_pixel(const struct framebuffer *fb);
void *fb_pixel(struct framebuffer *fb, int x, int y);
void fb_setpixel(struct framebuffer *fb, int x, int y, Color c);
Color fb_getpixel(struct framebuffer *fb, int x, int y);
void fb_clear_to_color(struct framebuffer *fb, Color c);
void fb_clear(struct framebuffer *fb);
Color fb_encode_color_rgb(struct framebuffer *fb, int r, int g, int b);
......
......@@ -13,6 +13,42 @@ const struct gfx_color_rgb gfx_colors_rgb[COLORS] = {
{ 255, 255, 0 } /* YELLOW */
};
static inline uint16_t rgb8_to_rgb565(const uint8_t *bytes)
{
return ((bytes[0] & 0b11111000) << 8) | ((bytes[1] & 0b11111100) << 3) |
(bytes[2] >> 3);
}
static inline void rgb565_to_rgb8(uint16_t c, uint8_t *bytes)
{
bytes[0] = (c & 0b1111100000000000) >> 8;
bytes[1] = (c & 0b0000011111100000) >> 3;
bytes[2] = (c & 0b0000000000011111) << 3;
}
static inline uint16_t rgba5551_to_rgb565(const uint8_t *bytes)
{
return (bytes[1] << 8) | (bytes[0] & 0b11000000) |
((bytes[0] & 0b00111110) >> 1);
}
static inline uint8_t apply_alpha(uint8_t in, uint8_t bg, uint8_t alpha)
{
/* Not sure if it is worth (or even a good idea) to have
* the special cases here. */
if (bg == 0) {
return (in * alpha) / 255;
}
uint8_t beta = 255 - alpha;
if (bg == 255) {
return ((in * alpha) / 255) + beta;
}
return (in * alpha + bg * beta) / 255;
}
void gfx_setpixel(struct gfx_region *r, int x, int y, Color c)
{
if (x < 0 || y < 0)
......@@ -23,6 +59,25 @@ void gfx_setpixel(struct gfx_region *r, int x, int y, Color c)
fb_setpixel(r->fb, r->x + x, r->y + y, c);
}
void gfx_setpixel_rgba(struct gfx_region *r, int x, int y, const uint8_t *c)
{
if (x < 0 || y < 0)
return;
if ((size_t)x >= r->width || (size_t)y >= r->height)
return;
uint8_t pixel[3];
Color p = fb_getpixel(r->fb, r->x + x, r->y + y);
rgb565_to_rgb8(p, pixel);
uint8_t alpha = c[3];
pixel[0] = apply_alpha(c[0], pixel[0], alpha);
pixel[1] = apply_alpha(c[1], pixel[1], alpha);
pixel[2] = apply_alpha(c[2], pixel[2], alpha);
fb_setpixel(r->fb, r->x + x, r->y + y, rgb8_to_rgb565(pixel));
}
struct gfx_region gfx_screen(struct framebuffer *fb)
{
struct gfx_region r = { .fb = fb,
......@@ -270,61 +325,48 @@ Color gfx_color(struct gfx_region *reg, enum gfx_color color)
return gfx_color_rgb(reg, c->r, c->g, c->b);
}
static void gfx_copy_region_raw(
void gfx_copy_region(
struct gfx_region *reg,
int x,
int y,
int w,
int h,
size_t size,
const void *p
enum gfx_encoding encoding,
const uint8_t *p
) {
size_t bpp = size / (w * h);
for (int y_ = 0; y_ < h; y_++) {
for (int x_ = 0; x_ < w; x_++) {
Color c;
uint8_t alpha;
switch (bpp) {
default:
case 2:
switch (encoding) {
case GFX_RGB565:
/* Assuming alignment here */
c = *(const uint16_t *)(p);
gfx_setpixel(reg, x + x_, y + y_, c);
p += 2;
break;
case GFX_RGB8:
c = rgb8_to_rgb565(p);
gfx_setpixel(reg, x + x_, y + y_, c);
p += 3;
break;
case GFX_RGBA5551:
c = rgba5551_to_rgb565(p);
alpha = (*p) & 1;
if (alpha) {
gfx_setpixel(reg, x + x_, y + y_, c);
}
p += 2;
break;
case GFX_RGBA8:
gfx_setpixel_rgba(reg, x + x_, y + y_, p);
p += 4;
break;
default:
return;
break;
}
gfx_setpixel(reg, x + x_, y + y_, c);
p += bpp;
}
}
}
static void gfx_copy_region_mono(
struct gfx_region *reg,
int x,
int y,
int w,
int h,
size_t size,
const void *p
) {
const char *bp = p;
int bit = 0;
Color white = gfx_color(reg, WHITE);
Color black = gfx_color(reg, BLACK);
for (int y_ = 0; y_ < h; y_++) {
for (int x_ = 0; x_ < w; x_++) {
int value = *bp & (1 << bit);
if (++bit >= 8) {
bp++;
bit %= 8;
if ((const void *)(bp) >= (p + size))
return;
}
Color c = value ? white : black;
gfx_setpixel(reg, x + x_, y + y_, c);
}
}
}
......@@ -336,7 +378,7 @@ static void gfx_copy_region_mono(
* significant bit determines the color, the remaining 7 bits determine the
* amount.
*/
static void gfx_copy_region_rle_mono(
void gfx_copy_region_rle_mono(
struct gfx_region *reg,
int x,
int y,
......@@ -363,31 +405,6 @@ static void gfx_copy_region_rle_mono(
}
}
void gfx_copy_region(
struct gfx_region *reg,
int x,
int y,
int w,
int h,
enum gfx_encoding encoding,
size_t size,
const void *p
) {
switch (encoding) {
case GFX_RAW:
gfx_copy_region_raw(reg, x, y, w, h, size, p);
break;
case GFX_MONO:
gfx_copy_region_mono(reg, x, y, w, h, size, p);
break;
case GFX_RLE_MONO:
gfx_copy_region_rle_mono(reg, x, y, w, h, size, p);
break;
default:
break;
}
}
void gfx_copy_raw(struct gfx_region *reg, const void *p, size_t size)
{
fb_copy_raw(reg->fb, p, size);
......
......@@ -44,9 +44,12 @@ enum gfx_color {
};
enum gfx_encoding {
GFX_RAW,
GFX_RGB565,
GFX_MONO,
GFX_RLE_MONO
GFX_RLE_MONO,
GFX_RGB8,
GFX_RGBA8,
GFX_RGBA5551
};
struct gfx_color_rgb {
......@@ -57,9 +60,10 @@ struct gfx_color_rgb {
Color gfx_color(struct gfx_region *reg, enum gfx_color color);
void gfx_copy_region(struct gfx_region *reg, int x, int y, int w, int h,
enum gfx_encoding encoding, size_t size,
const void *p);
void gfx_copy_region_rle_mono(struct gfx_region *reg, int x, int y, int w, int h,
size_t size, const void *p);
void gfx_copy_region( struct gfx_region *reg, int x, int y, int w, int h,
enum gfx_encoding encoding, const uint8_t *p);
void gfx_copy_raw(struct gfx_region *reg, const void *p, size_t size);
#endif
......@@ -8,6 +8,11 @@ FONT16 = 2
FONT20 = 3
FONT24 = 4
RGB8 = 0
RGBA8 = 1
RGB565 = 2
RGBA5551 = 3
class Display:
"""
......@@ -120,7 +125,7 @@ class Display:
sys_display.pixel(x, y, col)
return self
def blit(self, x, y, w, h, img, rgb565=False, alpha=None):
def blit(self, x, y, w, h, img, format=RGB8):
"""
Draws an image on the display.
......@@ -128,16 +133,20 @@ class Display:
:param y: Y coordinate
:param w: Image width
:param h: Image height
:param img: Buffer with pixel data. Default format is RGB with 8 bits per channel.
:param alpha: Alpha mask for `img`
:param rgb565: Set to `True` if the data supplied is in rgb565 format instead of 8 bit RGB.
:param img: Buffer with pixel data
:param format: Format of the RGB data. One of ``display.RGB8``, ``
- ``display.RGB8``: 24 bit RGB.
- ``display.RGBA8``: 24 bit RGB + 8 bit alpha.
- ``display.RGB565``: 16 bit RGB. This consumes 1 byte less RAM per pixel than ``display.RGB8``.
- ``display.RGBA5551``: 15 bit RGB + 1 bit alpha.
Default is ``display.RGB8``.
.. note::
Alpha mask support is not yet implemented.
.. versionadded:: 1.17
**Example with RGB data:**
**Example with RGB8 data:**
.. code-block:: python
......@@ -153,7 +162,7 @@ class Display:
d.update()
**Example with rgb565 data:**
**Example with RGB565 data:**
.. code-block:: python
......@@ -165,7 +174,7 @@ class Display:
img = array.array('H', [0x07E0 for x in range(32 * 20)])
with display.open() as d:
d.clear()
d.blit(10, 10, 32, 20, img, rgb565=True)
d.blit(10, 10, 32, 20, img, format=display.RGB565)
d.update()
......@@ -180,20 +189,13 @@ class Display:
f = framebuf.FrameBuffer(bytearray(160 * 80 * 2), 160, 80, framebuf.RGB565)
with display.open() as d:
f.text("Hello World", 0, 0, 0xF800) # red
d.blit(0, 0, 160, 80, f, rgb565=True)
d.blit(0, 0, 160, 80, f, format=display.RGB565)
d.update()
"""
# TODO: alpha is not yet supported by epicardium
if alpha is not None:
raise ValueError("alpha not yet supported")
if alpha is None:
sys_display.blit(x, y, w, h, img, rgb565)
else:
sys_display.blit(x, y, w, h, img, rgb565, alpha)
sys_display.blit(x, y, w, h, img, format)
return self
......
......@@ -97,14 +97,11 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(
/* blit image to display */
static mp_obj_t mp_display_blit(size_t n_args, const mp_obj_t *args)
{
/* Required arguments: posx, posy (on display),
width, height (of image),
buffer (rgb data of image) */
int pos_x = mp_obj_get_int(args[0]);
int pos_y = mp_obj_get_int(args[1]);
int width