Skip to content

Commit 619ce67

Browse files
committed
Endianness handling for RGB565 framebuf mode.
The current behavior uses the CPU endianness, which I found surprising: ``` b = bytearray(2) f = framebuf.FrameBuffer(b, 1, 1, framebuf.RGB565) f.fill(0xabcd) print(b) # bytearray(b'\xcd\xab') ``` I would expect that to result in `bytearray(b'\xab\xcd')`. The RGB565 OLED that I'm using needs the bytes arranged big-endian, so currently this means pushing the endianness-handling to the user of FrameBuffer. This commits retains the existing behavior for backwards-compatibility but adds two new modes `RGB565_BE` and `RGB565_LE` to allow the user to explicitly set the behavior. The existing `RGB565` will use whatever the CPU endianness is.
1 parent 1a51fc9 commit 619ce67

File tree

4 files changed

+133
-61
lines changed

4 files changed

+133
-61
lines changed

docs/library/framebuf.rst

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,18 @@ Constants
146146

147147
.. data:: framebuf.RGB565
148148

149-
Red Green Blue (16-bit, 5+6+5) color format
149+
Red Green Blue (16-bit, 5+6+5) device-endian color format
150+
This will default to the device's native endianness (likely to be little-endian).
151+
152+
.. data:: framebuf.RGB565_BE
153+
154+
Red Green Blue (16-bit, 5+6+5) big-endian color format
155+
This will result in a pixel color value of `0xabcd` being stored in the buffer as `[0xab, 0xcd]`.
156+
157+
.. data:: framebuf.RGB565_LE
158+
159+
Red Green Blue (16-bit, 5+6+5) little-endian color format
160+
This will result in a pixel color value of `0xabcd` being stored in the buffer as `[0xcd, 0xab]`.
150161

151162
.. data:: framebuf.GS2_HMSB
152163

extmod/modframebuf.c

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <stdio.h>
2828
#include <string.h>
2929

30+
#include "py/binary.h"
3031
#include "py/runtime.h"
3132

3233
#if MICROPY_PY_FRAMEBUF
@@ -52,13 +53,14 @@ typedef struct _mp_framebuf_p_t {
5253
} mp_framebuf_p_t;
5354

5455
// constants for formats
55-
#define FRAMEBUF_MVLSB (0)
56-
#define FRAMEBUF_RGB565 (1)
57-
#define FRAMEBUF_GS2_HMSB (5)
58-
#define FRAMEBUF_GS4_HMSB (2)
59-
#define FRAMEBUF_GS8 (6)
60-
#define FRAMEBUF_MHLSB (3)
61-
#define FRAMEBUF_MHMSB (4)
56+
#define FRAMEBUF_MVLSB (0)
57+
#define FRAMEBUF_RGB565_BE (1)
58+
#define FRAMEBUF_RGB565_LE (7)
59+
#define FRAMEBUF_GS2_HMSB (5)
60+
#define FRAMEBUF_GS4_HMSB (2)
61+
#define FRAMEBUF_GS8 (6)
62+
#define FRAMEBUF_MHLSB (3)
63+
#define FRAMEBUF_MHMSB (4)
6264

6365
// Functions for MHLSB and MHMSB
6466

@@ -114,16 +116,27 @@ STATIC void mvlsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, in
114116

115117
// Functions for RGB565 format
116118

119+
STATIC void rgb565_flip_endian(uint32_t* col, uint8_t format) {
120+
if ((MP_ENDIANNESS_LITTLE && format == FRAMEBUF_RGB565_BE) || (MP_ENDIANNESS_BIG && format == FRAMEBUF_RGB565_LE)) {
121+
*col = ((*col & 0xff) << 8) | ((*col >> 8) & 0xff);
122+
}
123+
}
124+
117125
STATIC void rgb565_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) {
126+
rgb565_flip_endian(&col, fb->format);
118127
((uint16_t*)fb->buf)[x + y * fb->stride] = col;
119128
}
120129

121130
STATIC uint32_t rgb565_getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
122-
return ((uint16_t*)fb->buf)[x + y * fb->stride];
131+
uint32_t col = ((uint16_t*)fb->buf)[x + y * fb->stride];
132+
rgb565_flip_endian(&col, fb->format);
133+
return col;
123134
}
124135

