Skip to content

Commit b153484

Browse files
committed
extmod/modframebuf: Add support for blit'ing read-only data.
Currently the `FrameBuffer.blit(buf, x, y)` method requires the `buf` argument to be another `FrameBuffer`, which is quite restrictive because it doesn't allow blit'ing read-only memory/data. This commit extends `blit()` to allow the `buf` argument to be a tuple or list of the form: (buffer, width, height, format[, stride]) where `buffer` can be anything with the buffer protocol and may be read-only, eg `bytes`. Also, the palette argument to `blit()` may be of the same form. The form of this tuple/list was chosen to be the same as the signature of the `FrameBuffer` constructor (that saves quite a bit of code size doing it that way). Signed-off-by: Damien George <damien@micropython.org>
1 parent d5f2fc2 commit b153484

File tree

5 files changed

+167
-18
lines changed

5 files changed

+167
-18
lines changed

docs/library/framebuf.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,18 @@ Other methods
137137
is compared to the value from *palette*, not to the value directly from
138138
*fbuf*.)
139139

140+
*fbuf* can be another FrameBuffer instance, or a tuple or list of the form::
141+
142+
(buffer, width, height, format)
143+
144+
or::
145+
146+
(buffer, width, height, format, stride)
147+
148+
This matches the signature of the FrameBuffer constructor, and the elements
149+
of the tuple/list are the same as the arguments to the constructor except that
150+
the *buffer* here can be read-only.
151+
140152
The *palette* argument enables blitting between FrameBuffers with differing
141153
formats. Typical usage is to render a monochrome or grayscale glyph/icon to
142154
a color display. The *palette* is a FrameBuffer instance whose format is

examples/natmod/framebuf/framebuf.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
#include "py/dynruntime.h"
55

66
#if !defined(__linux__)
7+
void *memcpy(void *dst, const void *src, size_t n) {
8+
return mp_fun_table.memmove_(dst, src, n);
9+
}
710
void *memset(void *s, int c, size_t n) {
811
return mp_fun_table.memset_(s, c, n);
912
}

extmod/modframebuf.c

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -270,8 +270,7 @@ static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, u
270270
formats[fb->format].fill_rect(fb, x, y, xend - x, yend - y, col);
271271
}
272272

273-
static mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args_in) {
274-
mp_arg_check_num(n_args, n_kw, 4, 5, false);
273+
static mp_obj_t framebuf_make_new_helper(size_t n_args, const mp_obj_t *args_in, unsigned int buf_flags, mp_obj_framebuf_t *o) {
275274

276275
mp_int_t width = mp_obj_get_int(args_in[1]);
277276
mp_int_t height = mp_obj_get_int(args_in[2]);
@@ -318,13 +317,15 @@ static mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size
318317
}
319318

320319
mp_buffer_info_t bufinfo;
321-
mp_get_buffer_raise(args_in[0], &bufinfo, MP_BUFFER_WRITE);
320+
mp_get_buffer_raise(args_in[0], &bufinfo, buf_flags);
322321

323322
if ((strides_required * stride + (height_required - strides_required) * width_required) * bpp / 8 > bufinfo.len) {
324323
mp_raise_ValueError(NULL);
325324
}
326325

327-
mp_obj_framebuf_t *o = mp_obj_malloc(mp_obj_framebuf_t, type);
326+
if (o == NULL) {
327+
o = mp_obj_malloc(mp_obj_framebuf_t, (const mp_obj_type_t *)&mp_type_framebuf);
328+
}
328329
o->buf_obj = args_in[0];
329330
o->buf = bufinfo.buf;
330331
o->width = width;
@@ -335,6 +336,11 @@ static mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size
335336
return MP_OBJ_FROM_PTR(o);
336337
}
337338

