diff --git a/docs/library/framebuf.rst b/docs/library/framebuf.rst index 0073ba708d..13502cc7ae 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 18b170078d..454624dda8 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 0000000000..84db834c15 --- /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 0000000000..2e883c51de --- /dev/null +++ b/tests/extmod/framebuf_palette.py.exp @@ -0,0 +1,5 @@ +True +True +True +True +True