125136
STATIC void rgb565_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) {
137+
rgb565_flip_endian(&col, fb->format);
126138
uint16_t *b = &((uint16_t*)fb->buf)[x + y * fb->stride];
139+
127140
while (h--) {
128141
for (int ww = w; ww; --ww) {
129142
*b++ = col;
@@ -228,14 +241,25 @@ STATIC void gs8_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int
228241

229242
STATIC mp_framebuf_p_t formats[] = {
230243
[FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect},
231-
[FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect},
244+
[FRAMEBUF_RGB565_BE] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect},
245+
[FRAMEBUF_RGB565_LE] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect},
232246
[FRAMEBUF_GS2_HMSB] = {gs2_hmsb_setpixel, gs2_hmsb_getpixel, gs2_hmsb_fill_rect},
233247
[FRAMEBUF_GS4_HMSB] = {gs4_hmsb_setpixel, gs4_hmsb_getpixel, gs4_hmsb_fill_rect},
234248
[FRAMEBUF_GS8] = {gs8_setpixel, gs8_getpixel, gs8_fill_rect},
235249
[FRAMEBUF_MHLSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect},
236250
[FRAMEBUF_MHMSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect},
237251
};
238252

253+
STATIC inline size_t bytes_per_pixel(const mp_obj_framebuf_t *fb) {
254+
switch (fb->format) {
255+
case FRAMEBUF_RGB565_BE:
256+
case FRAMEBUF_RGB565_LE:
257+
return 2;
258+
default:
259+
return 1;
260+
}
261+
}
262+
239263
static inline void setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) {
240264
formats[fb->format].setpixel(fb, x, y, col);
241265
}
@@ -281,7 +305,8 @@ STATIC mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size
281305