339+
static mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args_in) {
340+
mp_arg_check_num(n_args, n_kw, 4, 5, false);
341+
return framebuf_make_new_helper(n_args, args_in, MP_BUFFER_WRITE, NULL);
342+
}
343+
338344
static void framebuf_args(const mp_obj_t *args_in, mp_int_t *args_out, int n) {
339345
for (int i = 0; i < n; ++i) {
340346
args_out[i] = mp_obj_get_int(args_in[i + 1]);
@@ -707,30 +713,45 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_poly_obj, 5, 6, framebuf_pol
707713

708714
#endif // MICROPY_PY_ARRAY
709715

716+
static void get_readonly_framebuffer(mp_obj_t arg, mp_obj_framebuf_t *rofb) {
717+
mp_obj_t fb = mp_obj_cast_to_native_base(arg, MP_OBJ_FROM_PTR(&mp_type_framebuf));
718+
if (fb != MP_OBJ_NULL) {
719+
*rofb = *(mp_obj_framebuf_t *)MP_OBJ_TO_PTR(fb);
720+
} else {
721+
// A tuple/list of the form: (buffer, width, height, format[, stride]).
722+
size_t len;
723+
mp_obj_t *items;
724+
mp_obj_get_array(arg, &len, &items);
725+
if (len < 4 || len > 5) {
726+
mp_raise_ValueError(NULL);
727+
}
728+
framebuf_make_new_helper(len, items, MP_BUFFER_READ, rofb);
729+
}
730+
}
731+
710732
static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) {
711733
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
712-
mp_obj_t source_in = mp_obj_cast_to_native_base(args_in[1], MP_OBJ_FROM_PTR(&mp_type_framebuf));
713-
if (source_in == MP_OBJ_NULL) {
714-
mp_raise_TypeError(NULL);
715-
}
716-
mp_obj_framebuf_t *source = MP_OBJ_TO_PTR(source_in);
734+
735+
mp_obj_framebuf_t source;
736+
get_readonly_framebuffer(args_in[1], &source);
717737

718738
mp_int_t x = mp_obj_get_int(args_in[2]);
719739
mp_int_t y = mp_obj_get_int(args_in[3]);
720740
mp_int_t key = -1;
721741
if (n_args > 4) {
722742
key = mp_obj_get_int(args_in[4]);
723743
}
724-
mp_obj_framebuf_t *palette = NULL;
744+
mp_obj_framebuf_t palette;
745+
palette.buf = NULL;
725746
if (n_args > 5 && args_in[5] != mp_const_none) {
726-
palette = MP_OBJ_TO_PTR(mp_obj_cast_to_native_base(args_in[5], MP_OBJ_FROM_PTR(&mp_type_framebuf)));
747+
get_readonly_framebuffer(args_in[5], &palette);
727748
}
728749

729750
if (
730751
(x >= self->width) ||
731752
(y >= self->height) ||
732-
(-x >= source->width) ||
733-
(-y >= source->height)
753+
(-x >= source.width) ||
754+
(-y >= source.height)
734755
) {
735756
// Out of bounds, no-op.
736757
return mp_const_none;
@@ -741,15 +762,15 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) {
741762
int y0 = MAX(0, y);
742763
int x1 = MAX(0, -x);
743764
int y1 = MAX(0, -y);
744-
int x0end = MIN(self->width, x + source->width);
745-
int y0end = MIN(self->height, y + source->height);
765+
int x0end = MIN(self->width, x + source.width);
766+
int y0end = MIN(self->height, y + source.height);
746767

747768
for (; y0 < y0end; ++y0) {
748769
int cx1 = x1;
749770
for (int cx0 = x0; cx0 < x0end; ++cx0) {
750-
uint32_t col = getpixel(source, cx1, y1);
751-
if (palette) {
752-
col = getpixel(palette, col, 0);
771+
uint32_t col = getpixel(&source, cx1, y1);
772+
if (palette.buf) {
773+
col = getpixel(&palette, col, 0);
753774
}
754775
if (col != (uint32_t)key) {
755776
setpixel(self, cx0, y0, col);

tests/extmod/framebuf_blit.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Test FrameBuffer.blit method.
2+
3+
try:
4+
import framebuf
5+
except ImportError:
6+
print("SKIP")
7+
raise SystemExit
8+
9+
10+
def printbuf():
11+
print("--8<--")
12+
for y in range(h):
13+
for x in range(w):
14+
print("%02x" % buf[(x + y * w)], end="")
15+
print()
16+
print("-->8--")
17+
18+
19+
w = 5
20+
h = 4
21+
buf = bytearray(w * h)
22+
fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS8)
23+
24+
fbuf2 = framebuf.FrameBuffer(bytearray(4), 2, 2, framebuf.GS8)
25+
fbuf2.fill(0xFF)
26+
27+
# Blit another FrameBuffer, at various locations.
28+
for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)):
29+
fbuf.fill(0)
30+
fbuf.blit(fbuf2, x, y)
31+
printbuf()
32+
33+
# Blit a bytes object.
34+
fbuf.fill(0)
35+
image = (b"\x10\x11\x12\x13", 2, 2, framebuf.GS8)
36+
fbuf.blit(image, 1, 1)
37+
printbuf()
38+
39+
# Blit a bytes object that has a stride.
40+
fbuf.fill(0)
41+
image = (b"\x20\x21\xff\x22\x23\xff", 2, 2, framebuf.GS8, 3)
42+
fbuf.blit(image, 1, 1)
43+
printbuf()
44+
45+
# Blit a bytes object with a bytes palette.
46+
fbuf.fill(0)
47+
image = (b"\x00\x01\x01\x00", 2, 2, framebuf.GS8)
48+
palette = (b"\xa1\xa2", 2, 1, framebuf.GS8)
49+
fbuf.blit(image, 1, 1, -1, palette)
50+
printbuf()
51+
52+
# Not enough elements in the tuple.
53+
try:
54+
fbuf.blit((0, 0, 0), 0, 0)
55+
except ValueError:
56+
print("ValueError")
57+
58+
# Too many elements in the tuple.
59+
try:
60+
fbuf.blit((0, 0, 0, 0, 0, 0), 0, 0)
61+
except ValueError:
62+
print("ValueError")
63+
64+
# Bytes too small.
65+
try:
66+
fbuf.blit((b"", 1, 1, framebuf.GS8), 0, 0)
67+
except ValueError:
68+
print("ValueError")

tests/extmod/framebuf_blit.py.exp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
--8<--
2+
ff00000000
3+
0000000000
4+
0000000000
5+
0000000000
6+
-->8--
7+
--8<--
8+
ffff000000
9+
ffff000000
10+
0000000000
11+
0000000000
12+
-->8--
13+
--8<--
14+
0000000000
15+
00ffff0000
16+
00ffff0000
17+
0000000000
18+
-->8--
19+
--8<--
20+
0000000000
21+
0000000000
22+
0000000000
23+
00000000ff
24+
-->8--
25+
--8<--
26+
0000000000
27+
0010110000
28+
0012130000
29+
0000000000
30+
-->8--
31+
--8<--
32+
0000000000
33+
0020210000
34+
0022230000
35+
0000000000
36+
-->8--
37+
--8<--
38+
0000000000
39+
00a1a20000
40+
00a2a10000
41+
0000000000
42+
-->8--
43+
ValueError
44+
ValueError
45+
ValueError

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