Branch data Line data Source code
1 : : /*
2 : : * This file is part of the MicroPython project, http://micropython.org/
3 : : *
4 : : * The MIT License (MIT)
5 : : *
6 : : * Copyright (c) 2016 Damien P. George
7 : : *
8 : : * Permission is hereby granted, free of charge, to any person obtaining a copy
9 : : * of this software and associated documentation files (the "Software"), to deal
10 : : * in the Software without restriction, including without limitation the rights
11 : : * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : : * copies of the Software, and to permit persons to whom the Software is
13 : : * furnished to do so, subject to the following conditions:
14 : : *
15 : : * The above copyright notice and this permission notice shall be included in
16 : : * all copies or substantial portions of the Software.
17 : : *
18 : : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : : * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : : * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 : : * THE SOFTWARE.
25 : : */
26 : :
27 : : #include <stdio.h>
28 : : #include <string.h>
29 : :
30 : : #include "py/runtime.h"
31 : : #include "py/binary.h"
32 : :
33 : : #if MICROPY_PY_FRAMEBUF
34 : :
35 : : #include "extmod/font_petme128_8x8.h"
36 : :
37 : : typedef struct _mp_obj_framebuf_t {
38 : : mp_obj_base_t base;
39 : : mp_obj_t buf_obj; // need to store this to prevent GC from reclaiming buf
40 : : void *buf;
41 : : uint16_t width, height, stride;
42 : : uint8_t format;
43 : : } mp_obj_framebuf_t;
44 : :
45 : : #if !MICROPY_ENABLE_DYNRUNTIME
46 : : static const mp_obj_type_t mp_type_framebuf;
47 : : #endif
48 : :
49 : : typedef void (*setpixel_t)(const mp_obj_framebuf_t *, unsigned int, unsigned int, uint32_t);
50 : : typedef uint32_t (*getpixel_t)(const mp_obj_framebuf_t *, unsigned int, unsigned int);
51 : : typedef void (*fill_rect_t)(const mp_obj_framebuf_t *, unsigned int, unsigned int, unsigned int, unsigned int, uint32_t);
52 : :
53 : : typedef struct _mp_framebuf_p_t {
54 : : setpixel_t setpixel;
55 : : getpixel_t getpixel;
56 : : fill_rect_t fill_rect;
57 : : } mp_framebuf_p_t;
58 : :
59 : : // constants for formats
60 : : #define FRAMEBUF_MVLSB (0)
61 : : #define FRAMEBUF_RGB565 (1)
62 : : #define FRAMEBUF_GS2_HMSB (5)
63 : : #define FRAMEBUF_GS4_HMSB (2)
64 : : #define FRAMEBUF_GS8 (6)
65 : : #define FRAMEBUF_MHLSB (3)
66 : : #define FRAMEBUF_MHMSB (4)
67 : :
68 : : // Functions for MHLSB and MHMSB
69 : :
70 : 1536 : static void mono_horiz_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) {
71 : 1536 : size_t index = (x + y * fb->stride) >> 3;
72 [ + + ]: 1536 : unsigned int offset = fb->format == FRAMEBUF_MHMSB ? x & 0x07 : 7 - (x & 0x07);
73 : 1536 : ((uint8_t *)fb->buf)[index] = (((uint8_t *)fb->buf)[index] & ~(0x01 << offset)) | ((col != 0) << offset);
74 : 1536 : }
75 : :
76 : 1396 : static uint32_t mono_horiz_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) {
77 : 1396 : size_t index = (x + y * fb->stride) >> 3;
78 [ + + ]: 1396 : unsigned int offset = fb->format == FRAMEBUF_MHMSB ? x & 0x07 : 7 - (x & 0x07);
79 : 1396 : return (((uint8_t *)fb->buf)[index] >> (offset)) & 0x01;
80 : : }
81 : :
82 : 68 : static void mono_horiz_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) {
83 : 68 : unsigned int reverse = fb->format == FRAMEBUF_MHMSB;
84 : 68 : unsigned int advance = fb->stride >> 3;
85 [ + + ]: 336 : while (w--) {
86 : 268 : uint8_t *b = &((uint8_t *)fb->buf)[(x >> 3) + y * advance];
87 [ + + ]: 268 : unsigned int offset = reverse ? x & 7 : 7 - (x & 7);
88 [ + + ]: 3636 : for (unsigned int hh = h; hh; --hh) {
89 : 3368 : *b = (*b & ~(0x01 << offset)) | ((col != 0) << offset);
90 : 3368 : b += advance;
91 : : }
92 : 268 : ++x;
93 : : }
94 : 68 : }
95 : :
96 : : // Functions for MVLSB format
97 : :
98 : 758 : static void mvlsb_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) {
99 : 758 : size_t index = (y >> 3) * fb->stride + x;
100 : 758 : uint8_t offset = y & 0x07;
101 : 758 : ((uint8_t *)fb->buf)[index] = (((uint8_t *)fb->buf)[index] & ~(0x01 << offset)) | ((col != 0) << offset);
102 : 758 : }
103 : :
104 : 634 : static uint32_t mvlsb_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) {
105 : 634 : return (((uint8_t *)fb->buf)[(y >> 3) * fb->stride + x] >> (y & 0x07)) & 0x01;
106 : : }
107 : :
108 : 34 : static void mvlsb_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) {
109 [ + + ]: 410 : while (h--) {
110 : 376 : uint8_t *b = &((uint8_t *)fb->buf)[(y >> 3) * fb->stride + x];
111 : 376 : uint8_t offset = y & 0x07;
112 [ + + ]: 2060 : for (unsigned int ww = w; ww; --ww) {
113 : 1684 : *b = (*b & ~(0x01 << offset)) | ((col != 0) << offset);
114 : 1684 : ++b;
115 : : }
116 : 376 : ++y;
117 : : }
118 : 34 : }
119 : :
120 : : // Functions for RGB565 format
121 : :
122 : 258 : static void rgb565_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) {
123 : 258 : ((uint16_t *)fb->buf)[x + y * fb->stride] = col;
124 : 258 : }
125 : :
126 : 248 : static uint32_t rgb565_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) {
127 : 248 : return ((uint16_t *)fb->buf)[x + y * fb->stride];
128 : : }
129 : :
130 : 14 : 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) {
131 : 14 : uint16_t *b = &((uint16_t *)fb->buf)[x + y * fb->stride];
132 [ + + ]: 68 : while (h--) {
133 [ + + ]: 250 : for (unsigned int ww = w; ww; --ww) {
134 : 196 : *b++ = col;
135 : : }
136 : 54 : b += fb->stride - w;
137 : : }
138 : 14 : }
139 : :
140 : : // Functions for GS2_HMSB format
141 : :
142 : 532 : static void gs2_hmsb_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) {
143 : 532 : uint8_t *pixel = &((uint8_t *)fb->buf)[(x + y * fb->stride) >> 2];
144 : 532 : uint8_t shift = (x & 0x3) << 1;
145 : 532 : uint8_t mask = 0x3 << shift;
146 : 532 : uint8_t color = (col & 0x3) << shift;
147 : 532 : *pixel = color | (*pixel & (~mask));
148 : 532 : }
149 : :
150 : 192 : static uint32_t gs2_hmsb_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) {
151 : 192 : uint8_t pixel = ((uint8_t *)fb->buf)[(x + y * fb->stride) >> 2];
152 : 192 : uint8_t shift = (x & 0x3) << 1;
153 : 192 : return (pixel >> shift) & 0x3;
154 : : }
155 : :
156 : 10 : static void gs2_hmsb_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) {
157 [ + + ]: 78 : for (unsigned int xx = x; xx < x + w; xx++) {
158 [ + + ]: 400 : for (unsigned int yy = y; yy < y + h; yy++) {
159 : 332 : gs2_hmsb_setpixel(fb, xx, yy, col);
160 : : }
161 : : }
162 : 10 : }
163 : :
164 : : // Functions for GS4_HMSB format
165 : :
166 : 148 : static void gs4_hmsb_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) {
167 : 148 : uint8_t *pixel = &((uint8_t *)fb->buf)[(x + y * fb->stride) >> 1];
168 : :
169 [ + + ]: 148 : if (x % 2) {
170 : 78 : *pixel = ((uint8_t)col & 0x0f) | (*pixel & 0xf0);
171 : : } else {
172 : 70 : *pixel = ((uint8_t)col << 4) | (*pixel & 0x0f);
173 : : }
174 : 148 : }
175 : :
176 : 156 : static uint32_t gs4_hmsb_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) {
177 [ + + ]: 156 : if (x % 2) {
178 : 74 : return ((uint8_t *)fb->buf)[(x + y * fb->stride) >> 1] & 0x0f;
179 : : }
180 : :
181 : 82 : return ((uint8_t *)fb->buf)[(x + y * fb->stride) >> 1] >> 4;
182 : : }
183 : :
184 : 42 : static void gs4_hmsb_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) {
185 : 42 : col &= 0x0f;
186 : 42 : uint8_t *pixel_pair = &((uint8_t *)fb->buf)[(x + y * fb->stride) >> 1];
187 : 42 : uint8_t col_shifted_left = col << 4;
188 : 42 : uint8_t col_pixel_pair = col_shifted_left | col;
189 : 42 : unsigned int pixel_count_till_next_line = (fb->stride - w) >> 1;
190 : 42 : bool odd_x = (x % 2 == 1);
191 : :
192 [ + + ]: 302 : while (h--) {
193 : 260 : unsigned int ww = w;
194 : :
195 [ + + ]: 260 : if (odd_x && ww > 0) {
196 : 8 : *pixel_pair = (*pixel_pair & 0xf0) | col;
197 : 8 : pixel_pair++;
198 : 8 : ww--;
199 : : }
200 : :
201 : 260 : memset(pixel_pair, col_pixel_pair, ww >> 1);
202 : 260 : pixel_pair += ww >> 1;
203 : :
204 [ + + ]: 260 : if (ww % 2) {
205 : 20 : *pixel_pair = col_shifted_left | (*pixel_pair & 0x0f);
206 [ + + ]: 20 : if (!odd_x) {
207 : 16 : pixel_pair++;
208 : : }
209 : : }
210 : :
211 : 260 : pixel_pair += pixel_count_till_next_line;
212 : : }
213 : 42 : }
214 : :
215 : : // Functions for GS8 format
216 : :
217 : 4116 : static void gs8_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) {
218 : 4116 : uint8_t *pixel = &((uint8_t *)fb->buf)[(x + y * fb->stride)];
219 : 4116 : *pixel = col & 0xff;
220 : 4116 : }
221 : :
222 : 4 : static uint32_t gs8_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) {
223 : 4 : return ((uint8_t *)fb->buf)[(x + y * fb->stride)];
224 : : }
225 : :
226 : 1846 : static void gs8_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) {
227 : 1846 : uint8_t *pixel = &((uint8_t *)fb->buf)[(x + y * fb->stride)];
228 [ + + ]: 6242 : while (h--) {
229 : 4396 : memset(pixel, col, w);
230 : 4396 : pixel += fb->stride;
231 : : }
232 : 1846 : }
233 : :
234 : : static mp_framebuf_p_t formats[] = {
235 : : [FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect},
236 : : [FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect},
237 : : [FRAMEBUF_GS2_HMSB] = {gs2_hmsb_setpixel, gs2_hmsb_getpixel, gs2_hmsb_fill_rect},
238 : : [FRAMEBUF_GS4_HMSB] = {gs4_hmsb_setpixel, gs4_hmsb_getpixel, gs4_hmsb_fill_rect},
239 : : [FRAMEBUF_GS8] = {gs8_setpixel, gs8_getpixel, gs8_fill_rect},
240 : : [FRAMEBUF_MHLSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect},
241 : : [FRAMEBUF_MHMSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect},
242 : : };
243 : :
244 : 7016 : static inline void setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) {
245 : 7016 : formats[fb->format].setpixel(fb, x, y, col);
246 : 4730 : }
247 : :
248 : 1446 : static void setpixel_checked(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, mp_int_t col, mp_int_t mask) {
249 [ + + + + : 1446 : if (mask && 0 <= x && x < fb->width && 0 <= y && y < fb->height) {
+ + + + ]
250 : 630 : setpixel(fb, x, y, col);
251 : : }
252 : 1446 : }
253 : :
254 : 2630 : static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) {
255 : 2630 : return formats[fb->format].getpixel(fb, x, y);
256 : : }
257 : :
258 : 2142 : static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) {
259 [ + + + + : 2142 : if (h < 1 || w < 1 || x + w <= 0 || y + h <= 0 || y >= fb->height || x >= fb->width) {
+ + + + +
+ ]
260 : : // No operation needed.
261 : : return;
262 : : }
263 : :
264 : : // clip to the framebuffer
265 : 1832 : int xend = MIN(fb->width, x + w);
266 : 1832 : int yend = MIN(fb->height, y + h);
267 : 1832 : x = MAX(x, 0);
268 : 1832 : y = MAX(y, 0);
269 : :
270 : 1832 : formats[fb->format].fill_rect(fb, x, y, xend - x, yend - y, col);
271 : : }
272 : :
273 : 176 : 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 : 176 : mp_arg_check_num(n_args, n_kw, 4, 5, false);
275 : :
276 : 176 : mp_int_t width = mp_obj_get_int(args_in[1]);
277 : 176 : mp_int_t height = mp_obj_get_int(args_in[2]);
278 : 176 : mp_int_t format = mp_obj_get_int(args_in[3]);
279 [ + + ]: 176 : mp_int_t stride = n_args >= 5 ? mp_obj_get_int(args_in[4]) : width;
280 : :
281 [ + + + - : 176 : if (width < 1 || height < 1 || width > 0xffff || height > 0xffff || stride > 0xffff || stride < width) {
+ + ]
282 : 10 : mp_raise_ValueError(NULL);
283 : : }
284 : :
285 : 166 : size_t bpp = 1;
286 : 166 : size_t height_required = height;
287 : 166 : size_t width_required = width;
288 : 166 : size_t strides_required = height - 1;
289 : :
290 [ + + + + : 166 : switch (format) {
+ + + ]
291 : 28 : case FRAMEBUF_MVLSB:
292 : 28 : height_required = (height + 7) & ~7;
293 : 28 : strides_required = height_required - 8;
294 : 28 : break;
295 : 44 : case FRAMEBUF_MHLSB:
296 : : case FRAMEBUF_MHMSB:
297 : 44 : stride = (stride + 7) & ~7;
298 : 44 : width_required = (width + 7) & ~7;
299 : 44 : break;
300 : 18 : case FRAMEBUF_GS2_HMSB:
301 : 18 : stride = (stride + 3) & ~3;
302 : 18 : width_required = (width + 3) & ~3;
303 : 18 : bpp = 2;
304 : 18 : break;
305 : 18 : case FRAMEBUF_GS4_HMSB:
306 : 18 : stride = (stride + 1) & ~1;
307 : 18 : width_required = (width + 1) & ~1;
308 : 18 : bpp = 4;
309 : 18 : break;
310 : : case FRAMEBUF_GS8:
311 : : bpp = 8;
312 : : break;
313 : 30 : case FRAMEBUF_RGB565:
314 : 30 : bpp = 16;
315 : 30 : break;
316 : : default:
317 : 2 : mp_raise_ValueError(MP_ERROR_TEXT("invalid format"));
318 : : }
319 : :
320 : 164 : mp_buffer_info_t bufinfo;
321 : 164 : mp_get_buffer_raise(args_in[0], &bufinfo, MP_BUFFER_WRITE);
322 : :
323 [ + + ]: 164 : if ((strides_required * stride + (height_required - strides_required) * width_required) * bpp / 8 > bufinfo.len) {
324 : 46 : mp_raise_ValueError(NULL);
325 : : }
326 : :
327 : 118 : mp_obj_framebuf_t *o = mp_obj_malloc(mp_obj_framebuf_t, type);
328 : 118 : o->buf_obj = args_in[0];
329 : 118 : o->buf = bufinfo.buf;
330 : 118 : o->width = width;
331 : 118 : o->height = height;
332 : 118 : o->format = format;
333 : 118 : o->stride = stride;
334 : :
335 : 118 : return MP_OBJ_FROM_PTR(o);
336 : : }
337 : :
338 : 128 : static void framebuf_args(const mp_obj_t *args_in, mp_int_t *args_out, int n) {
339 [ + + ]: 754 : for (int i = 0; i < n; ++i) {
340 : 626 : args_out[i] = mp_obj_get_int(args_in[i + 1]);
341 : : }
342 : 128 : }
343 : :
344 : 18 : static mp_int_t framebuf_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
345 : 18 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in);
346 : 18 : return mp_get_buffer(self->buf_obj, bufinfo, flags) ? 0 : 1;
347 : : }
348 : :
349 : 182 : static mp_obj_t framebuf_fill(mp_obj_t self_in, mp_obj_t col_in) {
350 : 182 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in);
351 : 182 : mp_int_t col = mp_obj_get_int(col_in);
352 : 182 : formats[self->format].fill_rect(self, 0, 0, self->width, self->height, col);
353 : 182 : return mp_const_none;
354 : : }
355 : : static MP_DEFINE_CONST_FUN_OBJ_2(framebuf_fill_obj, framebuf_fill);
356 : :
357 : 32 : static mp_obj_t framebuf_fill_rect(size_t n_args, const mp_obj_t *args_in) {
358 : 32 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
359 : 32 : mp_int_t args[5]; // x, y, w, h, col
360 : 32 : framebuf_args(args_in, args, 5);
361 : 32 : fill_rect(self, args[0], args[1], args[2], args[3], args[4]);
362 : 32 : return mp_const_none;
363 : : }
364 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_fill_rect_obj, 6, 6, framebuf_fill_rect);
365 : :
366 : 152 : static mp_obj_t framebuf_pixel(size_t n_args, const mp_obj_t *args_in) {
367 : 152 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
368 : 152 : mp_int_t x = mp_obj_get_int(args_in[1]);
369 : 152 : mp_int_t y = mp_obj_get_int(args_in[2]);
370 [ + - + - : 152 : if (0 <= x && x < self->width && 0 <= y && y < self->height) {
+ - + - ]
371 [ + + ]: 152 : if (n_args == 3) {
372 : : // get
373 : 50 : return MP_OBJ_NEW_SMALL_INT(getpixel(self, x, y));
374 : : } else {
375 : : // set
376 : 102 : setpixel(self, x, y, mp_obj_get_int(args_in[3]));
377 : : }
378 : : }
379 : : return mp_const_none;
380 : : }
381 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_pixel_obj, 3, 4, framebuf_pixel);
382 : :
383 : 8 : static mp_obj_t framebuf_hline(size_t n_args, const mp_obj_t *args_in) {
384 : 8 : (void)n_args;
385 : :
386 : 8 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
387 : 8 : mp_int_t args[4]; // x, y, w, col
388 : 8 : framebuf_args(args_in, args, 4);
389 : :
390 : 8 : fill_rect(self, args[0], args[1], args[2], 1, args[3]);
391 : :
392 : 8 : return mp_const_none;
393 : : }
394 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_hline_obj, 5, 5, framebuf_hline);
395 : :
396 : 6 : static mp_obj_t framebuf_vline(size_t n_args, const mp_obj_t *args_in) {
397 : 6 : (void)n_args;
398 : :
399 : 6 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
400 : 6 : mp_int_t args[4]; // x, y, h, col
401 : 6 : framebuf_args(args_in, args, 4);
402 : :
403 : 6 : fill_rect(self, args[0], args[1], 1, args[2], args[3]);
404 : :
405 : 6 : return mp_const_none;
406 : : }
407 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_vline_obj, 5, 5, framebuf_vline);
408 : :
409 : 18 : static mp_obj_t framebuf_rect(size_t n_args, const mp_obj_t *args_in) {
410 : 18 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
411 : 18 : mp_int_t args[5]; // x, y, w, h, col
412 : 18 : framebuf_args(args_in, args, 5);
413 [ + + + - ]: 18 : if (n_args > 6 && mp_obj_is_true(args_in[6])) {
414 : 12 : fill_rect(self, args[0], args[1], args[2], args[3], args[4]);
415 : : } else {
416 : 6 : fill_rect(self, args[0], args[1], args[2], 1, args[4]);
417 : 6 : fill_rect(self, args[0], args[1] + args[3] - 1, args[2], 1, args[4]);
418 : 6 : fill_rect(self, args[0], args[1], 1, args[3], args[4]);
419 : 6 : fill_rect(self, args[0] + args[2] - 1, args[1], 1, args[3], args[4]);
420 : : }
421 : 18 : return mp_const_none;
422 : : }
423 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_rect_obj, 6, 7, framebuf_rect);
424 : :
425 : 438 : static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t x2, mp_int_t y2, mp_int_t col) {
426 : 438 : mp_int_t dx = x2 - x1;
427 : 438 : mp_int_t sx;
428 [ + + ]: 438 : if (dx > 0) {
429 : : sx = 1;
430 : : } else {
431 : 270 : dx = -dx;
432 : 270 : sx = -1;
433 : : }
434 : :
435 : 438 : mp_int_t dy = y2 - y1;
436 : 438 : mp_int_t sy;
437 [ + + ]: 438 : if (dy > 0) {
438 : : sy = 1;
439 : : } else {
440 : 326 : dy = -dy;
441 : 326 : sy = -1;
442 : : }
443 : :
444 : 438 : bool steep;
445 [ + + ]: 438 : if (dy > dx) {
446 : 210 : mp_int_t temp;
447 : 210 : temp = x1;
448 : 210 : x1 = y1;
449 : 210 : y1 = temp;
450 : 210 : temp = dx;
451 : 210 : dx = dy;
452 : 210 : dy = temp;
453 : 210 : temp = sx;
454 : 210 : sx = sy;
455 : 210 : sy = temp;
456 : 210 : steep = true;
457 : : } else {
458 : : steep = false;
459 : : }
460 : :
461 : 438 : mp_int_t e = 2 * dy - dx;
462 [ + + ]: 4132 : for (mp_int_t i = 0; i < dx; ++i) {
463 [ + + ]: 3694 : if (steep) {
464 [ + + + + : 2588 : if (0 <= y1 && y1 < fb->width && 0 <= x1 && x1 < fb->height) {
+ + + + ]
465 : 2204 : setpixel(fb, y1, x1, col);
466 : : }
467 : : } else {
468 [ + + + + : 1106 : if (0 <= x1 && x1 < fb->width && 0 <= y1 && y1 < fb->height) {
+ + + + ]
469 : 970 : setpixel(fb, x1, y1, col);
470 : : }
471 : : }
472 [ + + ]: 4430 : while (e >= 0) {
473 : 736 : y1 += sy;
474 : 736 : e -= 2 * dx;
475 : : }
476 : 3694 : x1 += sx;
477 : 3694 : e += 2 * dy;
478 : : }
479 : :
480 [ + + + + : 438 : if (0 <= x2 && x2 < fb->width && 0 <= y2 && y2 < fb->height) {
+ + + + ]
481 : 356 : setpixel(fb, x2, y2, col);
482 : : }
483 : 438 : }
484 : :
485 : 14 : static mp_obj_t framebuf_line(size_t n_args, const mp_obj_t *args_in) {
486 : 14 : (void)n_args;
487 : :
488 : 14 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
489 : 14 : mp_int_t args[5]; // x1, y1, x2, y2, col
490 : 14 : framebuf_args(args_in, args, 5);
491 : :
492 : 14 : line(self, args[0], args[1], args[2], args[3], args[4]);
493 : :
494 : 14 : return mp_const_none;
495 : : }
496 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_line_obj, 6, 6, framebuf_line);
497 : :
498 : : // Q2 Q1
499 : : // Q3 Q4
500 : : #define ELLIPSE_MASK_FILL (0x10)
501 : : #define ELLIPSE_MASK_ALL (0x0f)
502 : : #define ELLIPSE_MASK_Q1 (0x01)
503 : : #define ELLIPSE_MASK_Q2 (0x02)
504 : : #define ELLIPSE_MASK_Q3 (0x04)
505 : : #define ELLIPSE_MASK_Q4 (0x08)
506 : :
507 : 616 : static void draw_ellipse_points(const mp_obj_framebuf_t *fb, mp_int_t cx, mp_int_t cy, mp_int_t x, mp_int_t y, mp_int_t col, mp_int_t mask) {
508 [ + + ]: 616 : if (mask & ELLIPSE_MASK_FILL) {
509 [ + + ]: 308 : if (mask & ELLIPSE_MASK_Q1) {
510 : 168 : fill_rect(fb, cx, cy - y, x + 1, 1, col);
511 : : }
512 [ + + ]: 308 : if (mask & ELLIPSE_MASK_Q2) {
513 : 196 : fill_rect(fb, cx - x, cy - y, x + 1, 1, col);
514 : : }
515 [ + + ]: 308 : if (mask & ELLIPSE_MASK_Q3) {
516 : 168 : fill_rect(fb, cx - x, cy + y, x + 1, 1, col);
517 : : }
518 [ + + ]: 308 : if (mask & ELLIPSE_MASK_Q4) {
519 : 196 : fill_rect(fb, cx, cy + y, x + 1, 1, col);
520 : : }
521 : : } else {
522 : 308 : setpixel_checked(fb, cx + x, cy - y, col, mask & ELLIPSE_MASK_Q1);
523 : 308 : setpixel_checked(fb, cx - x, cy - y, col, mask & ELLIPSE_MASK_Q2);
524 : 308 : setpixel_checked(fb, cx - x, cy + y, col, mask & ELLIPSE_MASK_Q3);
525 : 308 : setpixel_checked(fb, cx + x, cy + y, col, mask & ELLIPSE_MASK_Q4);
526 : : }
527 : 616 : }
528 : :
529 : 50 : static mp_obj_t framebuf_ellipse(size_t n_args, const mp_obj_t *args_in) {
530 : 50 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
531 : 50 : mp_int_t args[5];
532 : 50 : framebuf_args(args_in, args, 5); // cx, cy, xradius, yradius, col
533 [ + - + + ]: 50 : mp_int_t mask = (n_args > 6 && mp_obj_is_true(args_in[6])) ? ELLIPSE_MASK_FILL : 0;
534 [ + + ]: 50 : if (n_args > 7) {
535 : 26 : mask |= mp_obj_get_int(args_in[7]) & ELLIPSE_MASK_ALL;
536 : : } else {
537 : 24 : mask |= ELLIPSE_MASK_ALL;
538 : : }
539 [ + + + - ]: 50 : if (args[2] == 0 && args[3] == 0) {
540 : 6 : setpixel_checked(self, args[0], args[1], args[4], mask & ELLIPSE_MASK_ALL);
541 : 6 : return mp_const_none;
542 : : }
543 : 44 : mp_int_t two_asquare = 2 * args[2] * args[2];
544 : 44 : mp_int_t two_bsquare = 2 * args[3] * args[3];
545 : 44 : mp_int_t x = args[2];
546 : 44 : mp_int_t y = 0;
547 : 44 : mp_int_t xchange = args[3] * args[3] * (1 - 2 * args[2]);
548 : 44 : mp_int_t ychange = args[2] * args[2];
549 : 44 : mp_int_t ellipse_error = 0;
550 : 44 : mp_int_t stoppingx = two_bsquare * args[2];
551 : 44 : mp_int_t stoppingy = 0;
552 [ + + ]: 512 : while (stoppingx >= stoppingy) { // 1st set of points, y' > -1
553 : 468 : draw_ellipse_points(self, args[0], args[1], x, y, args[4], mask);
554 : 468 : y += 1;
555 : 468 : stoppingy += two_asquare;
556 : 468 : ellipse_error += ychange;
557 : 468 : ychange += two_asquare;
558 [ + + ]: 468 : if ((2 * ellipse_error + xchange) > 0) {
559 : 172 : x -= 1;
560 : 172 : stoppingx -= two_bsquare;
561 : 172 : ellipse_error += xchange;
562 : 172 : xchange += two_bsquare;
563 : : }
564 : : }
565 : : // 1st point set is done start the 2nd set of points
566 : 44 : x = 0;
567 : 44 : y = args[3];
568 : 44 : xchange = args[3] * args[3];
569 : 44 : ychange = args[2] * args[2] * (1 - 2 * args[3]);
570 : 44 : ellipse_error = 0;
571 : 44 : stoppingx = 0;
572 : 44 : stoppingy = two_asquare * args[3];
573 [ + + ]: 192 : while (stoppingx <= stoppingy) { // 2nd set of points, y' < -1
574 : 148 : draw_ellipse_points(self, args[0], args[1], x, y, args[4], mask);
575 : 148 : x += 1;
576 : 148 : stoppingx += two_bsquare;
577 : 148 : ellipse_error += xchange;
578 : 148 : xchange += two_bsquare;
579 [ + + ]: 148 : if ((2 * ellipse_error + ychange) > 0) {
580 : 92 : y -= 1;
581 : 92 : stoppingy -= two_asquare;
582 : 92 : ellipse_error += ychange;
583 : 92 : ychange += two_asquare;
584 : : }
585 : : }
586 : : return mp_const_none;
587 : : }
588 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_ellipse_obj, 6, 8, framebuf_ellipse);
589 : :
590 : : #if MICROPY_PY_ARRAY
591 : :
592 : 14932 : static mp_int_t poly_int(mp_buffer_info_t *bufinfo, size_t index) {
593 : 14932 : return mp_obj_get_int(mp_binary_get_val_array(bufinfo->typecode, bufinfo->buf, index));
594 : : }
595 : :
596 : 80 : static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) {
597 : 80 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
598 : :
599 : 80 : mp_int_t x = mp_obj_get_int(args_in[1]);
600 : 80 : mp_int_t y = mp_obj_get_int(args_in[2]);
601 : :
602 : 80 : mp_buffer_info_t bufinfo;
603 : 80 : mp_get_buffer_raise(args_in[3], &bufinfo, MP_BUFFER_READ);
604 : : // If an odd number of values was given, this rounds down to multiple of two.
605 : 80 : int n_poly = bufinfo.len / (mp_binary_get_size('@', bufinfo.typecode, NULL) * 2);
606 : :
607 [ + + ]: 80 : if (n_poly == 0) {
608 : : return mp_const_none;
609 : : }
610 : :
611 : 78 : mp_int_t col = mp_obj_get_int(args_in[4]);
612 [ + + + + ]: 78 : bool fill = n_args > 5 && mp_obj_is_true(args_in[5]);
613 : :
614 : 42 : if (fill) {
615 : : // This implements an integer version of http://alienryderflex.com/polygon_fill/
616 : :
617 : : // The idea is for each scan line, compute the sorted list of x
618 : : // coordinates where the scan line intersects the polygon edges,
619 : : // then fill between each resulting pair.
620 : :
621 : : // Restrict just to the scan lines that include the vertical extent of
622 : : // this polygon.
623 : : mp_int_t y_min = INT_MAX, y_max = INT_MIN;
624 [ + + ]: 348 : for (int i = 0; i < n_poly; i++) {
625 : 312 : mp_int_t py = poly_int(&bufinfo, i * 2 + 1);
626 : 312 : y_min = MIN(y_min, py);
627 : 312 : y_max = MAX(y_max, py);
628 : : }
629 : :
630 [ + + ]: 872 : for (mp_int_t row = y_min; row <= y_max; row++) {
631 : : // Each node is the x coordinate where an edge crosses this scan line.
632 : 836 : mp_int_t nodes[n_poly];
633 : 836 : int n_nodes = 0;
634 : 836 : mp_int_t px1 = poly_int(&bufinfo, 0);
635 : 836 : mp_int_t py1 = poly_int(&bufinfo, 1);
636 : 836 : int i = n_poly * 2 - 1;
637 : 6112 : do {
638 : 6112 : mp_int_t py2 = poly_int(&bufinfo, i--);
639 : 6112 : mp_int_t px2 = poly_int(&bufinfo, i--);
640 : :
641 : : // Don't include the bottom pixel of a given edge to avoid
642 : : // duplicating the node with the start of the next edge. This
643 : : // will miss some pixels on the boundary, and in particular
644 : : // at a local minima or inflection point.
645 [ + + + + ]: 6112 : if (py1 != py2 && ((py1 > row && py2 <= row) || (py1 <= row && py2 > row))) {
646 : 2664 : mp_int_t node = (32 * px1 + 32 * (px2 - px1) * (row - py1) / (py2 - py1) + 16) / 32;
647 : 2664 : nodes[n_nodes++] = node;
648 [ + + ]: 3448 : } else if (row == MAX(py1, py2)) {
649 : : // At local-minima, try and manually fill in the pixels that get missed above.
650 [ + + ]: 312 : if (py1 < py2) {
651 : 102 : setpixel_checked(self, x + px2, y + py2, col, 1);
652 [ + + ]: 210 : } else if (py2 < py1) {
653 : 106 : setpixel_checked(self, x + px1, y + py1, col, 1);
654 : : } else {
655 : : // Even though this is a hline and would be faster to
656 : : // use fill_rect, use line() because it handles x2 <
657 : : // x1.
658 : 104 : line(self, x + px1, y + py1, x + px2, y + py2, col);
659 : : }
660 : : }
661 : :
662 : 6112 : px1 = px2;
663 : 6112 : py1 = py2;
664 [ + + ]: 6112 : } while (i >= 0);
665 : :
666 [ + + ]: 836 : if (!n_nodes) {
667 : 36 : continue;
668 : : }
669 : :
670 : : // Sort the nodes left-to-right (bubble-sort for code size).
671 : : i = 0;
672 [ + + ]: 5242 : while (i < n_nodes - 1) {
673 [ + + ]: 4442 : if (nodes[i] > nodes[i + 1]) {
674 : 1510 : mp_int_t swap = nodes[i];
675 : 1510 : nodes[i] = nodes[i + 1];
676 : 1510 : nodes[i + 1] = swap;
677 [ + + ]: 1510 : if (i) {
678 : 1068 : i--;
679 : : }
680 : : } else {
681 : : i++;
682 : : }
683 : : }
684 : :
685 : : // Fill between each pair of nodes.
686 [ + + ]: 2132 : for (i = 0; i < n_nodes; i += 2) {
687 : 1332 : fill_rect(self, x + nodes[i], y + row, (nodes[i + 1] - nodes[i]) + 1, 1, col);
688 : : }
689 : : }
690 : : } else {
691 : : // Outline only.
692 : 42 : mp_int_t px1 = poly_int(&bufinfo, 0);
693 : 42 : mp_int_t py1 = poly_int(&bufinfo, 1);
694 : 42 : int i = n_poly * 2 - 1;
695 : 320 : do {
696 : 320 : mp_int_t py2 = poly_int(&bufinfo, i--);
697 : 320 : mp_int_t px2 = poly_int(&bufinfo, i--);
698 : 320 : line(self, x + px1, y + py1, x + px2, y + py2, col);
699 : 320 : px1 = px2;
700 : 320 : py1 = py2;
701 [ + + ]: 320 : } while (i >= 0);
702 : : }
703 : :
704 : : return mp_const_none;
705 : : }
706 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_poly_obj, 5, 6, framebuf_poly);
707 : :
708 : : #endif // MICROPY_PY_ARRAY
709 : :
710 : 20 : static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) {
711 : 20 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
712 : 20 : mp_obj_t source_in = mp_obj_cast_to_native_base(args_in[1], MP_OBJ_FROM_PTR(&mp_type_framebuf));
713 [ + + ]: 20 : if (source_in == MP_OBJ_NULL) {
714 : 4 : mp_raise_TypeError(NULL);
715 : : }
716 : 16 : mp_obj_framebuf_t *source = MP_OBJ_TO_PTR(source_in);
717 : :
718 : 16 : mp_int_t x = mp_obj_get_int(args_in[2]);
719 : 16 : mp_int_t y = mp_obj_get_int(args_in[3]);
720 : 16 : mp_int_t key = -1;
721 [ + + ]: 16 : if (n_args > 4) {
722 : 14 : key = mp_obj_get_int(args_in[4]);
723 : : }
724 : 14 : mp_obj_framebuf_t *palette = NULL;
725 [ + + + - ]: 14 : if (n_args > 5 && args_in[5] != mp_const_none) {
726 : 2 : palette = MP_OBJ_TO_PTR(mp_obj_cast_to_native_base(args_in[5], MP_OBJ_FROM_PTR(&mp_type_framebuf)));
727 : : }
728 : :
729 : 16 : if (
730 [ + + ]: 16 : (x >= self->width) ||
731 [ + - ]: 12 : (y >= self->height) ||
732 [ + - ]: 12 : (-x >= source->width) ||
733 [ + - ]: 12 : (-y >= source->height)
734 : : ) {
735 : : // Out of bounds, no-op.
736 : : return mp_const_none;
737 : : }
738 : :
739 : : // Clip.
740 : 12 : int x0 = MAX(0, x);
741 : 12 : int y0 = MAX(0, y);
742 : 12 : int x1 = MAX(0, -x);
743 : 12 : int y1 = MAX(0, -y);
744 : 12 : int x0end = MIN(self->width, x + source->width);
745 : 12 : int y0end = MIN(self->height, y + source->height);
746 : :
747 [ + + ]: 50 : for (; y0 < y0end; ++y0) {
748 : : int cx1 = x1;
749 [ + + ]: 204 : for (int cx0 = x0; cx0 < x0end; ++cx0) {
750 : 166 : uint32_t col = getpixel(source, cx1, y1);
751 [ + + ]: 166 : if (palette) {
752 : 128 : col = getpixel(palette, col, 0);
753 : : }
754 [ + + ]: 166 : if (col != (uint32_t)key) {
755 : 156 : setpixel(self, cx0, y0, col);
756 : : }
757 : 166 : ++cx1;
758 : : }
759 : 38 : ++y1;
760 : : }
761 : : return mp_const_none;
762 : : }
763 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_obj, 4, 6, framebuf_blit);
764 : :
765 : 52 : static mp_obj_t framebuf_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ystep_in) {
766 : 52 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in);
767 : 52 : mp_int_t xstep = mp_obj_get_int(xstep_in);
768 : 52 : mp_int_t ystep = mp_obj_get_int(ystep_in);
769 : 52 : int sx, y, xend, yend, dx, dy;
770 [ + + ]: 52 : if (xstep < 0) {
771 : 12 : sx = 0;
772 : 12 : xend = self->width + xstep;
773 [ + - ]: 12 : if (xend <= 0) {
774 : : return mp_const_none;
775 : : }
776 : : dx = 1;
777 : : } else {
778 : 40 : sx = self->width - 1;
779 : 40 : xend = xstep - 1;
780 [ + + ]: 40 : if (xend >= sx) {
781 : : return mp_const_none;
782 : : }
783 : : dx = -1;
784 : : }
785 [ + + ]: 48 : if (ystep < 0) {
786 : 14 : y = 0;
787 : 14 : yend = self->height + ystep;
788 [ + + ]: 14 : if (yend <= 0) {
789 : : return mp_const_none;
790 : : }
791 : 46 : dy = 1;
792 : : } else {
793 : 34 : y = self->height - 1;
794 : 34 : yend = ystep - 1;
795 [ + - ]: 34 : if (yend >= y) {
796 : : return mp_const_none;
797 : : }
798 : : dy = -1;
799 : : }
800 [ + + ]: 572 : for (; y != yend; y += dy) {
801 [ + + ]: 2812 : for (int x = sx; x != xend; x += dx) {
802 : 2286 : setpixel(self, x, y, getpixel(self, x - xstep, y - ystep));
803 : : }
804 : : }
805 : : return mp_const_none;
806 : : }
807 : : static MP_DEFINE_CONST_FUN_OBJ_3(framebuf_scroll_obj, framebuf_scroll);
808 : :
809 : 18 : static mp_obj_t framebuf_text(size_t n_args, const mp_obj_t *args_in) {
810 : : // extract arguments
811 : 18 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
812 : 18 : const char *str = mp_obj_str_get_str(args_in[1]);
813 : 18 : mp_int_t x0 = mp_obj_get_int(args_in[2]);
814 : 18 : mp_int_t y0 = mp_obj_get_int(args_in[3]);
815 : 18 : mp_int_t col = 1;
816 [ + + ]: 18 : if (n_args >= 5) {
817 : 12 : col = mp_obj_get_int(args_in[4]);
818 : : }
819 : :
820 : : // loop over chars
821 [ + + ]: 84 : for (; *str; ++str) {
822 : : // get char and make sure its in range of font
823 : 66 : int chr = *(uint8_t *)str;
824 [ + + ]: 66 : if (chr < 32 || chr > 127) {
825 : 6 : chr = 127;
826 : : }
827 : : // get char data
828 : 66 : const uint8_t *chr_data = &font_petme128_8x8[(chr - 32) * 8];
829 : : // loop over char data
830 [ + + ]: 594 : for (int j = 0; j < 8; j++, x0++) {
831 [ + - + + ]: 528 : if (0 <= x0 && x0 < self->width) { // clip x
832 : 90 : uint vline_data = chr_data[j]; // each byte is a column of 8 pixels, LSB at top
833 [ + + ]: 558 : for (int y = y0; vline_data; vline_data >>= 1, y++) { // scan over vertical column
834 [ + + ]: 468 : if (vline_data & 1) { // only draw if pixel set
835 [ + - + - ]: 312 : if (0 <= y && y < self->height) { // clip y
836 : 312 : setpixel(self, x0, y, col);
837 : : }
838 : : }
839 : : }
840 : : }
841 : : }
842 : : }
843 : 18 : return mp_const_none;
844 : : }
845 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_text_obj, 4, 5, framebuf_text);
846 : :
847 : : #if !MICROPY_ENABLE_DYNRUNTIME
848 : : static const mp_rom_map_elem_t framebuf_locals_dict_table[] = {
849 : : { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&framebuf_fill_obj) },
850 : : { MP_ROM_QSTR(MP_QSTR_fill_rect), MP_ROM_PTR(&framebuf_fill_rect_obj) },
851 : : { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&framebuf_pixel_obj) },
852 : : { MP_ROM_QSTR(MP_QSTR_hline), MP_ROM_PTR(&framebuf_hline_obj) },
853 : : { MP_ROM_QSTR(MP_QSTR_vline), MP_ROM_PTR(&framebuf_vline_obj) },
854 : : { MP_ROM_QSTR(MP_QSTR_rect), MP_ROM_PTR(&framebuf_rect_obj) },
855 : : { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&framebuf_line_obj) },
856 : : { MP_ROM_QSTR(MP_QSTR_ellipse), MP_ROM_PTR(&framebuf_ellipse_obj) },
857 : : #if MICROPY_PY_ARRAY
858 : : { MP_ROM_QSTR(MP_QSTR_poly), MP_ROM_PTR(&framebuf_poly_obj) },
859 : : #endif
860 : : { MP_ROM_QSTR(MP_QSTR_blit), MP_ROM_PTR(&framebuf_blit_obj) },
861 : : { MP_ROM_QSTR(MP_QSTR_scroll), MP_ROM_PTR(&framebuf_scroll_obj) },
862 : : { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&framebuf_text_obj) },
863 : : };
864 : : static MP_DEFINE_CONST_DICT(framebuf_locals_dict, framebuf_locals_dict_table);
865 : :
866 : : static MP_DEFINE_CONST_OBJ_TYPE(
867 : : mp_type_framebuf,
868 : : MP_QSTR_FrameBuffer,
869 : : MP_TYPE_FLAG_NONE,
870 : : make_new, framebuf_make_new,
871 : : buffer, framebuf_get_buffer,
872 : : locals_dict, &framebuf_locals_dict
873 : : );
874 : : #endif
875 : :
876 : : #if !MICROPY_ENABLE_DYNRUNTIME
877 : : // This factory function is provided for backwards compatibility with the old
878 : : // FrameBuffer1 class which did not support a format argument.
879 : 4 : static mp_obj_t legacy_framebuffer1(size_t n_args, const mp_obj_t *args_in) {
880 [ + + ]: 4 : mp_obj_t args[] = {args_in[0], args_in[1], args_in[2], MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MVLSB), n_args >= 4 ? args_in[3] : args_in[1] };
881 : 4 : return framebuf_make_new(&mp_type_framebuf, 5, 0, args);
882 : : }
883 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(legacy_framebuffer1_obj, 3, 4, legacy_framebuffer1);
884 : :
885 : : static const mp_rom_map_elem_t framebuf_module_globals_table[] = {
886 : : { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_framebuf) },
887 : : { MP_ROM_QSTR(MP_QSTR_FrameBuffer), MP_ROM_PTR(&mp_type_framebuf) },
888 : : { MP_ROM_QSTR(MP_QSTR_FrameBuffer1), MP_ROM_PTR(&legacy_framebuffer1_obj) },
889 : : { MP_ROM_QSTR(MP_QSTR_MVLSB), MP_ROM_INT(FRAMEBUF_MVLSB) },
890 : : { MP_ROM_QSTR(MP_QSTR_MONO_VLSB), MP_ROM_INT(FRAMEBUF_MVLSB) },
891 : : { MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(FRAMEBUF_RGB565) },
892 : : { MP_ROM_QSTR(MP_QSTR_GS2_HMSB), MP_ROM_INT(FRAMEBUF_GS2_HMSB) },
893 : : { MP_ROM_QSTR(MP_QSTR_GS4_HMSB), MP_ROM_INT(FRAMEBUF_GS4_HMSB) },
894 : : { MP_ROM_QSTR(MP_QSTR_GS8), MP_ROM_INT(FRAMEBUF_GS8) },
895 : : { MP_ROM_QSTR(MP_QSTR_MONO_HLSB), MP_ROM_INT(FRAMEBUF_MHLSB) },
896 : : { MP_ROM_QSTR(MP_QSTR_MONO_HMSB), MP_ROM_INT(FRAMEBUF_MHMSB) },
897 : : };
898 : :
899 : : static MP_DEFINE_CONST_DICT(framebuf_module_globals, framebuf_module_globals_table);
900 : :
901 : : const mp_obj_module_t mp_module_framebuf = {
902 : : .base = { &mp_type_module },
903 : : .globals = (mp_obj_dict_t *)&framebuf_module_globals,
904 : : };
905 : :
906 : : MP_REGISTER_MODULE(MP_QSTR_framebuf, mp_module_framebuf);
907 : : #endif
908 : :
909 : : #endif // MICROPY_PY_FRAMEBUF
|