Skip to content
Snippets Groups Projects
Commit 17f6478a authored by schneider's avatar schneider
Browse files

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

parent 2acbd18c
No related branches found
No related tags found
No related merge requests found
...@@ -8,13 +8,12 @@ ...@@ -8,13 +8,12 @@
static void bootloader_display_splash(void) static void bootloader_display_splash(void)
{ {
gfx_copy_region( gfx_copy_region_rle_mono(
&display_screen, &display_screen,
0, 0,
0, 0,
160, 160,
80, 80,
GFX_RLE_MONO,
sizeof(splash), sizeof(splash),
(const void *)(splash) (const void *)(splash)
); );
......
...@@ -1652,6 +1652,16 @@ enum disp_font_name { ...@@ -1652,6 +1652,16 @@ enum disp_font_name {
DISP_FONT24 = 4, 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 * Prints a string into the display framebuffer with font type selectable
* *
...@@ -1699,22 +1709,22 @@ API(API_DISP_PIXEL, int epic_disp_pixel( ...@@ -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 x: x position
* :param y: y position * :param y: y position
* :param w: image width * :param w: Image width
* :param h: image height * :param h: Image height
* :param img: image data (rgb565) * :param img: Image data
* :param alpha: 8 bit alpha channel. Currently unused. * :param format: Format of the image data. One of :c:type:`epic_rgb_format`.
*/ */
API(API_DISP_BLIT, int epic_disp_blit( API(API_DISP_BLIT, int epic_disp_blit(
int16_t x, int16_t x,
int16_t y, int16_t y,
int16_t w, int16_t w,
int16_t h, int16_t h,
uint16_t *img, void *img,
uint8_t *alpha enum epic_rgb_format format
)); ));
/** /**
......
...@@ -48,14 +48,7 @@ int main(void) ...@@ -48,14 +48,7 @@ int main(void)
epic_disp_clear(0x0000); epic_disp_clear(0x0000);
gfx_copy_region( gfx_copy_region(
&display_screen, &display_screen, 0, 0, 160, 80, GFX_RGB565, version_splash
0,
0,
160,
80,
GFX_RAW,
sizeof(version_splash),
version_splash
); );
if (strcmp(CARD10_VERSION, "v1.16") != 0) { if (strcmp(CARD10_VERSION, "v1.16") != 0) {
......
...@@ -92,14 +92,14 @@ int epic_disp_blit( ...@@ -92,14 +92,14 @@ int epic_disp_blit(
int16_t pos_y, int16_t pos_y,
int16_t width, int16_t width,
int16_t height, int16_t height,
uint16_t *img, void *img,
uint8_t *alpha enum epic_rgb_format format
) { ) {
/* TODO: alpha is not supported yet */
int cl = check_lock(); int cl = check_lock();
if (cl < 0) { if (cl < 0) {
return cl; return cl;
} else { }
int16_t offset_x = (pos_x < 0) ? -pos_x : 0; int16_t offset_x = (pos_x < 0) ? -pos_x : 0;
int16_t count_x = width - offset_x; int16_t count_x = width - offset_x;
int16_t offset_y = (pos_y < 0) ? -pos_y : 0; int16_t offset_y = (pos_y < 0) ? -pos_y : 0;
...@@ -112,42 +112,62 @@ int epic_disp_blit( ...@@ -112,42 +112,62 @@ int epic_disp_blit(
count_y -= (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 && if (offset_x == 0 && offset_y == 0 && count_x == width &&
count_y == height) { count_y == height) {
/* Simply copy full image, no cropping or alpha blending */ /* Copy full image. No cropping.*/
gfx_copy_region( gfx_copy_region(
&display_screen, &display_screen,
pos_x, pos_x,
pos_y, pos_y,
width, width,
height, height,
GFX_RAW, encoding,
width * height * 2,
img img
); );
} else { } else {
/* Copy cropped image line by line */ /* Copy cropped image line by line. */
for (int16_t curr_y = offset_y; int16_t curr_y;
curr_y < offset_y + count_y; for (curr_y = offset_y; curr_y < offset_y + count_y; curr_y++) {
curr_y++) { uint8_t *line = img + (curr_y * width + offset_x) * bpp;
uint16_t *curr_img =
img + (curr_y * width + offset_x);
gfx_copy_region( gfx_copy_region(
&display_screen, &display_screen,
pos_x + offset_x, pos_x + offset_x,
pos_y + curr_y, pos_y + curr_y,
count_x, count_x,
1, 1,
GFX_RAW, encoding,
count_x * 2, line
curr_img
); );
} }
} }
return 0; return 0;
} }
}
int epic_disp_line( int epic_disp_line(
int16_t xstart, int16_t xstart,
......
...@@ -120,13 +120,12 @@ static void faultsplash(const char *msg) ...@@ -120,13 +120,12 @@ static void faultsplash(const char *msg)
{ {
LCD_SetBacklight(100); LCD_SetBacklight(100);
gfx_copy_region( gfx_copy_region_rle_mono(
&display_screen, &display_screen,
0, 0,
0, 0,
160, 160,
80, 80,
GFX_RLE_MONO,
sizeof(faultsplash_rle), sizeof(faultsplash_rle),
faultsplash_rle faultsplash_rle
); );
......
...@@ -28,8 +28,7 @@ int main(void) ...@@ -28,8 +28,7 @@ int main(void)
0, 0,
160, 160,
80, 80,
GFX_RAW, GFX_RGB565,
sizeof(Heart),
(const void *)(Heart) (const void *)(Heart)
); );
gfx_update(&display_screen); gfx_update(&display_screen);
......
...@@ -36,8 +36,7 @@ int main(void) ...@@ -36,8 +36,7 @@ int main(void)
0, 0,
160, 160,
80, 80,
GFX_RAW, GFX_RGB565,
sizeof(Heart),
(const void *)(Heart) (const void *)(Heart)
); );
gfx_update(&display_screen); gfx_update(&display_screen);
......
...@@ -43,8 +43,7 @@ int main(void) ...@@ -43,8 +43,7 @@ int main(void)
0, 0,
40, 40,
40, 40,
GFX_RAW, GFX_RGB565,
40 * 40 * 2,
(const void *)(gImage_40X40) (const void *)(gImage_40X40)
); );
gfx_copy_region( gfx_copy_region(
...@@ -53,8 +52,7 @@ int main(void) ...@@ -53,8 +52,7 @@ int main(void)
0, 0,
160, 160,
80, 80,
GFX_RAW, GFX_RGB565,
160 * 80 * 2,
(const void *)(gImage_160X80) (const void *)(gImage_160X80)
); );
gfx_update(&display_screen); gfx_update(&display_screen);
......
...@@ -99,3 +99,21 @@ void fb_setpixel(struct framebuffer *fb, int x, int y, Color c) ...@@ -99,3 +99,21 @@ void fb_setpixel(struct framebuffer *fb, int x, int y, Color c)
pixel[0] = color[1]; 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 { ...@@ -27,6 +27,7 @@ struct framebuffer {
size_t fb_bytes_per_pixel(const struct framebuffer *fb); size_t fb_bytes_per_pixel(const struct framebuffer *fb);
void *fb_pixel(struct framebuffer *fb, int x, int y); void *fb_pixel(struct framebuffer *fb, int x, int y);
void fb_setpixel(struct framebuffer *fb, int x, int y, Color c); 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_to_color(struct framebuffer *fb, Color c);
void fb_clear(struct framebuffer *fb); void fb_clear(struct framebuffer *fb);
Color fb_encode_color_rgb(struct framebuffer *fb, int r, int g, int b); 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] = { ...@@ -13,6 +13,42 @@ const struct gfx_color_rgb gfx_colors_rgb[COLORS] = {
{ 255, 255, 0 } /* YELLOW */ { 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) void gfx_setpixel(struct gfx_region *r, int x, int y, Color c)
{ {
if (x < 0 || y < 0) if (x < 0 || y < 0)
...@@ -23,6 +59,25 @@ void gfx_setpixel(struct gfx_region *r, int x, int y, Color c) ...@@ -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); 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 gfx_screen(struct framebuffer *fb)
{ {
struct gfx_region r = { .fb = fb, struct gfx_region r = { .fb = fb,
...@@ -270,61 +325,48 @@ Color gfx_color(struct gfx_region *reg, enum gfx_color color) ...@@ -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); 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, struct gfx_region *reg,
int x, int x,
int y, int y,
int w, int w,
int h, int h,
size_t size, enum gfx_encoding encoding,
const void *p const uint8_t *p
) { ) {
size_t bpp = size / (w * h);
for (int y_ = 0; y_ < h; y_++) { for (int y_ = 0; y_ < h; y_++) {
for (int x_ = 0; x_ < w; x_++) { for (int x_ = 0; x_ < w; x_++) {
Color c; Color c;
uint8_t alpha;
switch (bpp) { switch (encoding) {
default: case GFX_RGB565:
case 2: /* Assuming alignment here */
c = *(const uint16_t *)(p); c = *(const uint16_t *)(p);
gfx_setpixel(reg, x + x_, y + y_, c);
p += 2;
break; 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); gfx_setpixel(reg, x + x_, y + y_, c);
p += bpp;
}
}
} }
p += 2;
static void gfx_copy_region_mono( break;
struct gfx_region *reg, case GFX_RGBA8:
int x, gfx_setpixel_rgba(reg, x + x_, y + y_, p);
int y, p += 4;
int w, break;
int h, default:
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; return;
break;
} }
Color c = value ? white : black;
gfx_setpixel(reg, x + x_, y + y_, c);
} }
} }
} }
...@@ -336,7 +378,7 @@ static void gfx_copy_region_mono( ...@@ -336,7 +378,7 @@ static void gfx_copy_region_mono(
* significant bit determines the color, the remaining 7 bits determine the * significant bit determines the color, the remaining 7 bits determine the
* amount. * amount.
*/ */
static void gfx_copy_region_rle_mono( void gfx_copy_region_rle_mono(
struct gfx_region *reg, struct gfx_region *reg,
int x, int x,
int y, int y,
...@@ -363,31 +405,6 @@ static void gfx_copy_region_rle_mono( ...@@ -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) void gfx_copy_raw(struct gfx_region *reg, const void *p, size_t size)
{ {
fb_copy_raw(reg->fb, p, size); fb_copy_raw(reg->fb, p, size);
......
...@@ -44,9 +44,12 @@ enum gfx_color { ...@@ -44,9 +44,12 @@ enum gfx_color {
}; };
enum gfx_encoding { enum gfx_encoding {
GFX_RAW, GFX_RGB565,
GFX_MONO, GFX_MONO,
GFX_RLE_MONO GFX_RLE_MONO,
GFX_RGB8,
GFX_RGBA8,
GFX_RGBA5551
}; };
struct gfx_color_rgb { struct gfx_color_rgb {
...@@ -57,9 +60,10 @@ struct gfx_color_rgb { ...@@ -57,9 +60,10 @@ struct gfx_color_rgb {
Color gfx_color(struct gfx_region *reg, enum gfx_color color); Color gfx_color(struct gfx_region *reg, enum gfx_color color);
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, void gfx_copy_region( struct gfx_region *reg, int x, int y, int w, int h,
enum gfx_encoding encoding, size_t size, enum gfx_encoding encoding, const uint8_t *p);
const void *p);
void gfx_copy_raw(struct gfx_region *reg, const void *p, size_t size); void gfx_copy_raw(struct gfx_region *reg, const void *p, size_t size);
#endif #endif
...@@ -8,6 +8,11 @@ FONT16 = 2 ...@@ -8,6 +8,11 @@ FONT16 = 2
FONT20 = 3 FONT20 = 3
FONT24 = 4 FONT24 = 4
RGB8 = 0
RGBA8 = 1
RGB565 = 2
RGBA5551 = 3
class Display: class Display:
""" """
...@@ -120,7 +125,7 @@ class Display: ...@@ -120,7 +125,7 @@ class Display:
sys_display.pixel(x, y, col) sys_display.pixel(x, y, col)
return self 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. Draws an image on the display.
...@@ -128,16 +133,20 @@ class Display: ...@@ -128,16 +133,20 @@ class Display:
:param y: Y coordinate :param y: Y coordinate
:param w: Image width :param w: Image width
:param h: Image height :param h: Image height
:param img: Buffer with pixel data. Default format is RGB with 8 bits per channel. :param img: Buffer with pixel data
:param alpha: Alpha mask for `img` :param format: Format of the RGB data. One of ``display.RGB8``, ``
:param rgb565: Set to `True` if the data supplied is in rgb565 format instead of 8 bit RGB.
- ``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 .. versionadded:: 1.17
**Example with RGB data:** **Example with RGB8 data:**
.. code-block:: python .. code-block:: python
...@@ -153,7 +162,7 @@ class Display: ...@@ -153,7 +162,7 @@ class Display:
d.update() d.update()
**Example with rgb565 data:** **Example with RGB565 data:**
.. code-block:: python .. code-block:: python
...@@ -165,7 +174,7 @@ class Display: ...@@ -165,7 +174,7 @@ class Display:
img = array.array('H', [0x07E0 for x in range(32 * 20)]) img = array.array('H', [0x07E0 for x in range(32 * 20)])
with display.open() as d: with display.open() as d:
d.clear() d.clear()
d.blit(10, 10, 32, 20, img, rgb565=True) d.blit(10, 10, 32, 20, img, format=display.RGB565)
d.update() d.update()
...@@ -180,20 +189,13 @@ class Display: ...@@ -180,20 +189,13 @@ class Display:
f = framebuf.FrameBuffer(bytearray(160 * 80 * 2), 160, 80, framebuf.RGB565) f = framebuf.FrameBuffer(bytearray(160 * 80 * 2), 160, 80, framebuf.RGB565)
with display.open() as d: with display.open() as d:
f.text("Hello World", 0, 0, 0xF800) # red 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() d.update()
""" """
# TODO: alpha is not yet supported by epicardium sys_display.blit(x, y, w, h, img, format)
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)
return self return self
......
...@@ -97,14 +97,11 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( ...@@ -97,14 +97,11 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(
/* blit image to display */ /* blit image to display */
static mp_obj_t mp_display_blit(size_t n_args, const mp_obj_t *args) 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_x = mp_obj_get_int(args[0]);
int pos_y = mp_obj_get_int(args[1]); int pos_y = mp_obj_get_int(args[1]);
int width = mp_obj_get_int(args[2]); int width = mp_obj_get_int(args[2]);
int height = mp_obj_get_int(args[3]); int height = mp_obj_get_int(args[3]);
bool rgb565 = mp_obj_is_true(args[5]); enum epic_rgb_format format = mp_obj_get_int(args[5]);
mp_buffer_info_t img; mp_buffer_info_t img;
int res = 0; int res = 0;
...@@ -114,46 +111,7 @@ static mp_obj_t mp_display_blit(size_t n_args, const mp_obj_t *args) ...@@ -114,46 +111,7 @@ static mp_obj_t mp_display_blit(size_t n_args, const mp_obj_t *args)
mp_raise_TypeError("'img' does not support buffer protocol."); mp_raise_TypeError("'img' does not support buffer protocol.");
} }
int bpp = rgb565 ? 2 : 3; res = epic_disp_blit(pos_x, pos_y, width, height, img.buf, format);
if ((int)img.len < width * height * bpp) {
mp_raise_ValueError("'img' is too small.");
}
uint16_t *buf = NULL;
if (rgb565) {
buf = (uint16_t *)img.buf;
} else {
/* Will raise an exception if out of memory: */
buf = m_malloc(width * height * bpp);
for (int i = 0; i < width * height; i++) {
buf[i] = rgb888_to_rgb565(((uint8_t *)img.buf) + 3 * i);
}
}
if (n_args > 6) {
mp_buffer_info_t alpha;
/* Load alpha buffer and check size */
if (!mp_get_buffer(args[6], &alpha, MP_BUFFER_READ)) {
mp_raise_TypeError(
"'alpha' does not support buffer protocol."
);
}
if ((int)alpha.len < width * height) {
mp_raise_ValueError("'alpha' is too small.");
}
res = epic_disp_blit(
pos_x, pos_y, width, height, buf, (uint8_t *)alpha.buf
);
} else {
res = epic_disp_blit(pos_x, pos_y, width, height, buf, NULL);
}
if (!rgb565) {
/* Do not free rgb565 data. It is owned by the caller */
m_free(buf);
}
if (res < 0) { if (res < 0) {
mp_raise_OSError(-res); mp_raise_OSError(-res);
...@@ -162,7 +120,7 @@ static mp_obj_t mp_display_blit(size_t n_args, const mp_obj_t *args) ...@@ -162,7 +120,7 @@ static mp_obj_t mp_display_blit(size_t n_args, const mp_obj_t *args)
return mp_const_none; return mp_const_none;
} }
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(
display_blit_obj, 5, 6, mp_display_blit display_blit_obj, 6, 6, mp_display_blit
); );
/* set display backlight brightness */ /* set display backlight brightness */
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment