summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Hinch <peter@hinch.me.uk>2021-08-18 20:05:25 +0100
committerDamien George <damien@micropython.org>2021-08-25 15:31:23 +1000
commit2296df0a327150f39ef5a915bf828daac3cd4658 (patch)
treeea791c3b6308ca854e1ea62442abc6b3ea2c4b19
parent996f703166d8c57122756f35f7f0185666237f2b (diff)
extmod/modframebuf: Enable blit between different formats via a palette.
This achieves a substantial performance improvement when rendering glyphs to color displays, the benefit increasing proportional to the number of pixels in the glyph.
-rw-r--r--docs/library/framebuf.rst15
-rw-r--r--extmod/modframebuf.c9
-rw-r--r--tests/extmod/framebuf_palette.py35
-rw-r--r--tests/extmod/framebuf_palette.py.exp5
4 files changed, 59 insertions, 5 deletions
diff --git a/docs/library/framebuf.rst b/docs/library/framebuf.rst
index 0073ba708..13502cc7a 100644
--- a/docs/library/framebuf.rst
+++ b/docs/library/framebuf.rst
@@ -103,16 +103,23 @@ Other methods
Shift the contents of the FrameBuffer by the given vector. This may
leave a footprint of the previous colors in the FrameBuffer.
-.. method:: FrameBuffer.blit(fbuf, x, y[, key])
+.. method:: FrameBuffer.blit(fbuf, x, y, key=-1, palette=None)
Draw another FrameBuffer on top of the current one at the given coordinates.
If *key* is specified then it should be a color integer and the
corresponding color will be considered transparent: all pixels with that
color value will not be drawn.
- This method works between FrameBuffer instances utilising different formats,
- but the resulting colors may be unexpected due to the mismatch in color
- formats.
+ The *palette* argument enables blitting between FrameBuffers with differing
+ formats. Typical usage is to render a monochrome or grayscale glyph/icon to
+ a color display. The *palette* is a FrameBuffer instance whose format is
+ that of the current FrameBuffer. The *palette* height is one pixel and its
+ pixel width is the number of colors in the source FrameBuffer. The *palette*
+ for an N-bit source needs 2**N pixels; the *palette* for a monochrome source
+ would have 2 pixels representing background and foreground colors. The
+ application assigns a color to each pixel in the *palette*. The color of the
+ current pixel will be that of that *palette* pixel whose x position is the
+ color of the corresponding source pixel.
Constants
---------
diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c
index 18b170078..454624dda 100644
--- a/extmod/modframebuf.c
+++ b/extmod/modframebuf.c
@@ -491,6 +491,10 @@ STATIC mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args) {
if (n_args > 4) {
key = mp_obj_get_int(args[4]);
}
+ mp_obj_framebuf_t *palette = NULL;
+ if (n_args > 5 && args[5] != mp_const_none) {
+ palette = MP_OBJ_TO_PTR(mp_obj_cast_to_native_base(args[5], MP_OBJ_FROM_PTR(&mp_type_framebuf)));
+ }
if (
(x >= self->width) ||
@@ -514,6 +518,9 @@ STATIC mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args) {
int cx1 = x1;
for (int cx0 = x0; cx0 < x0end; ++cx0) {
uint32_t col = getpixel(source, cx1, y1);
+ if (palette) {
+ col = getpixel(palette, col, 0);
+ }
if (col != (uint32_t)key) {
setpixel(self, cx0, y0, col);
}
@@ -523,7 +530,7 @@ STATIC mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args) {
}
return mp_const_none;
}
-STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_obj, 4, 5, framebuf_blit);
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_obj, 4, 6, framebuf_blit);
STATIC mp_obj_t framebuf_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ystep_in) {
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in);
diff --git a/tests/extmod/framebuf_palette.py b/tests/extmod/framebuf_palette.py
new file mode 100644
index 000000000..84db834c1
--- /dev/null
+++ b/tests/extmod/framebuf_palette.py
@@ -0,0 +1,35 @@
+# Test blit between different color spaces
+try:
+ import framebuf, usys
+except ImportError:
+ print("SKIP")
+ raise SystemExit
+
+# Monochrome glyph/icon
+w = 8
+h = 8
+cbuf = bytearray(w * h // 8)
+fbc = framebuf.FrameBuffer(cbuf, w, h, framebuf.MONO_HLSB)
+fbc.line(0, 0, 7, 7, 1)
+
+# RGB565 destination
+wd = 16
+hd = 16
+dest = bytearray(wd * hd * 2)
+fbd = framebuf.FrameBuffer(dest, wd, hd, framebuf.RGB565)
+
+wp = 2
+bg = 0x1234
+fg = 0xF800
+pal = bytearray(wp * 2)
+palette = framebuf.FrameBuffer(pal, wp, 1, framebuf.RGB565)
+palette.pixel(0, 0, bg)
+palette.pixel(1, 0, fg)
+
+fbd.blit(fbc, 0, 0, -1, palette)
+
+print(fbd.pixel(0, 0) == fg)
+print(fbd.pixel(7, 7) == fg)
+print(fbd.pixel(8, 8) == 0) # Ouside blit
+print(fbd.pixel(0, 1) == bg)
+print(fbd.pixel(1, 0) == bg)
diff --git a/tests/extmod/framebuf_palette.py.exp b/tests/extmod/framebuf_palette.py.exp
new file mode 100644
index 000000000..2e883c51d
--- /dev/null
+++ b/tests/extmod/framebuf_palette.py.exp
@@ -0,0 +1,5 @@
+True
+True
+True
+True
+True