From 4b03c991a3cb0c8c017ad245df13da1ce9aa98ea Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 14 Nov 2025 13:30:53 +0000 Subject: [PATCH 01/11] extmod/modframebuf.c: Add byte-swapped RGB565 format. This is required for identifying which bits are in which channels for alpha blending. It also has some utility when working with display devices where the display memory is opposite endianness from the microcontroller, as it permits more straightforward translation from colors to bytes. --- extmod/modframebuf.c | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 593125aa16f91..4f5fb900d1b53 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -57,13 +57,14 @@ typedef struct _mp_framebuf_p_t { } mp_framebuf_p_t; // constants for formats -#define FRAMEBUF_MVLSB (0) -#define FRAMEBUF_RGB565 (1) -#define FRAMEBUF_GS2_HMSB (5) -#define FRAMEBUF_GS4_HMSB (2) -#define FRAMEBUF_GS8 (6) -#define FRAMEBUF_MHLSB (3) -#define FRAMEBUF_MHMSB (4) +#define FRAMEBUF_MVLSB (0) +#define FRAMEBUF_RGB565 (1) +#define FRAMEBUF_RGB565_BS (7) +#define FRAMEBUF_GS2_HMSB (5) +#define FRAMEBUF_GS4_HMSB (2) +#define FRAMEBUF_GS8 (6) +#define FRAMEBUF_MHLSB (3) +#define FRAMEBUF_MHMSB (4) // Functions for MHLSB and MHMSB @@ -137,6 +138,29 @@ static void rgb565_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsign } } +// Functions for RGB565_BS format + +static void rgb565_bs_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { + col = __builtin_bswap16(col); + ((uint16_t *)fb->buf)[x + y * fb->stride] = col; +} + +static uint32_t rgb565_bs_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { + uint32_t col = ((uint16_t *)fb->buf)[x + y * fb->stride]; + return __builtin_bswap16(col); +} + +static void rgb565_bs_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { + col = __builtin_bswap16(col); + uint16_t *b = &((uint16_t *)fb->buf)[x + y * fb->stride]; + while (h--) { + for (unsigned int ww = w; ww; --ww) { + *b++ = col; + } + b += fb->stride - w; + } +} + // Functions for GS2_HMSB format static void gs2_hmsb_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { @@ -234,6 +258,7 @@ static void gs8_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned static mp_framebuf_p_t formats[] = { [FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect}, [FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, + [FRAMEBUF_RGB565_BS] = {rgb565_bs_setpixel, rgb565_bs_getpixel, rgb565_bs_fill_rect}, [FRAMEBUF_GS2_HMSB] = {gs2_hmsb_setpixel, gs2_hmsb_getpixel, gs2_hmsb_fill_rect}, [FRAMEBUF_GS4_HMSB] = {gs4_hmsb_setpixel, gs4_hmsb_getpixel, gs4_hmsb_fill_rect}, [FRAMEBUF_GS8] = {gs8_setpixel, gs8_getpixel, gs8_fill_rect}, @@ -909,6 +934,7 @@ static const mp_rom_map_elem_t framebuf_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_MVLSB), MP_ROM_INT(FRAMEBUF_MVLSB) }, { MP_ROM_QSTR(MP_QSTR_MONO_VLSB), MP_ROM_INT(FRAMEBUF_MVLSB) }, { MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(FRAMEBUF_RGB565) }, + { MP_ROM_QSTR(MP_QSTR_RGB565_BS), MP_ROM_INT(FRAMEBUF_RGB565_BS) }, { MP_ROM_QSTR(MP_QSTR_GS2_HMSB), MP_ROM_INT(FRAMEBUF_GS2_HMSB) }, { MP_ROM_QSTR(MP_QSTR_GS4_HMSB), MP_ROM_INT(FRAMEBUF_GS4_HMSB) }, { MP_ROM_QSTR(MP_QSTR_GS8), MP_ROM_INT(FRAMEBUF_GS8) }, From eb96c715dd7f3eff0b9a5d6e3e5b42b30f6dbcc4 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 14 Nov 2025 13:44:53 +0000 Subject: [PATCH 02/11] tests/extmod/framebuf16_bs.py Add tests for byteswapped RGB565 format. --- tests/extmod/framebuf16_bs.py | 66 +++++++++++++++++++++++++++++++ tests/extmod/framebuf16_bs.py.exp | 57 ++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 tests/extmod/framebuf16_bs.py create mode 100644 tests/extmod/framebuf16_bs.py.exp diff --git a/tests/extmod/framebuf16_bs.py b/tests/extmod/framebuf16_bs.py new file mode 100644 index 0000000000000..d3e0e681b1c6d --- /dev/null +++ b/tests/extmod/framebuf16_bs.py @@ -0,0 +1,66 @@ +try: + import framebuf, sys +except ImportError: + print("SKIP") + raise SystemExit + +# This test and its .exp file is based on a little-endian architecture. +if sys.byteorder != "little": + print("SKIP") + raise SystemExit + + +def printbuf(): + print("--8<--") + for y in range(h): + print(buf[y * w * 2 : (y + 1) * w * 2]) + print("-->8--") + + +w = 4 +h = 5 +buf = bytearray(w * h * 2) +fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.RGB565_BS) + +# fill +fbuf.fill(0xFFFF) +printbuf() +fbuf.fill(0x0000) +printbuf() + +# put pixel +fbuf.pixel(0, 0, 0xEEEE) +fbuf.pixel(3, 0, 0xEE00) +fbuf.pixel(0, 4, 0x00EE) +fbuf.pixel(3, 4, 0x0EE0) +printbuf() + +# get pixel +print(fbuf.pixel(0, 4), fbuf.pixel(1, 1)) + +# scroll +fbuf.fill(0x0000) +fbuf.pixel(2, 2, 0xFFFF) +printbuf() +fbuf.scroll(0, 1) +printbuf() +fbuf.scroll(1, 0) +printbuf() +fbuf.scroll(-1, -2) +printbuf() + +w2 = 2 +h2 = 3 +buf2 = bytearray(w2 * h2 * 2) +fbuf2 = framebuf.FrameBuffer(buf2, w2, h2, framebuf.RGB565) + +fbuf2.fill(0x0000) +fbuf2.pixel(0, 0, 0x0EE0) +fbuf2.pixel(0, 2, 0xEE00) +fbuf2.pixel(1, 0, 0x00EE) +fbuf2.pixel(1, 2, 0xE00E) +fbuf.fill(0xFFFF) +fbuf.blit(fbuf2, 3, 3, 0x0000) +fbuf.blit(fbuf2, -1, -1, 0x0000) +fbuf.blit(fbuf2, 16, 16, 0x0000) +printbuf() diff --git a/tests/extmod/framebuf16_bs.py.exp b/tests/extmod/framebuf16_bs.py.exp new file mode 100644 index 0000000000000..e539d7908dc3b --- /dev/null +++ b/tests/extmod/framebuf16_bs.py.exp @@ -0,0 +1,57 @@ +--8<-- +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +-->8-- +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\xee\xee\x00\x00\x00\x00\xee\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\xee\x00\x00\x00\x00\x0e\xe0') +-->8-- +238 0 +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\xff\xff') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\xff\xff') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xe0\x0e\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\x0e\xe0') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +-->8-- From 19ba0e01f0749f4e6c6a9734301481091edf37b4 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 17 Nov 2025 10:17:52 +0000 Subject: [PATCH 03/11] extmod/modframebuf.c: Add RGB565_BS to valid format cases. Without this, we don't know the bits per pixel. --- extmod/modframebuf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 4f5fb900d1b53..b16e3aa2ff171 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -335,6 +335,7 @@ static mp_obj_t framebuf_make_new_helper(size_t n_args, const mp_obj_t *args_in, bpp = 8; break; case FRAMEBUF_RGB565: + case FRAMEBUF_RGB565_BS: bpp = 16; break; default: From 845dcadedc8703892be438f52c8b4ccbdf90cde2 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 17 Nov 2025 10:36:21 +0000 Subject: [PATCH 04/11] tests/extmod/framebuf16_bs.py: Correct buffer type. --- tests/extmod/framebuf16_bs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/extmod/framebuf16_bs.py b/tests/extmod/framebuf16_bs.py index d3e0e681b1c6d..ffcfb2779789b 100644 --- a/tests/extmod/framebuf16_bs.py +++ b/tests/extmod/framebuf16_bs.py @@ -52,7 +52,7 @@ def printbuf(): w2 = 2 h2 = 3 buf2 = bytearray(w2 * h2 * 2) -fbuf2 = framebuf.FrameBuffer(buf2, w2, h2, framebuf.RGB565) +fbuf2 = framebuf.FrameBuffer(buf2, w2, h2, framebuf.RGB565_BS) fbuf2.fill(0x0000) fbuf2.pixel(0, 0, 0x0EE0) From 86ce41da19082a1462e8438645e668244d989150 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 17 Nov 2025 10:55:16 +0000 Subject: [PATCH 05/11] run-tests.py: Skip framebuf16_bs tests on platforms without slicing. --- tests/run-tests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/run-tests.py b/tests/run-tests.py index d8d2c42ad22e7..a4a4ee679aab0 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -280,6 +280,7 @@ def open(self, path, mode): "extmod/btree1.py", "extmod/deflate_decompress.py", "extmod/framebuf16.py", + "extmod/framebuf16_bs.py", "extmod/framebuf4.py", "extmod/machine1.py", "extmod/time_mktime.py", From e197cd4bb4d97b8fe30329ed1b4a3be9b06a9231 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 17 Nov 2025 10:56:35 +0000 Subject: [PATCH 06/11] docs/framebuf.rst: Add documentation for new format. --- docs/library/framebuf.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/library/framebuf.rst b/docs/library/framebuf.rst index e2a231207d673..e221d974ab4b0 100644 --- a/docs/library/framebuf.rst +++ b/docs/library/framebuf.rst @@ -194,6 +194,12 @@ Constants Red Green Blue (16-bit, 5+6+5) color format +.. data:: framebuf.RGB565_BS + + Red Green Blue (16-bit, 5+6+5, byte-swapped) color format + This defines a 16-bit format where the bytes are stored in memory with + opposite endianness to the native endianness. + .. data:: framebuf.GS2_HMSB Grayscale (2-bit) color format From 91cfa9934a94eb844e9dee6c1156832ac696522d Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 17 Nov 2025 11:48:29 +0000 Subject: [PATCH 07/11] examples/natmod/framebuf.c: Add RGB565_BS to example native module. --- examples/natmod/framebuf/framebuf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/natmod/framebuf/framebuf.c b/examples/natmod/framebuf/framebuf.c index 5fd7c6be3a456..76b6bee995995 100644 --- a/examples/natmod/framebuf/framebuf.c +++ b/examples/natmod/framebuf/framebuf.c @@ -44,6 +44,7 @@ mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *a mp_store_global(MP_QSTR_MVLSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MVLSB)); mp_store_global(MP_QSTR_MONO_VLSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MVLSB)); mp_store_global(MP_QSTR_RGB565, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565)); + mp_store_global(MP_QSTR_RGB565_BS, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565_BS)); mp_store_global(MP_QSTR_GS2_HMSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS2_HMSB)); mp_store_global(MP_QSTR_GS4_HMSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS4_HMSB)); mp_store_global(MP_QSTR_GS8, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS8)); From 7b66bd1e1831e5af1ad4494240a1bbc9c1e314db Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 17 Nov 2025 11:52:08 +0000 Subject: [PATCH 08/11] extmod/modframebuf.c: Reduce codesize. Re-use the rgb565_fill_rect() function to avoid repeated code. --- extmod/modframebuf.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index b16e3aa2ff171..c6db7b42ac9f8 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -151,14 +151,7 @@ static uint32_t rgb565_bs_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, } static void rgb565_bs_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { - col = __builtin_bswap16(col); - uint16_t *b = &((uint16_t *)fb->buf)[x + y * fb->stride]; - while (h--) { - for (unsigned int ww = w; ww; --ww) { - *b++ = col; - } - b += fb->stride - w; - } + rgb565_fill_rect(fb, x, y, w, h, __builtin_bswap16(col)); } // Functions for GS2_HMSB format From 700085e384dbee1367a012eaa4f275768aba8371 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 17 Nov 2025 12:14:58 +0000 Subject: [PATCH 09/11] extmod/modframbeuf.c: Completely remove byteswapped fill rect. --- extmod/modframebuf.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index c6db7b42ac9f8..18ff5dc8a3554 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -118,7 +118,7 @@ static void mvlsb_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigne } } -// Functions for RGB565 format +// Functions for RGB565 and RGB565_BS format static void rgb565_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { ((uint16_t *)fb->buf)[x + y * fb->stride] = col; @@ -128,18 +128,6 @@ static uint32_t rgb565_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, uns return ((uint16_t *)fb->buf)[x + y * fb->stride]; } -static void rgb565_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { - uint16_t *b = &((uint16_t *)fb->buf)[x + y * fb->stride]; - while (h--) { - for (unsigned int ww = w; ww; --ww) { - *b++ = col; - } - b += fb->stride - w; - } -} - -// Functions for RGB565_BS format - static void rgb565_bs_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { col = __builtin_bswap16(col); ((uint16_t *)fb->buf)[x + y * fb->stride] = col; @@ -150,8 +138,17 @@ static uint32_t rgb565_bs_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, return __builtin_bswap16(col); } -static void rgb565_bs_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { - rgb565_fill_rect(fb, x, y, w, h, __builtin_bswap16(col)); +static void rgb565_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { + if (fb->format == FRAMEBUF_RGB565_BS) { + col = __builtin_bswap16(col); + } + uint16_t *b = &((uint16_t *)fb->buf)[x + y * fb->stride]; + while (h--) { + for (unsigned int ww = w; ww; --ww) { + *b++ = col; + } + b += fb->stride - w; + } } // Functions for GS2_HMSB format @@ -251,7 +248,7 @@ static void gs8_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned static mp_framebuf_p_t formats[] = { [FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect}, [FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, - [FRAMEBUF_RGB565_BS] = {rgb565_bs_setpixel, rgb565_bs_getpixel, rgb565_bs_fill_rect}, + [FRAMEBUF_RGB565_BS] = {rgb565_bs_setpixel, rgb565_bs_getpixel, rgb565_fill_rect}, [FRAMEBUF_GS2_HMSB] = {gs2_hmsb_setpixel, gs2_hmsb_getpixel, gs2_hmsb_fill_rect}, [FRAMEBUF_GS4_HMSB] = {gs4_hmsb_setpixel, gs4_hmsb_getpixel, gs4_hmsb_fill_rect}, [FRAMEBUF_GS8] = {gs8_setpixel, gs8_getpixel, gs8_fill_rect}, From 1948f3614cbfc7c4583752af1a4c7b6e6965c949 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 17 Nov 2025 15:09:27 +0000 Subject: [PATCH 10/11] extmod/modframebuf.c: Make RGB565 endianness explicit. This keeps the internal C code more-or-less unchanged, but exposes the byte-order as "RGB565_LE" and "RGB565_BE" for little and big endian versions. Updates docs and tests and, as a bonus, turns on RGB565 testing for big-endian native systems. --- docs/library/framebuf.rst | 17 ++++-- extmod/modframebuf.c | 11 +++- tests/extmod/framebuf16.py | 95 ++++++++++++++++--------------- tests/extmod/framebuf16.py.exp | 58 +++++++++++++++++++ tests/extmod/framebuf16_bs.py | 66 --------------------- tests/extmod/framebuf16_bs.py.exp | 57 ------------------- tests/run-tests.py | 1 - 7 files changed, 130 insertions(+), 175 deletions(-) delete mode 100644 tests/extmod/framebuf16_bs.py delete mode 100644 tests/extmod/framebuf16_bs.py.exp diff --git a/docs/library/framebuf.rst b/docs/library/framebuf.rst index e221d974ab4b0..dfed26b042e43 100644 --- a/docs/library/framebuf.rst +++ b/docs/library/framebuf.rst @@ -192,13 +192,20 @@ Constants .. data:: framebuf.RGB565 - Red Green Blue (16-bit, 5+6+5) color format + Red Green Blue (16-bit, 5+6+5, native) color format in native byte-order. -.. data:: framebuf.RGB565_BS +.. data:: framebuf.RGB565_LE - Red Green Blue (16-bit, 5+6+5, byte-swapped) color format - This defines a 16-bit format where the bytes are stored in memory with - opposite endianness to the native endianness. + Red Green Blue (16-bit, 5+6+5, little-endian) color format in little-endian + byte order. This defines a 16-bit format where the bytes are stored in + little-endian order. If the system is little-endian, this is the same as + ``RGB565``. + +.. data:: framebuf.RGB565_BE + + Red Green Blue (16-bit, 5+6+5, big-endian) color format in big-endian byte + order. This defines a 16-bit format where the bytes are stored in + big-endian order. If the system is big-endian, this is the same as ``RGB565``. .. data:: framebuf.GS2_HMSB diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 18ff5dc8a3554..4b07fe20346d1 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -119,6 +119,9 @@ static void mvlsb_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigne } // Functions for RGB565 and RGB565_BS format +// +// Internally we use 'native' and 'byte-swapped' formats, and then expose those as big- or little- +// endian to Python as appropriate. static void rgb565_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { ((uint16_t *)fb->buf)[x + y * fb->stride] = col; @@ -925,7 +928,13 @@ static const mp_rom_map_elem_t framebuf_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_MVLSB), MP_ROM_INT(FRAMEBUF_MVLSB) }, { MP_ROM_QSTR(MP_QSTR_MONO_VLSB), MP_ROM_INT(FRAMEBUF_MVLSB) }, { MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(FRAMEBUF_RGB565) }, - { MP_ROM_QSTR(MP_QSTR_RGB565_BS), MP_ROM_INT(FRAMEBUF_RGB565_BS) }, +#if MP_ENDIANNESS_LITTLE + { MP_ROM_QSTR(MP_QSTR_RGB565_LE), MP_ROM_INT(FRAMEBUF_RGB565) }, + { MP_ROM_QSTR(MP_QSTR_RGB565_BE), MP_ROM_INT(FRAMEBUF_RGB565_BS) }, +#elif MP_ENDIANNESS_BIG + { MP_ROM_QSTR(MP_QSTR_RGB565_BE), MP_ROM_INT(FRAMEBUF_RGB565) }, + { MP_ROM_QSTR(MP_QSTR_RGB565_LE), MP_ROM_INT(FRAMEBUF_RGB565_BS) }, +#endif // MP_ENDIANNESS_... { MP_ROM_QSTR(MP_QSTR_GS2_HMSB), MP_ROM_INT(FRAMEBUF_GS2_HMSB) }, { MP_ROM_QSTR(MP_QSTR_GS4_HMSB), MP_ROM_INT(FRAMEBUF_GS4_HMSB) }, { MP_ROM_QSTR(MP_QSTR_GS8), MP_ROM_INT(FRAMEBUF_GS8) }, diff --git a/tests/extmod/framebuf16.py b/tests/extmod/framebuf16.py index 9f373c331e51f..0da85fe95f306 100644 --- a/tests/extmod/framebuf16.py +++ b/tests/extmod/framebuf16.py @@ -4,10 +4,14 @@ print("SKIP") raise SystemExit -# This test and its .exp file is based on a little-endian architecture. -if sys.byteorder != "little": - print("SKIP") - raise SystemExit +if ( + sys.byteorder == "little" and framebuf.RGB565 == framebuf.RGB565_LE +) or ( + sys.byteorder == "big" and framebuf.RGB565 == framebuf.RGB565_BE +): + print("Native format matches expected value.") +else: + print("Unexpected native format.") def printbuf(): @@ -17,50 +21,51 @@ def printbuf(): print("-->8--") -w = 4 -h = 5 -buf = bytearray(w * h * 2) -fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.RGB565) +for format in [framebuf.RGB565_LE, framebuf.RGB565_BE]: + w = 4 + h = 5 + buf = bytearray(w * h * 2) + fbuf = framebuf.FrameBuffer(buf, w, h, format) -# fill -fbuf.fill(0xFFFF) -printbuf() -fbuf.fill(0x0000) -printbuf() + # fill + fbuf.fill(0xFFFF) + printbuf() + fbuf.fill(0x0000) + printbuf() -# put pixel -fbuf.pixel(0, 0, 0xEEEE) -fbuf.pixel(3, 0, 0xEE00) -fbuf.pixel(0, 4, 0x00EE) -fbuf.pixel(3, 4, 0x0EE0) -printbuf() + # put pixel + fbuf.pixel(0, 0, 0xEEEE) + fbuf.pixel(3, 0, 0xEE00) + fbuf.pixel(0, 4, 0x00EE) + fbuf.pixel(3, 4, 0x0EE0) + printbuf() -# get pixel -print(fbuf.pixel(0, 4), fbuf.pixel(1, 1)) + # get pixel + print(fbuf.pixel(0, 4), fbuf.pixel(1, 1)) -# scroll -fbuf.fill(0x0000) -fbuf.pixel(2, 2, 0xFFFF) -printbuf() -fbuf.scroll(0, 1) -printbuf() -fbuf.scroll(1, 0) -printbuf() -fbuf.scroll(-1, -2) -printbuf() + # scroll + fbuf.fill(0x0000) + fbuf.pixel(2, 2, 0xFFFF) + printbuf() + fbuf.scroll(0, 1) + printbuf() + fbuf.scroll(1, 0) + printbuf() + fbuf.scroll(-1, -2) + printbuf() -w2 = 2 -h2 = 3 -buf2 = bytearray(w2 * h2 * 2) -fbuf2 = framebuf.FrameBuffer(buf2, w2, h2, framebuf.RGB565) + w2 = 2 + h2 = 3 + buf2 = bytearray(w2 * h2 * 2) + fbuf2 = framebuf.FrameBuffer(buf2, w2, h2, format) -fbuf2.fill(0x0000) -fbuf2.pixel(0, 0, 0x0EE0) -fbuf2.pixel(0, 2, 0xEE00) -fbuf2.pixel(1, 0, 0x00EE) -fbuf2.pixel(1, 2, 0xE00E) -fbuf.fill(0xFFFF) -fbuf.blit(fbuf2, 3, 3, 0x0000) -fbuf.blit(fbuf2, -1, -1, 0x0000) -fbuf.blit(fbuf2, 16, 16, 0x0000) -printbuf() + fbuf2.fill(0x0000) + fbuf2.pixel(0, 0, 0x0EE0) + fbuf2.pixel(0, 2, 0xEE00) + fbuf2.pixel(1, 0, 0x00EE) + fbuf2.pixel(1, 2, 0xE00E) + fbuf.fill(0xFFFF) + fbuf.blit(fbuf2, 3, 3, 0x0000) + fbuf.blit(fbuf2, -1, -1, 0x0000) + fbuf.blit(fbuf2, 16, 16, 0x0000) + printbuf() diff --git a/tests/extmod/framebuf16.py.exp b/tests/extmod/framebuf16.py.exp index c41dc19d07140..bb23762efcf75 100644 --- a/tests/extmod/framebuf16.py.exp +++ b/tests/extmod/framebuf16.py.exp @@ -1,3 +1,4 @@ +Native format matches expected value. --8<-- bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') @@ -55,3 +56,60 @@ bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') bytearray(b'\xff\xff\xff\xff\xff\xff\xe0\x0e') bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') -->8-- +--8<-- +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +-->8-- +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\xee\xee\x00\x00\x00\x00\xee\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\xee\x00\x00\x00\x00\x0e\xe0') +-->8-- +238 0 +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\xff\xff') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\xff\xff') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xe0\x0e\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\x0e\xe0') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +-->8-- diff --git a/tests/extmod/framebuf16_bs.py b/tests/extmod/framebuf16_bs.py deleted file mode 100644 index ffcfb2779789b..0000000000000 --- a/tests/extmod/framebuf16_bs.py +++ /dev/null @@ -1,66 +0,0 @@ -try: - import framebuf, sys -except ImportError: - print("SKIP") - raise SystemExit - -# This test and its .exp file is based on a little-endian architecture. -if sys.byteorder != "little": - print("SKIP") - raise SystemExit - - -def printbuf(): - print("--8<--") - for y in range(h): - print(buf[y * w * 2 : (y + 1) * w * 2]) - print("-->8--") - - -w = 4 -h = 5 -buf = bytearray(w * h * 2) -fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.RGB565_BS) - -# fill -fbuf.fill(0xFFFF) -printbuf() -fbuf.fill(0x0000) -printbuf() - -# put pixel -fbuf.pixel(0, 0, 0xEEEE) -fbuf.pixel(3, 0, 0xEE00) -fbuf.pixel(0, 4, 0x00EE) -fbuf.pixel(3, 4, 0x0EE0) -printbuf() - -# get pixel -print(fbuf.pixel(0, 4), fbuf.pixel(1, 1)) - -# scroll -fbuf.fill(0x0000) -fbuf.pixel(2, 2, 0xFFFF) -printbuf() -fbuf.scroll(0, 1) -printbuf() -fbuf.scroll(1, 0) -printbuf() -fbuf.scroll(-1, -2) -printbuf() - -w2 = 2 -h2 = 3 -buf2 = bytearray(w2 * h2 * 2) -fbuf2 = framebuf.FrameBuffer(buf2, w2, h2, framebuf.RGB565_BS) - -fbuf2.fill(0x0000) -fbuf2.pixel(0, 0, 0x0EE0) -fbuf2.pixel(0, 2, 0xEE00) -fbuf2.pixel(1, 0, 0x00EE) -fbuf2.pixel(1, 2, 0xE00E) -fbuf.fill(0xFFFF) -fbuf.blit(fbuf2, 3, 3, 0x0000) -fbuf.blit(fbuf2, -1, -1, 0x0000) -fbuf.blit(fbuf2, 16, 16, 0x0000) -printbuf() diff --git a/tests/extmod/framebuf16_bs.py.exp b/tests/extmod/framebuf16_bs.py.exp deleted file mode 100644 index e539d7908dc3b..0000000000000 --- a/tests/extmod/framebuf16_bs.py.exp +++ /dev/null @@ -1,57 +0,0 @@ ---8<-- -bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') -bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') -bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') -bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') -bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') --->8-- ---8<-- -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') --->8-- ---8<-- -bytearray(b'\xee\xee\x00\x00\x00\x00\xee\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\xee\x00\x00\x00\x00\x0e\xe0') --->8-- -238 0 ---8<-- -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') --->8-- ---8<-- -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') --->8-- ---8<-- -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\xff\xff') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') --->8-- ---8<-- -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\xff\xff') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') --->8-- ---8<-- -bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') -bytearray(b'\xe0\x0e\xff\xff\xff\xff\xff\xff') -bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') -bytearray(b'\xff\xff\xff\xff\xff\xff\x0e\xe0') -bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') --->8-- diff --git a/tests/run-tests.py b/tests/run-tests.py index a4a4ee679aab0..d8d2c42ad22e7 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -280,7 +280,6 @@ def open(self, path, mode): "extmod/btree1.py", "extmod/deflate_decompress.py", "extmod/framebuf16.py", - "extmod/framebuf16_bs.py", "extmod/framebuf4.py", "extmod/machine1.py", "extmod/time_mktime.py", From f1f616d9720bf070b793d4df1f78b270809813da Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 17 Nov 2025 15:35:54 +0000 Subject: [PATCH 11/11] extmod/modframebuf.c: Fix code style --- examples/natmod/framebuf/framebuf.c | 8 +++++++- extmod/modframebuf.c | 6 +++--- tests/extmod/framebuf16.py | 4 +--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/examples/natmod/framebuf/framebuf.c b/examples/natmod/framebuf/framebuf.c index 76b6bee995995..e05799d1eb389 100644 --- a/examples/natmod/framebuf/framebuf.c +++ b/examples/natmod/framebuf/framebuf.c @@ -44,7 +44,13 @@ mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *a mp_store_global(MP_QSTR_MVLSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MVLSB)); mp_store_global(MP_QSTR_MONO_VLSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MVLSB)); mp_store_global(MP_QSTR_RGB565, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565)); - mp_store_global(MP_QSTR_RGB565_BS, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565_BS)); + #if MP_ENDIANNESS_LITTLE + mp_store_global(MP_QSTR_RGB565_LE, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565)); + mp_store_global(MP_QSTR_RGB565_BE, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565_BS)); + #else + mp_store_global(MP_QSTR_RGB565_BE, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565)); + mp_store_global(MP_QSTR_RGB565_LE, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565_BS)); + #endif mp_store_global(MP_QSTR_GS2_HMSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS2_HMSB)); mp_store_global(MP_QSTR_GS4_HMSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS4_HMSB)); mp_store_global(MP_QSTR_GS8, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS8)); diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 4b07fe20346d1..be80000c3550f 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -928,13 +928,13 @@ static const mp_rom_map_elem_t framebuf_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_MVLSB), MP_ROM_INT(FRAMEBUF_MVLSB) }, { MP_ROM_QSTR(MP_QSTR_MONO_VLSB), MP_ROM_INT(FRAMEBUF_MVLSB) }, { MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(FRAMEBUF_RGB565) }, -#if MP_ENDIANNESS_LITTLE + #if MP_ENDIANNESS_LITTLE { MP_ROM_QSTR(MP_QSTR_RGB565_LE), MP_ROM_INT(FRAMEBUF_RGB565) }, { MP_ROM_QSTR(MP_QSTR_RGB565_BE), MP_ROM_INT(FRAMEBUF_RGB565_BS) }, -#elif MP_ENDIANNESS_BIG + #elif MP_ENDIANNESS_BIG { MP_ROM_QSTR(MP_QSTR_RGB565_BE), MP_ROM_INT(FRAMEBUF_RGB565) }, { MP_ROM_QSTR(MP_QSTR_RGB565_LE), MP_ROM_INT(FRAMEBUF_RGB565_BS) }, -#endif // MP_ENDIANNESS_... + #endif // MP_ENDIANNESS_... { MP_ROM_QSTR(MP_QSTR_GS2_HMSB), MP_ROM_INT(FRAMEBUF_GS2_HMSB) }, { MP_ROM_QSTR(MP_QSTR_GS4_HMSB), MP_ROM_INT(FRAMEBUF_GS4_HMSB) }, { MP_ROM_QSTR(MP_QSTR_GS8), MP_ROM_INT(FRAMEBUF_GS8) }, diff --git a/tests/extmod/framebuf16.py b/tests/extmod/framebuf16.py index 0da85fe95f306..7d4c21ff9ada3 100644 --- a/tests/extmod/framebuf16.py +++ b/tests/extmod/framebuf16.py @@ -4,9 +4,7 @@ print("SKIP") raise SystemExit -if ( - sys.byteorder == "little" and framebuf.RGB565 == framebuf.RGB565_LE -) or ( +if (sys.byteorder == "little" and framebuf.RGB565 == framebuf.RGB565_LE) or ( sys.byteorder == "big" and framebuf.RGB565 == framebuf.RGB565_BE ): print("Native format matches expected value.")