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 : 4114 : static void gs8_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) {
218 : 4114 : uint8_t *pixel = &((uint8_t *)fb->buf)[(x + y * fb->stride)];
219 : 4114 : *pixel = col & 0xff;
220 : 4114 : }
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 : 1840 : 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 : 1840 : uint8_t *pixel = &((uint8_t *)fb->buf)[(x + y * fb->stride)];
228 [ + + ]: 6056 : while (h--) {
229 : 4216 : memset(pixel, col, w);
230 : 4216 : pixel += fb->stride;
231 : : }
232 : 1840 : }
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 : 7014 : static inline void setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) {
245 : 7014 : formats[fb->format].setpixel(fb, x, y, col);
246 : 4728 : }
247 : :
248 : 1440 : 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 [ + + + + : 1440 : if (mask && 0 <= x && x < fb->width && 0 <= y && y < fb->height) {
+ + + + ]
250 : 628 : setpixel(fb, x, y, col);
251 : : }
252 : 1440 : }
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 : 116 : 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 : 116 : mp_arg_check_num(n_args, n_kw, 4, 5, false);
275 : :
276 : 116 : mp_int_t width = mp_obj_get_int(args_in[1]);
277 : 116 : mp_int_t height = mp_obj_get_int(args_in[2]);
278 : 116 : mp_int_t format = mp_obj_get_int(args_in[3]);
279 [ + + ]: 116 : mp_int_t stride = n_args >= 5 ? mp_obj_get_int(args_in[4]) : width;
280 : :
281 [ + + + - : 116 : if (width < 1 || height < 1 || width > 0xffff || height > 0xffff || stride > 0xffff || stride < width) {
+ + ]
282 : 8 : mp_raise_ValueError(NULL);
283 : : }
284 : :
285 : 108 : size_t height_required = height;
286 : 108 : size_t bpp = 1;
287 : :
288 [ + + + + : 108 : switch (format) {
+ + + ]
289 : 12 : case FRAMEBUF_MVLSB:
290 : 12 : height_required = (height + 7) & ~7;
291 : 12 : break;
292 : 34 : case FRAMEBUF_MHLSB:
293 : : case FRAMEBUF_MHMSB:
294 : 34 : stride = (stride + 7) & ~7;
295 : 34 : break;
296 : 12 : case FRAMEBUF_GS2_HMSB:
297 : 12 : stride = (stride + 3) & ~3;
298 : 12 : bpp = 2;
299 : 12 : break;
300 : 12 : case FRAMEBUF_GS4_HMSB:
301 : 12 : stride = (stride + 1) & ~1;
302 : 12 : bpp = 4;
303 : 12 : break;
304 : : case FRAMEBUF_GS8:
305 : : bpp = 8;
306 : : break;
307 : 20 : case FRAMEBUF_RGB565:
308 : 20 : bpp = 16;
309 : 20 : break;
310 : : default:
311 : 2 : mp_raise_ValueError(MP_ERROR_TEXT("invalid format"));
312 : : }
313 : :
314 : 106 : mp_buffer_info_t bufinfo;
315 : 106 : mp_get_buffer_raise(args_in[0], &bufinfo, MP_BUFFER_WRITE);
316 : :
317 [ + + ]: 106 : if (height_required * stride * bpp / 8 > bufinfo.len) {
318 : 26 : mp_raise_ValueError(NULL);
319 : : }
320 : :
321 : 80 : mp_obj_framebuf_t *o = mp_obj_malloc(mp_obj_framebuf_t, type);
322 : 80 : o->buf_obj = args_in[0];
323 : 80 : o->buf = bufinfo.buf;
324 : 80 : o->width = width;
325 : 80 : o->height = height;
326 : 80 : o->format = format;
327 : 80 : o->stride = stride;
328 : :
329 : 80 : return MP_OBJ_FROM_PTR(o);
330 : : }
331 : :
332 : 122 : static void framebuf_args(const mp_obj_t *args_in, mp_int_t *args_out, int n) {
333 [ + + ]: 718 : for (int i = 0; i < n; ++i) {
334 : 596 : args_out[i] = mp_obj_get_int(args_in[i + 1]);
335 : : }
336 : 122 : }
337 : :
338 : 18 : static mp_int_t framebuf_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
339 : 18 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in);
340 : 18 : return mp_get_buffer(self->buf_obj, bufinfo, flags) ? 0 : 1;
341 : : }
342 : :
343 : 176 : static mp_obj_t framebuf_fill(mp_obj_t self_in, mp_obj_t col_in) {
344 : 176 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in);
345 : 176 : mp_int_t col = mp_obj_get_int(col_in);
346 : 176 : formats[self->format].fill_rect(self, 0, 0, self->width, self->height, col);
347 : 176 : return mp_const_none;
348 : : }
349 : : static MP_DEFINE_CONST_FUN_OBJ_2(framebuf_fill_obj, framebuf_fill);
350 : :
351 : 32 : static mp_obj_t framebuf_fill_rect(size_t n_args, const mp_obj_t *args_in) {
352 : 32 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
353 : 32 : mp_int_t args[5]; // x, y, w, h, col
354 : 32 : framebuf_args(args_in, args, 5);
355 : 32 : fill_rect(self, args[0], args[1], args[2], args[3], args[4]);
356 : 32 : return mp_const_none;
357 : : }
358 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_fill_rect_obj, 6, 6, framebuf_fill_rect);
359 : :
360 : 152 : static mp_obj_t framebuf_pixel(size_t n_args, const mp_obj_t *args_in) {
361 : 152 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
362 : 152 : mp_int_t x = mp_obj_get_int(args_in[1]);
363 : 152 : mp_int_t y = mp_obj_get_int(args_in[2]);
364 [ + - + - : 152 : if (0 <= x && x < self->width && 0 <= y && y < self->height) {
+ - + - ]
365 [ + + ]: 152 : if (n_args == 3) {
366 : : // get
367 : 50 : return MP_OBJ_NEW_SMALL_INT(getpixel(self, x, y));
368 : : } else {
369 : : // set
370 : 102 : setpixel(self, x, y, mp_obj_get_int(args_in[3]));
371 : : }
372 : : }
373 : : return mp_const_none;
374 : : }
375 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_pixel_obj, 3, 4, framebuf_pixel);
376 : :
377 : 8 : static mp_obj_t framebuf_hline(size_t n_args, const mp_obj_t *args_in) {
378 : 8 : (void)n_args;
379 : :
380 : 8 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
381 : 8 : mp_int_t args[4]; // x, y, w, col
382 : 8 : framebuf_args(args_in, args, 4);
383 : :
384 : 8 : fill_rect(self, args[0], args[1], args[2], 1, args[3]);
385 : :
386 : 8 : return mp_const_none;
387 : : }
388 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_hline_obj, 5, 5, framebuf_hline);
389 : :
390 : 6 : static mp_obj_t framebuf_vline(size_t n_args, const mp_obj_t *args_in) {
391 : 6 : (void)n_args;
392 : :
393 : 6 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
394 : 6 : mp_int_t args[4]; // x, y, h, col
395 : 6 : framebuf_args(args_in, args, 4);
396 : :
397 : 6 : fill_rect(self, args[0], args[1], 1, args[2], args[3]);
398 : :
399 : 6 : return mp_const_none;
400 : : }
401 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_vline_obj, 5, 5, framebuf_vline);
402 : :
403 : 18 : static mp_obj_t framebuf_rect(size_t n_args, const mp_obj_t *args_in) {
404 : 18 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
405 : 18 : mp_int_t args[5]; // x, y, w, h, col
406 : 18 : framebuf_args(args_in, args, 5);
407 [ + + + - ]: 18 : if (n_args > 6 && mp_obj_is_true(args_in[6])) {
408 : 12 : fill_rect(self, args[0], args[1], args[2], args[3], args[4]);
409 : : } else {
410 : 6 : fill_rect(self, args[0], args[1], args[2], 1, args[4]);
411 : 6 : fill_rect(self, args[0], args[1] + args[3] - 1, args[2], 1, args[4]);
412 : 6 : fill_rect(self, args[0], args[1], 1, args[3], args[4]);
413 : 6 : fill_rect(self, args[0] + args[2] - 1, args[1], 1, args[3], args[4]);
414 : : }
415 : 18 : return mp_const_none;
416 : : }
417 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_rect_obj, 6, 7, framebuf_rect);
418 : :
419 : 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) {
420 : 438 : mp_int_t dx = x2 - x1;
421 : 438 : mp_int_t sx;
422 [ + + ]: 438 : if (dx > 0) {
423 : : sx = 1;
424 : : } else {
425 : 270 : dx = -dx;
426 : 270 : sx = -1;
427 : : }
428 : :
429 : 438 : mp_int_t dy = y2 - y1;
430 : 438 : mp_int_t sy;
431 [ + + ]: 438 : if (dy > 0) {
432 : : sy = 1;
433 : : } else {
434 : 326 : dy = -dy;
435 : 326 : sy = -1;
436 : : }
437 : :
438 : 438 : bool steep;
439 [ + + ]: 438 : if (dy > dx) {
440 : 210 : mp_int_t temp;
441 : 210 : temp = x1;
442 : 210 : x1 = y1;
443 : 210 : y1 = temp;
444 : 210 : temp = dx;
445 : 210 : dx = dy;
446 : 210 : dy = temp;
447 : 210 : temp = sx;
448 : 210 : sx = sy;
449 : 210 : sy = temp;
450 : 210 : steep = true;
451 : : } else {
452 : : steep = false;
453 : : }
454 : :
455 : 438 : mp_int_t e = 2 * dy - dx;
456 [ + + ]: 4132 : for (mp_int_t i = 0; i < dx; ++i) {
457 [ + + ]: 3694 : if (steep) {
458 [ + + + + : 2588 : if (0 <= y1 && y1 < fb->width && 0 <= x1 && x1 < fb->height) {
+ + + + ]
459 : 2204 : setpixel(fb, y1, x1, col);
460 : : }
461 : : } else {
462 [ + + + + : 1106 : if (0 <= x1 && x1 < fb->width && 0 <= y1 && y1 < fb->height) {
+ + + + ]
463 : 970 : setpixel(fb, x1, y1, col);
464 : : }
465 : : }
466 [ + + ]: 4430 : while (e >= 0) {
467 : 736 : y1 += sy;
468 : 736 : e -= 2 * dx;
469 : : }
470 : 3694 : x1 += sx;
471 : 3694 : e += 2 * dy;
472 : : }
473 : :
474 [ + + + + : 438 : if (0 <= x2 && x2 < fb->width && 0 <= y2 && y2 < fb->height) {
+ + + + ]
475 : 356 : setpixel(fb, x2, y2, col);
476 : : }
477 : 438 : }
478 : :
479 : 14 : static mp_obj_t framebuf_line(size_t n_args, const mp_obj_t *args_in) {
480 : 14 : (void)n_args;
481 : :
482 : 14 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
483 : 14 : mp_int_t args[5]; // x1, y1, x2, y2, col
484 : 14 : framebuf_args(args_in, args, 5);
485 : :
486 : 14 : line(self, args[0], args[1], args[2], args[3], args[4]);
487 : :
488 : 14 : return mp_const_none;
489 : : }
490 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_line_obj, 6, 6, framebuf_line);
491 : :
492 : : // Q2 Q1
493 : : // Q3 Q4
494 : : #define ELLIPSE_MASK_FILL (0x10)
495 : : #define ELLIPSE_MASK_ALL (0x0f)
496 : : #define ELLIPSE_MASK_Q1 (0x01)
497 : : #define ELLIPSE_MASK_Q2 (0x02)
498 : : #define ELLIPSE_MASK_Q3 (0x04)
499 : : #define ELLIPSE_MASK_Q4 (0x08)
500 : :
501 : 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) {
502 [ + + ]: 616 : if (mask & ELLIPSE_MASK_FILL) {
503 [ + + ]: 308 : if (mask & ELLIPSE_MASK_Q1) {
504 : 168 : fill_rect(fb, cx, cy - y, x + 1, 1, col);
505 : : }
506 [ + + ]: 308 : if (mask & ELLIPSE_MASK_Q2) {
507 : 196 : fill_rect(fb, cx - x, cy - y, x + 1, 1, col);
508 : : }
509 [ + + ]: 308 : if (mask & ELLIPSE_MASK_Q3) {
510 : 168 : fill_rect(fb, cx - x, cy + y, x + 1, 1, col);
511 : : }
512 [ + + ]: 308 : if (mask & ELLIPSE_MASK_Q4) {
513 : 196 : fill_rect(fb, cx, cy + y, x + 1, 1, col);
514 : : }
515 : : } else {
516 : 308 : setpixel_checked(fb, cx + x, cy - y, col, mask & ELLIPSE_MASK_Q1);
517 : 308 : setpixel_checked(fb, cx - x, cy - y, col, mask & ELLIPSE_MASK_Q2);
518 : 308 : setpixel_checked(fb, cx - x, cy + y, col, mask & ELLIPSE_MASK_Q3);
519 : 308 : setpixel_checked(fb, cx + x, cy + y, col, mask & ELLIPSE_MASK_Q4);
520 : : }
521 : 616 : }
522 : :
523 : 44 : static mp_obj_t framebuf_ellipse(size_t n_args, const mp_obj_t *args_in) {
524 : 44 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
525 : 44 : mp_int_t args[5];
526 : 44 : framebuf_args(args_in, args, 5); // cx, cy, xradius, yradius, col
527 [ + - + + ]: 44 : mp_int_t mask = (n_args > 6 && mp_obj_is_true(args_in[6])) ? ELLIPSE_MASK_FILL : 0;
528 [ + + ]: 44 : if (n_args > 7) {
529 : 24 : mask |= mp_obj_get_int(args_in[7]) & ELLIPSE_MASK_ALL;
530 : : } else {
531 : 20 : mask |= ELLIPSE_MASK_ALL;
532 : : }
533 : 44 : mp_int_t two_asquare = 2 * args[2] * args[2];
534 : 44 : mp_int_t two_bsquare = 2 * args[3] * args[3];
535 : 44 : mp_int_t x = args[2];
536 : 44 : mp_int_t y = 0;
537 : 44 : mp_int_t xchange = args[3] * args[3] * (1 - 2 * args[2]);
538 : 44 : mp_int_t ychange = args[2] * args[2];
539 : 44 : mp_int_t ellipse_error = 0;
540 : 44 : mp_int_t stoppingx = two_bsquare * args[2];
541 : 44 : mp_int_t stoppingy = 0;
542 [ + + ]: 512 : while (stoppingx >= stoppingy) { // 1st set of points, y' > -1
543 : 468 : draw_ellipse_points(self, args[0], args[1], x, y, args[4], mask);
544 : 468 : y += 1;
545 : 468 : stoppingy += two_asquare;
546 : 468 : ellipse_error += ychange;
547 : 468 : ychange += two_asquare;
548 [ + + ]: 468 : if ((2 * ellipse_error + xchange) > 0) {
549 : 172 : x -= 1;
550 : 172 : stoppingx -= two_bsquare;
551 : 172 : ellipse_error += xchange;
552 : 172 : xchange += two_bsquare;
553 : : }
554 : : }
555 : : // 1st point set is done start the 2nd set of points
556 : 44 : x = 0;
557 : 44 : y = args[3];
558 : 44 : xchange = args[3] * args[3];
559 : 44 : ychange = args[2] * args[2] * (1 - 2 * args[3]);
560 : 44 : ellipse_error = 0;
561 : 44 : stoppingx = 0;
562 : 44 : stoppingy = two_asquare * args[3];
563 [ + + ]: 192 : while (stoppingx <= stoppingy) { // 2nd set of points, y' < -1
564 : 148 : draw_ellipse_points(self, args[0], args[1], x, y, args[4], mask);
565 : 148 : x += 1;
566 : 148 : stoppingx += two_bsquare;
567 : 148 : ellipse_error += xchange;
568 : 148 : xchange += two_bsquare;
569 [ + + ]: 148 : if ((2 * ellipse_error + ychange) > 0) {
570 : 92 : y -= 1;
571 : 92 : stoppingy -= two_asquare;
572 : 92 : ellipse_error += ychange;
573 : 92 : ychange += two_asquare;
574 : : }
575 : : }
576 : 44 : return mp_const_none;
577 : : }
578 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_ellipse_obj, 6, 8, framebuf_ellipse);
579 : :
580 : : #if MICROPY_PY_ARRAY
581 : :
582 : 14932 : static mp_int_t poly_int(mp_buffer_info_t *bufinfo, size_t index) {
583 : 14932 : return mp_obj_get_int(mp_binary_get_val_array(bufinfo->typecode, bufinfo->buf, index));
584 : : }
585 : :
586 : 80 : static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) {
587 : 80 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
588 : :
589 : 80 : mp_int_t x = mp_obj_get_int(args_in[1]);
590 : 80 : mp_int_t y = mp_obj_get_int(args_in[2]);
591 : :
592 : 80 : mp_buffer_info_t bufinfo;
593 : 80 : mp_get_buffer_raise(args_in[3], &bufinfo, MP_BUFFER_READ);
594 : : // If an odd number of values was given, this rounds down to multiple of two.
595 : 80 : int n_poly = bufinfo.len / (mp_binary_get_size('@', bufinfo.typecode, NULL) * 2);
596 : :
597 [ + + ]: 80 : if (n_poly == 0) {
598 : : return mp_const_none;
599 : : }
600 : :
601 : 78 : mp_int_t col = mp_obj_get_int(args_in[4]);
602 [ + + + + ]: 78 : bool fill = n_args > 5 && mp_obj_is_true(args_in[5]);
603 : :
604 : 42 : if (fill) {
605 : : // This implements an integer version of http://alienryderflex.com/polygon_fill/
606 : :
607 : : // The idea is for each scan line, compute the sorted list of x
608 : : // coordinates where the scan line intersects the polygon edges,
609 : : // then fill between each resulting pair.
610 : :
611 : : // Restrict just to the scan lines that include the vertical extent of
612 : : // this polygon.
613 : : mp_int_t y_min = INT_MAX, y_max = INT_MIN;
614 [ + + ]: 348 : for (int i = 0; i < n_poly; i++) {
615 : 312 : mp_int_t py = poly_int(&bufinfo, i * 2 + 1);
616 : 312 : y_min = MIN(y_min, py);
617 : 312 : y_max = MAX(y_max, py);
618 : : }
619 : :
620 [ + + ]: 872 : for (mp_int_t row = y_min; row <= y_max; row++) {
621 : : // Each node is the x coordinate where an edge crosses this scan line.
622 : 836 : mp_int_t nodes[n_poly];
623 : 836 : int n_nodes = 0;
624 : 836 : mp_int_t px1 = poly_int(&bufinfo, 0);
625 : 836 : mp_int_t py1 = poly_int(&bufinfo, 1);
626 : 836 : int i = n_poly * 2 - 1;
627 : 6112 : do {
628 : 6112 : mp_int_t py2 = poly_int(&bufinfo, i--);
629 : 6112 : mp_int_t px2 = poly_int(&bufinfo, i--);
630 : :
631 : : // Don't include the bottom pixel of a given edge to avoid
632 : : // duplicating the node with the start of the next edge. This
633 : : // will miss some pixels on the boundary, and in particular
634 : : // at a local minima or inflection point.
635 [ + + + + ]: 6112 : if (py1 != py2 && ((py1 > row && py2 <= row) || (py1 <= row && py2 > row))) {
636 : 2664 : mp_int_t node = (32 * px1 + 32 * (px2 - px1) * (row - py1) / (py2 - py1) + 16) / 32;
637 : 2664 : nodes[n_nodes++] = node;
638 [ + + ]: 3448 : } else if (row == MAX(py1, py2)) {
639 : : // At local-minima, try and manually fill in the pixels that get missed above.
640 [ + + ]: 312 : if (py1 < py2) {
641 : 102 : setpixel_checked(self, x + px2, y + py2, col, 1);
642 [ + + ]: 210 : } else if (py2 < py1) {
643 : 106 : setpixel_checked(self, x + px1, y + py1, col, 1);
644 : : } else {
645 : : // Even though this is a hline and would be faster to
646 : : // use fill_rect, use line() because it handles x2 <
647 : : // x1.
648 : 104 : line(self, x + px1, y + py1, x + px2, y + py2, col);
649 : : }
650 : : }
651 : :
652 : 6112 : px1 = px2;
653 : 6112 : py1 = py2;
654 [ + + ]: 6112 : } while (i >= 0);
655 : :
656 [ + + ]: 836 : if (!n_nodes) {
657 : 36 : continue;
658 : : }
659 : :
660 : : // Sort the nodes left-to-right (bubble-sort for code size).
661 : : i = 0;
662 [ + + ]: 5242 : while (i < n_nodes - 1) {
663 [ + + ]: 4442 : if (nodes[i] > nodes[i + 1]) {
664 : 1510 : mp_int_t swap = nodes[i];
665 : 1510 : nodes[i] = nodes[i + 1];
666 : 1510 : nodes[i + 1] = swap;
667 [ + + ]: 1510 : if (i) {
668 : 1068 : i--;
669 : : }
670 : : } else {
671 : : i++;
672 : : }
673 : : }
674 : :
675 : : // Fill between each pair of nodes.
676 [ + + ]: 2132 : for (i = 0; i < n_nodes; i += 2) {
677 : 1332 : fill_rect(self, x + nodes[i], y + row, (nodes[i + 1] - nodes[i]) + 1, 1, col);
678 : : }
679 : : }
680 : : } else {
681 : : // Outline only.
682 : 42 : mp_int_t px1 = poly_int(&bufinfo, 0);
683 : 42 : mp_int_t py1 = poly_int(&bufinfo, 1);
684 : 42 : int i = n_poly * 2 - 1;
685 : 320 : do {
686 : 320 : mp_int_t py2 = poly_int(&bufinfo, i--);
687 : 320 : mp_int_t px2 = poly_int(&bufinfo, i--);
688 : 320 : line(self, x + px1, y + py1, x + px2, y + py2, col);
689 : 320 : px1 = px2;
690 : 320 : py1 = py2;
691 [ + + ]: 320 : } while (i >= 0);
692 : : }
693 : :
694 : : return mp_const_none;
695 : : }
696 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_poly_obj, 5, 6, framebuf_poly);
697 : :
698 : : #endif // MICROPY_PY_ARRAY
699 : :
700 : 20 : static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) {
701 : 20 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
702 : 20 : mp_obj_t source_in = mp_obj_cast_to_native_base(args_in[1], MP_OBJ_FROM_PTR(&mp_type_framebuf));
703 [ + + ]: 20 : if (source_in == MP_OBJ_NULL) {
704 : 4 : mp_raise_TypeError(NULL);
705 : : }
706 : 16 : mp_obj_framebuf_t *source = MP_OBJ_TO_PTR(source_in);
707 : :
708 : 16 : mp_int_t x = mp_obj_get_int(args_in[2]);
709 : 16 : mp_int_t y = mp_obj_get_int(args_in[3]);
710 : 16 : mp_int_t key = -1;
711 [ + + ]: 16 : if (n_args > 4) {
712 : 14 : key = mp_obj_get_int(args_in[4]);
713 : : }
714 : 14 : mp_obj_framebuf_t *palette = NULL;
715 [ + + + - ]: 14 : if (n_args > 5 && args_in[5] != mp_const_none) {
716 : 2 : palette = MP_OBJ_TO_PTR(mp_obj_cast_to_native_base(args_in[5], MP_OBJ_FROM_PTR(&mp_type_framebuf)));
717 : : }
718 : :
719 : 16 : if (
720 [ + + ]: 16 : (x >= self->width) ||
721 [ + - ]: 12 : (y >= self->height) ||
722 [ + - ]: 12 : (-x >= source->width) ||
723 [ + - ]: 12 : (-y >= source->height)
724 : : ) {
725 : : // Out of bounds, no-op.
726 : : return mp_const_none;
727 : : }
728 : :
729 : : // Clip.
730 : 12 : int x0 = MAX(0, x);
731 : 12 : int y0 = MAX(0, y);
732 : 12 : int x1 = MAX(0, -x);
733 : 12 : int y1 = MAX(0, -y);
734 : 12 : int x0end = MIN(self->width, x + source->width);
735 : 12 : int y0end = MIN(self->height, y + source->height);
736 : :
737 [ + + ]: 50 : for (; y0 < y0end; ++y0) {
738 : : int cx1 = x1;
739 [ + + ]: 204 : for (int cx0 = x0; cx0 < x0end; ++cx0) {
740 : 166 : uint32_t col = getpixel(source, cx1, y1);
741 [ + + ]: 166 : if (palette) {
742 : 128 : col = getpixel(palette, col, 0);
743 : : }
744 [ + + ]: 166 : if (col != (uint32_t)key) {
745 : 156 : setpixel(self, cx0, y0, col);
746 : : }
747 : 166 : ++cx1;
748 : : }
749 : 38 : ++y1;
750 : : }
751 : : return mp_const_none;
752 : : }
753 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_obj, 4, 6, framebuf_blit);
754 : :
755 : 52 : static mp_obj_t framebuf_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ystep_in) {
756 : 52 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in);
757 : 52 : mp_int_t xstep = mp_obj_get_int(xstep_in);
758 : 52 : mp_int_t ystep = mp_obj_get_int(ystep_in);
759 : 52 : int sx, y, xend, yend, dx, dy;
760 [ + + ]: 52 : if (xstep < 0) {
761 : 12 : sx = 0;
762 : 12 : xend = self->width + xstep;
763 [ + - ]: 12 : if (xend <= 0) {
764 : : return mp_const_none;
765 : : }
766 : : dx = 1;
767 : : } else {
768 : 40 : sx = self->width - 1;
769 : 40 : xend = xstep - 1;
770 [ + + ]: 40 : if (xend >= sx) {
771 : : return mp_const_none;
772 : : }
773 : : dx = -1;
774 : : }
775 [ + + ]: 48 : if (ystep < 0) {
776 : 14 : y = 0;
777 : 14 : yend = self->height + ystep;
778 [ + + ]: 14 : if (yend <= 0) {
779 : : return mp_const_none;
780 : : }
781 : 46 : dy = 1;
782 : : } else {
783 : 34 : y = self->height - 1;
784 : 34 : yend = ystep - 1;
785 [ + - ]: 34 : if (yend >= y) {
786 : : return mp_const_none;
787 : : }
788 : : dy = -1;
789 : : }
790 [ + + ]: 572 : for (; y != yend; y += dy) {
791 [ + + ]: 2812 : for (int x = sx; x != xend; x += dx) {
792 : 2286 : setpixel(self, x, y, getpixel(self, x - xstep, y - ystep));
793 : : }
794 : : }
795 : : return mp_const_none;
796 : : }
797 : : static MP_DEFINE_CONST_FUN_OBJ_3(framebuf_scroll_obj, framebuf_scroll);
798 : :
799 : 18 : static mp_obj_t framebuf_text(size_t n_args, const mp_obj_t *args_in) {
800 : : // extract arguments
801 : 18 : mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
802 : 18 : const char *str = mp_obj_str_get_str(args_in[1]);
803 : 18 : mp_int_t x0 = mp_obj_get_int(args_in[2]);
804 : 18 : mp_int_t y0 = mp_obj_get_int(args_in[3]);
805 : 18 : mp_int_t col = 1;
806 [ + + ]: 18 : if (n_args >= 5) {
807 : 12 : col = mp_obj_get_int(args_in[4]);
808 : : }
809 : :
810 : : // loop over chars
811 [ + + ]: 84 : for (; *str; ++str) {
812 : : // get char and make sure its in range of font
813 : 66 : int chr = *(uint8_t *)str;
814 [ + + ]: 66 : if (chr < 32 || chr > 127) {
815 : 6 : chr = 127;
816 : : }
817 : : // get char data
818 : 66 : const uint8_t *chr_data = &font_petme128_8x8[(chr - 32) * 8];
819 : : // loop over char data
820 [ + + ]: 594 : for (int j = 0; j < 8; j++, x0++) {
821 [ + - + + ]: 528 : if (0 <= x0 && x0 < self->width) { // clip x
822 : 90 : uint vline_data = chr_data[j]; // each byte is a column of 8 pixels, LSB at top
823 [ + + ]: 558 : for (int y = y0; vline_data; vline_data >>= 1, y++) { // scan over vertical column
824 [ + + ]: 468 : if (vline_data & 1) { // only draw if pixel set
825 [ + - + - ]: 312 : if (0 <= y && y < self->height) { // clip y
826 : 312 : setpixel(self, x0, y, col);
827 : : }
828 : : }
829 : : }
830 : : }
831 : : }
832 : : }
833 : 18 : return mp_const_none;
834 : : }
835 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_text_obj, 4, 5, framebuf_text);
836 : :
837 : : #if !MICROPY_ENABLE_DYNRUNTIME
838 : : static const mp_rom_map_elem_t framebuf_locals_dict_table[] = {
839 : : { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&framebuf_fill_obj) },
840 : : { MP_ROM_QSTR(MP_QSTR_fill_rect), MP_ROM_PTR(&framebuf_fill_rect_obj) },
841 : : { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&framebuf_pixel_obj) },
842 : : { MP_ROM_QSTR(MP_QSTR_hline), MP_ROM_PTR(&framebuf_hline_obj) },
843 : : { MP_ROM_QSTR(MP_QSTR_vline), MP_ROM_PTR(&framebuf_vline_obj) },
844 : : { MP_ROM_QSTR(MP_QSTR_rect), MP_ROM_PTR(&framebuf_rect_obj) },
845 : : { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&framebuf_line_obj) },
846 : : { MP_ROM_QSTR(MP_QSTR_ellipse), MP_ROM_PTR(&framebuf_ellipse_obj) },
847 : : #if MICROPY_PY_ARRAY
848 : : { MP_ROM_QSTR(MP_QSTR_poly), MP_ROM_PTR(&framebuf_poly_obj) },
849 : : #endif
850 : : { MP_ROM_QSTR(MP_QSTR_blit), MP_ROM_PTR(&framebuf_blit_obj) },
851 : : { MP_ROM_QSTR(MP_QSTR_scroll), MP_ROM_PTR(&framebuf_scroll_obj) },
852 : : { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&framebuf_text_obj) },
853 : : };
854 : : static MP_DEFINE_CONST_DICT(framebuf_locals_dict, framebuf_locals_dict_table);
855 : :
856 : : static MP_DEFINE_CONST_OBJ_TYPE(
857 : : mp_type_framebuf,
858 : : MP_QSTR_FrameBuffer,
859 : : MP_TYPE_FLAG_NONE,
860 : : make_new, framebuf_make_new,
861 : : buffer, framebuf_get_buffer,
862 : : locals_dict, &framebuf_locals_dict
863 : : );
864 : : #endif
865 : :
866 : : #if !MICROPY_ENABLE_DYNRUNTIME
867 : : // This factory function is provided for backwards compatibility with the old
868 : : // FrameBuffer1 class which did not support a format argument.
869 : 4 : static mp_obj_t legacy_framebuffer1(size_t n_args, const mp_obj_t *args_in) {
870 [ + + ]: 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] };
871 : 4 : return framebuf_make_new(&mp_type_framebuf, 5, 0, args);
872 : : }
873 : : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(legacy_framebuffer1_obj, 3, 4, legacy_framebuffer1);
874 : :
875 : : static const mp_rom_map_elem_t framebuf_module_globals_table[] = {
876 : : { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_framebuf) },
877 : : { MP_ROM_QSTR(MP_QSTR_FrameBuffer), MP_ROM_PTR(&mp_type_framebuf) },
878 : : { MP_ROM_QSTR(MP_QSTR_FrameBuffer1), MP_ROM_PTR(&legacy_framebuffer1_obj) },
879 : : { MP_ROM_QSTR(MP_QSTR_MVLSB), MP_ROM_INT(FRAMEBUF_MVLSB) },
880 : : { MP_ROM_QSTR(MP_QSTR_MONO_VLSB), MP_ROM_INT(FRAMEBUF_MVLSB) },
881 : : { MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(FRAMEBUF_RGB565) },
882 : : { MP_ROM_QSTR(MP_QSTR_GS2_HMSB), MP_ROM_INT(FRAMEBUF_GS2_HMSB) },
883 : : { MP_ROM_QSTR(MP_QSTR_GS4_HMSB), MP_ROM_INT(FRAMEBUF_GS4_HMSB) },
884 : : { MP_ROM_QSTR(MP_QSTR_GS8), MP_ROM_INT(FRAMEBUF_GS8) },
885 : : { MP_ROM_QSTR(MP_QSTR_MONO_HLSB), MP_ROM_INT(FRAMEBUF_MHLSB) },
886 : : { MP_ROM_QSTR(MP_QSTR_MONO_HMSB), MP_ROM_INT(FRAMEBUF_MHMSB) },
887 : : };
888 : :
889 : : static MP_DEFINE_CONST_DICT(framebuf_module_globals, framebuf_module_globals_table);
890 : :
891 : : const mp_obj_module_t mp_module_framebuf = {
892 : : .base = { &mp_type_module },
893 : : .globals = (mp_obj_dict_t *)&framebuf_module_globals,
894 : : };
895 : :
896 : : MP_REGISTER_MODULE(MP_QSTR_framebuf, mp_module_framebuf);
897 : : #endif
898 : :
899 : : #endif // MICROPY_PY_FRAMEBUF
|