282306
switch (o->format) {
283307
case FRAMEBUF_MVLSB:
284-
case FRAMEBUF_RGB565:
308+
case FRAMEBUF_RGB565_BE:
309+
case FRAMEBUF_RGB565_LE:
285310
break;
286311
case FRAMEBUF_MHLSB:
287312
case FRAMEBUF_MHMSB:
@@ -306,7 +331,7 @@ STATIC mp_int_t framebuf_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo,
306331
(void)flags;
307332
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in);
308333
bufinfo->buf = self->buf;
309-
bufinfo->len = self->stride * self->height * (self->format == FRAMEBUF_RGB565 ? 2 : 1);
334+
bufinfo->len = self->stride * self->height * bytes_per_pixel(self);
310335
bufinfo->typecode = 'B'; // view framebuf as bytes
311336
return 0;
312337
}
@@ -630,7 +655,13 @@ STATIC const mp_rom_map_elem_t framebuf_module_globals_table[] = {
630655
{ MP_ROM_QSTR(MP_QSTR_FrameBuffer1), MP_ROM_PTR(&legacy_framebuffer1_obj) },
631656
{ MP_ROM_QSTR(MP_QSTR_MVLSB), MP_ROM_INT(FRAMEBUF_MVLSB) },
632657
{ MP_ROM_QSTR(MP_QSTR_MONO_VLSB), MP_ROM_INT(FRAMEBUF_MVLSB) },
633-
{ MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(FRAMEBUF_RGB565) },
658+
#if MP_ENDIANNESS_BIG
659+
{ MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(FRAMEBUF_RGB565_BE) },
660+
#else
661+
{ MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(FRAMEBUF_RGB565_LE) },
662+
#endif
663+
{ MP_ROM_QSTR(MP_QSTR_RGB565_BE), MP_ROM_INT(FRAMEBUF_RGB565_BE) },
664+
{ MP_ROM_QSTR(MP_QSTR_RGB565_LE), MP_ROM_INT(FRAMEBUF_RGB565_LE) },
634665
{ MP_ROM_QSTR(MP_QSTR_GS2_HMSB), MP_ROM_INT(FRAMEBUF_GS2_HMSB) },
635666
{ MP_ROM_QSTR(MP_QSTR_GS4_HMSB), MP_ROM_INT(FRAMEBUF_GS4_HMSB) },
636667
{ MP_ROM_QSTR(MP_QSTR_GS8), MP_ROM_INT(FRAMEBUF_GS8) },

tests/extmod/framebuf16.py

Lines changed: 66 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,56 +4,74 @@
44
print("SKIP")
55
raise SystemExit
66

7-
def printbuf():
7+
8+
def printbuf(buf, w, h):
89
print("--8<--")
910
for y in range(h):
1011
print(buf[y * w * 2:(y + 1) * w * 2])
1112
print("-->8--")
1213

13-
w = 4
14-
h = 5
15-
buf = bytearray(w * h * 2)
16-
fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.RGB565)
17-
18-
# fill
19-
fbuf.fill(0xffff)
20-
printbuf()
21-
fbuf.fill(0x0000)
22-
printbuf()
23-
24-
# put pixel
25-
fbuf.pixel(0, 0, 0xeeee)
26-
fbuf.pixel(3, 0, 0xee00)
27-
fbuf.pixel(0, 4, 0x00ee)
28-
fbuf.pixel(3, 4, 0x0ee0)
29-
printbuf()
30-
31-
# get pixel
32-
print(fbuf.pixel(0, 4), fbuf.pixel(1, 1))
33-
34-
# scroll
35-
fbuf.fill(0x0000)
36-
fbuf.pixel(2, 2, 0xffff)
37-
printbuf()
38-
fbuf.scroll(0, 1)
39-
printbuf()
40-
fbuf.scroll(1, 0)
41-
printbuf()
42-
fbuf.scroll(-1, -2)
43-
printbuf()
44-
45-
w2 = 2
46-
h2 = 3
47-
buf2 = bytearray(w2 * h2 * 2)
48-
fbuf2 = framebuf.FrameBuffer(buf2, w2, h2, framebuf.RGB565)
49-
50-
fbuf2.fill(0x0000)
51-
fbuf2.pixel(0, 0, 0x0ee0)
52-
fbuf2.pixel(0, 2, 0xee00)
53-
fbuf2.pixel(1, 0, 0x00ee)
54-
fbuf2.pixel(1, 2, 0xe00e)
55-
fbuf.fill(0xffff)
56-
fbuf.blit(fbuf2, 3, 3, 0x0000)
57-
fbuf.blit(fbuf2, -1, -1, 0x0000)
58-
fbuf.blit(fbuf2, 16, 16, 0x0000)
59-
printbuf()
14+
15+
def test_basic():
16+
w = 4
17+
h = 5
18+
buf = bytearray(w * h * 2)
19+
fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.RGB565)
20+
21+
# fill
22+
fbuf.fill(0xffff)
23+
printbuf(buf, w, h)
24+
fbuf.fill(0x0000)
25+
printbuf(buf, w, h)
26+
27+
# put pixel
28+
fbuf.pixel(0, 0, 0xeeee)
29+
fbuf.pixel(3, 0, 0xee00)
30+
fbuf.pixel(0, 4, 0x00ee)
31+
fbuf.pixel(3, 4, 0x0ee0)
32+
printbuf(buf, w, h)
33+
34+
# get pixel
35+
print(fbuf.pixel(0, 4), fbuf.pixel(1, 1))
36+
37+
# scroll
38+
fbuf.fill(0x0000)
39+
fbuf.pixel(2, 2, 0xffff)
40+
printbuf(buf, w, h)
41+
fbuf.scroll(0, 1)
42+
printbuf(buf, w, h)
43+
fbuf.scroll(1, 0)
44+
printbuf(buf, w, h)
45+
fbuf.scroll(-1, -2)
46+
printbuf(buf, w, h)
47+
48+
w2 = 2
49+
h2 = 3
50+
buf2 = bytearray(w2 * h2 * 2)
51+
fbuf2 = framebuf.FrameBuffer(buf2, w2, h2, framebuf.RGB565)
52+
53+
fbuf2.fill(0x0000)
54+
fbuf2.pixel(0, 0, 0x0ee0)
55+
fbuf2.pixel(0, 2, 0xee00)
56+
fbuf2.pixel(1, 0, 0x00ee)
57+
fbuf2.pixel(1, 2, 0xe00e)
58+
fbuf.fill(0xffff)
59+
fbuf.blit(fbuf2, 3, 3, 0x0000)
60+
fbuf.blit(fbuf2, -1, -1, 0x0000)
61+
fbuf.blit(fbuf2, 16, 16, 0x0000)
62+
printbuf(buf, w, h)
63+
64+
65+
def test_endian():
66+
for endian in (framebuf.RGB565_BE, framebuf.RGB565_LE,):
67+
w = 4
68+
h = 4
69+
buf = bytearray(w * h * 2)
70+
fbuf = framebuf.FrameBuffer(buf, w, h, endian)
71+
fbuf.fill_rect(1, 1, 2, 2, 0xabcd)
72+
fbuf.pixel(2, 2, 0xdcba)
73+
printbuf(buf, w, h)
74+
75+
76+
test_basic()
77+
test_endian()

tests/extmod/framebuf16.py.exp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,15 @@ bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff')
5555
bytearray(b'\xff\xff\xff\xff\xff\xff\xe0\x0e')
5656
bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff')
5757
-->8--
58+
--8<--
59+
bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00')
60+
bytearray(b'\x00\x00\xab\xcd\xab\xcd\x00\x00')
61+
bytearray(b'\x00\x00\xab\xcd\xdc\xba\x00\x00')
62+
bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00')
63+
-->8--
64+
--8<--
65+
bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00')
66+
bytearray(b'\x00\x00\xcd\xab\xcd\xab\x00\x00')
67+
bytearray(b'\x00\x00\xcd\xab\xba\xdc\x00\x00')
68+
bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00')
69+
-->8--

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy