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
32#if MICROPY_PY_FRAMEBUF
33
34#include "ports/stm32/font_petme128_8x8.h"
35
36typedef struct _mp_obj_framebuf_t {
37 mp_obj_base_t base;
38 mp_obj_t buf_obj; // need to store this to prevent GC from reclaiming buf
39 void *buf;
40 uint16_t width, height, stride;
41 uint8_t format;
42} mp_obj_framebuf_t;
43
44#if !MICROPY_ENABLE_DYNRUNTIME
45STATIC const mp_obj_type_t mp_type_framebuf;
46#endif
47
48typedef void (*setpixel_t)(const mp_obj_framebuf_t *, unsigned int, unsigned int, uint32_t);
49typedef uint32_t (*getpixel_t)(const mp_obj_framebuf_t *, unsigned int, unsigned int);
50typedef void (*fill_rect_t)(const mp_obj_framebuf_t *, unsigned int, unsigned int, unsigned int, unsigned int, uint32_t);
51
52typedef struct _mp_framebuf_p_t {
53 setpixel_t setpixel;
54 getpixel_t getpixel;
55 fill_rect_t fill_rect;
56} mp_framebuf_p_t;
57
58// constants for formats
59#define FRAMEBUF_MVLSB (0)
60#define FRAMEBUF_RGB565 (1)
61#define FRAMEBUF_GS2_HMSB (5)
62#define FRAMEBUF_GS4_HMSB (2)
63#define FRAMEBUF_GS8 (6)
64#define FRAMEBUF_MHLSB (3)
65#define FRAMEBUF_MHMSB (4)
66
67// Functions for MHLSB and MHMSB
68
69STATIC void mono_horiz_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) {
70 size_t index = (x + y * fb->stride) >> 3;
71 unsigned int offset = fb->format == FRAMEBUF_MHMSB ? x & 0x07 : 7 - (x & 0x07);
72 ((uint8_t *)fb->buf)[index] = (((uint8_t *)fb->buf)[index] & ~(0x01 << offset)) | ((col != 0) << offset);
73}
74
75STATIC uint32_t mono_horiz_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) {
76 size_t index = (x + y * fb->stride) >> 3;
77 unsigned int offset = fb->format == FRAMEBUF_MHMSB ? x & 0x07 : 7 - (x & 0x07);
78 return (((uint8_t *)fb->buf)[index] >> (offset)) & 0x01;
79}
80
81STATIC 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) {
82 unsigned int reverse = fb->format == FRAMEBUF_MHMSB;
83 unsigned int advance = fb->stride >> 3;
84 while (w--) {
85 uint8_t *b = &((uint8_t *)fb->buf)[(x >> 3) + y * advance];
86 unsigned int offset = reverse ? x & 7 : 7 - (x & 7);
87 for (unsigned int hh = h; hh; --hh) {
88 *b = (*b & ~(0x01 << offset)) | ((col != 0) << offset);
89 b += advance;
90 }
91 ++x;
92 }
93}
94
95// Functions for MVLSB format
96
97STATIC void mvlsb_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) {
98 size_t index = (y >> 3) * fb->stride + x;
99 uint8_t offset = y & 0x07;
100 ((uint8_t *)fb->buf)[index] = (((uint8_t *)fb->buf)[index] & ~(0x01 << offset)) | ((col != 0) << offset);
101}
102
103STATIC uint32_t mvlsb_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) {
104 return (((uint8_t *)fb->buf)[(y >> 3) * fb->stride + x] >> (y & 0x07)) & 0x01;
105}
106
107STATIC 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) {
108 while (h--) {
109 uint8_t *b = &((uint8_t *)fb->buf)[(y >> 3) * fb->stride + x];
110 uint8_t offset = y & 0x07;
111 for (unsigned int ww = w; ww; --ww) {
112 *b = (*b & ~(0x01 << offset)) | ((col != 0) << offset);
113 ++b;
114 }
115 ++y;
116 }
117}
118
119// Functions for RGB565 format
120
121STATIC void rgb565_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) {
122 ((uint16_t *)fb->buf)[x + y * fb->stride] = col;
123}
124
125STATIC uint32_t rgb565_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) {
126 return ((uint16_t *)fb->buf)[x + y * fb->stride];
127}
128
129STATIC 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) {
130 uint16_t *b = &((uint16_t *)fb->buf)[x + y * fb->stride];
131 while (h--) {
132 for (unsigned int ww = w; ww; --ww) {
133 *b++ = col;
134 }
135 b += fb->stride - w;
136 }
137}
138
139// Functions for GS2_HMSB format
140
141STATIC void gs2_hmsb_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) {
142 uint8_t *pixel = &((uint8_t *)fb->buf)[(x + y * fb->stride) >> 2];
143 uint8_t shift = (x & 0x3) << 1;
144 uint8_t mask = 0x3 << shift;
145 uint8_t color = (col & 0x3) << shift;
146 *pixel = color | (*pixel & (~mask));
147}
148
149STATIC uint32_t gs2_hmsb_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) {
150 uint8_t pixel = ((uint8_t *)fb->buf)[(x + y * fb->stride) >> 2];
151 uint8_t shift = (x & 0x3) << 1;
152 return (pixel >> shift) & 0x3;
153}
154
155STATIC 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) {
156 for (unsigned int xx = x; xx < x + w; xx++) {
157 for (unsigned int yy = y; yy < y + h; yy++) {
158 gs2_hmsb_setpixel(fb, xx, yy, col);
159 }
160 }
161}
162
163// Functions for GS4_HMSB format
164
165STATIC void gs4_hmsb_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) {
166 uint8_t *pixel = &((uint8_t *)fb->buf)[(x + y * fb->stride) >> 1];
167
168 if (x % 2) {
169 *pixel = ((uint8_t)col & 0x0f) | (*pixel & 0xf0);
170 } else {
171 *pixel = ((uint8_t)col << 4) | (*pixel & 0x0f);
172 }
173}
174
175STATIC uint32_t gs4_hmsb_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) {
176 if (x % 2) {
177 return ((uint8_t *)fb->buf)[(x + y * fb->stride) >> 1] & 0x0f;
178 }
179
180 return ((uint8_t *)fb->buf)[(x + y * fb->stride) >> 1] >> 4;
181}
182
183STATIC 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) {
184 col &= 0x0f;
185 uint8_t *pixel_pair = &((uint8_t *)fb->buf)[(x + y * fb->stride) >> 1];
186 uint8_t col_shifted_left = col << 4;
187 uint8_t col_pixel_pair = col_shifted_left | col;
188 unsigned int pixel_count_till_next_line = (fb->stride - w) >> 1;
189 bool odd_x = (x % 2 == 1);
190
191 while (h--) {
192 unsigned int ww = w;
193
194 if (odd_x && ww > 0) {
195 *pixel_pair = (*pixel_pair & 0xf0) | col;
196 pixel_pair++;
197 ww--;
198 }
199
200 memset(pixel_pair, col_pixel_pair, ww >> 1);
201 pixel_pair += ww >> 1;
202
203 if (ww % 2) {
204 *pixel_pair = col_shifted_left | (*pixel_pair & 0x0f);
205 if (!odd_x) {
206 pixel_pair++;
207 }
208 }
209
210 pixel_pair += pixel_count_till_next_line;
211 }
212}
213
214// Functions for GS8 format
215
216STATIC void gs8_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) {
217 uint8_t *pixel = &((uint8_t *)fb->buf)[(x + y * fb->stride)];
218 *pixel = col & 0xff;
219}
220
221STATIC uint32_t gs8_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) {
222 return ((uint8_t *)fb->buf)[(x + y * fb->stride)];
223}
224
225STATIC 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) {
226 uint8_t *pixel = &((uint8_t *)fb->buf)[(x + y * fb->stride)];
227 while (h--) {
228 memset(pixel, col, w);
229 pixel += fb->stride;
230 }
231}
232
233STATIC mp_framebuf_p_t formats[] = {
234 [FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect},
235 [FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect},
236 [FRAMEBUF_GS2_HMSB] = {gs2_hmsb_setpixel, gs2_hmsb_getpixel, gs2_hmsb_fill_rect},
237 [FRAMEBUF_GS4_HMSB] = {gs4_hmsb_setpixel, gs4_hmsb_getpixel, gs4_hmsb_fill_rect},
238 [FRAMEBUF_GS8] = {gs8_setpixel, gs8_getpixel, gs8_fill_rect},
239 [FRAMEBUF_MHLSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect},
240 [FRAMEBUF_MHMSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect},
241};
242
243static inline void setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) {
244 formats[fb->format].setpixel(fb, x, y, col);
245}
246
247static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) {
248 return formats[fb->format].getpixel(fb, x, y);
249}
250
251STATIC void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) {
252 if (h < 1 || w < 1 || x + w <= 0 || y + h <= 0 || y >= fb->height || x >= fb->width) {
253 // No operation needed.
254 return;
255 }
256
257 // clip to the framebuffer
258 int xend = MIN(fb->width, x + w);
259 int yend = MIN(fb->height, y + h);
260 x = MAX(x, 0);
261 y = MAX(y, 0);
262
263 formats[fb->format].fill_rect(fb, x, y, xend - x, yend - y, col);
264}
265
266STATIC 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) {
267 mp_arg_check_num(n_args, n_kw, 4, 5, false);
268
269 mp_obj_framebuf_t *o = m_new_obj(mp_obj_framebuf_t);
270 o->base.type = type;
271 o->buf_obj = args[0];
272
273 mp_buffer_info_t bufinfo;
274 mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_WRITE);
275 o->buf = bufinfo.buf;
276
277 o->width = mp_obj_get_int(args[1]);
278 o->height = mp_obj_get_int(args[2]);
279 o->format = mp_obj_get_int(args[3]);
280 if (n_args >= 5) {
281 o->stride = mp_obj_get_int(args[4]);
282 } else {
283 o->stride = o->width;
284 }
285
286 switch (o->format) {
287 case FRAMEBUF_MVLSB:
288 case FRAMEBUF_RGB565:
289 break;
290 case FRAMEBUF_MHLSB:
291 case FRAMEBUF_MHMSB:
292 o->stride = (o->stride + 7) & ~7;
293 break;
294 case FRAMEBUF_GS2_HMSB:
295 o->stride = (o->stride + 3) & ~3;
296 break;
297 case FRAMEBUF_GS4_HMSB:
298 o->stride = (o->stride + 1) & ~1;
299 break;
300 case FRAMEBUF_GS8:
301 break;
302 default:
303 mp_raise_ValueError(MP_ERROR_TEXT("invalid format"));
304 }
305
306 return MP_OBJ_FROM_PTR(o);
307}
308
309STATIC mp_int_t framebuf_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
310 (void)flags;
311 mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in);
312 bufinfo->buf = self->buf;
313 bufinfo->len = self->stride * self->height * (self->format == FRAMEBUF_RGB565 ? 2 : 1);
314 bufinfo->typecode = 'B'; // view framebuf as bytes
315 return 0;
316}
317
318STATIC mp_obj_t framebuf_fill(mp_obj_t self_in, mp_obj_t col_in) {
319 mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in);
320 mp_int_t col = mp_obj_get_int(col_in);
321 formats[self->format].fill_rect(self, 0, 0, self->width, self->height, col);
322 return mp_const_none;
323}
324STATIC MP_DEFINE_CONST_FUN_OBJ_2(framebuf_fill_obj, framebuf_fill);
325
326STATIC mp_obj_t framebuf_fill_rect(size_t n_args, const mp_obj_t *args) {
327 (void)n_args;
328
329 mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
330 mp_int_t x = mp_obj_get_int(args[1]);
331 mp_int_t y = mp_obj_get_int(args[2]);
332 mp_int_t width = mp_obj_get_int(args[3]);
333 mp_int_t height = mp_obj_get_int(args[4]);
334 mp_int_t col = mp_obj_get_int(args[5]);
335
336 fill_rect(self, x, y, width, height, col);
337
338 return mp_const_none;
339}
340STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_fill_rect_obj, 6, 6, framebuf_fill_rect);
341
342STATIC mp_obj_t framebuf_pixel(size_t n_args, const mp_obj_t *args) {
343 mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
344 mp_int_t x = mp_obj_get_int(args[1]);
345 mp_int_t y = mp_obj_get_int(args[2]);
346 if (0 <= x && x < self->width && 0 <= y && y < self->height) {
347 if (n_args == 3) {
348 // get
349 return MP_OBJ_NEW_SMALL_INT(getpixel(self, x, y));
350 } else {
351 // set
352 setpixel(self, x, y, mp_obj_get_int(args[3]));
353 }
354 }
355 return mp_const_none;
356}
357STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_pixel_obj, 3, 4, framebuf_pixel);
358
359STATIC mp_obj_t framebuf_hline(size_t n_args, const mp_obj_t *args) {
360 (void)n_args;
361
362 mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
363 mp_int_t x = mp_obj_get_int(args[1]);
364 mp_int_t y = mp_obj_get_int(args[2]);
365 mp_int_t w = mp_obj_get_int(args[3]);
366 mp_int_t col = mp_obj_get_int(args[4]);
367
368 fill_rect(self, x, y, w, 1, col);
369
370 return mp_const_none;
371}
372STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_hline_obj, 5, 5, framebuf_hline);
373
374STATIC mp_obj_t framebuf_vline(size_t n_args, const mp_obj_t *args) {
375 (void)n_args;
376
377 mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
378 mp_int_t x = mp_obj_get_int(args[1]);
379 mp_int_t y = mp_obj_get_int(args[2]);
380 mp_int_t h = mp_obj_get_int(args[3]);
381 mp_int_t col = mp_obj_get_int(args[4]);
382
383 fill_rect(self, x, y, 1, h, col);
384
385 return mp_const_none;
386}
387STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_vline_obj, 5, 5, framebuf_vline);
388
389STATIC mp_obj_t framebuf_rect(size_t n_args, const mp_obj_t *args) {
390 (void)n_args;
391
392 mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
393 mp_int_t x = mp_obj_get_int(args[1]);
394 mp_int_t y = mp_obj_get_int(args[2]);
395 mp_int_t w = mp_obj_get_int(args[3]);
396 mp_int_t h = mp_obj_get_int(args[4]);
397 mp_int_t col = mp_obj_get_int(args[5]);
398
399 fill_rect(self, x, y, w, 1, col);
400 fill_rect(self, x, y + h - 1, w, 1, col);
401 fill_rect(self, x, y, 1, h, col);
402 fill_rect(self, x + w - 1, y, 1, h, col);
403
404 return mp_const_none;
405}
406STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_rect_obj, 6, 6, framebuf_rect);
407
408STATIC mp_obj_t framebuf_line(size_t n_args, const mp_obj_t *args) {
409 (void)n_args;
410
411 mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
412 mp_int_t x1 = mp_obj_get_int(args[1]);
413 mp_int_t y1 = mp_obj_get_int(args[2]);
414 mp_int_t x2 = mp_obj_get_int(args[3]);
415 mp_int_t y2 = mp_obj_get_int(args[4]);
416 mp_int_t col = mp_obj_get_int(args[5]);
417
418 mp_int_t dx = x2 - x1;
419 mp_int_t sx;
420 if (dx > 0) {
421 sx = 1;
422 } else {
423 dx = -dx;
424 sx = -1;
425 }
426
427 mp_int_t dy = y2 - y1;
428 mp_int_t sy;
429 if (dy > 0) {
430 sy = 1;
431 } else {
432 dy = -dy;
433 sy = -1;
434 }
435
436 bool steep;
437 if (dy > dx) {
438 mp_int_t temp;
439 temp = x1;
440 x1 = y1;
441 y1 = temp;
442 temp = dx;
443 dx = dy;
444 dy = temp;
445 temp = sx;
446 sx = sy;
447 sy = temp;
448 steep = true;
449 } else {
450 steep = false;
451 }
452
453 mp_int_t e = 2 * dy - dx;
454 for (mp_int_t i = 0; i < dx; ++i) {
455 if (steep) {
456 if (0 <= y1 && y1 < self->width && 0 <= x1 && x1 < self->height) {
457 setpixel(self, y1, x1, col);
458 }
459 } else {
460 if (0 <= x1 && x1 < self->width && 0 <= y1 && y1 < self->height) {
461 setpixel(self, x1, y1, col);
462 }
463 }
464 while (e >= 0) {
465 y1 += sy;
466 e -= 2 * dx;
467 }
468 x1 += sx;
469 e += 2 * dy;
470 }
471
472 if (0 <= x2 && x2 < self->width && 0 <= y2 && y2 < self->height) {
473 setpixel(self, x2, y2, col);
474 }
475
476 return mp_const_none;
477}
478STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_line_obj, 6, 6, framebuf_line);
479
480STATIC mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args) {
481 mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
482 mp_obj_t source_in = mp_obj_cast_to_native_base(args[1], MP_OBJ_FROM_PTR(&mp_type_framebuf));
483 if (source_in == MP_OBJ_NULL) {
484 mp_raise_TypeError(NULL);
485 }
486 mp_obj_framebuf_t *source = MP_OBJ_TO_PTR(source_in);
487
488 mp_int_t x = mp_obj_get_int(args[2]);
489 mp_int_t y = mp_obj_get_int(args[3]);
490 mp_int_t key = -1;
491 if (n_args > 4) {
492 key = mp_obj_get_int(args[4]);
493 }
494
495 if (
496 (x >= self->width) ||
497 (y >= self->height) ||
498 (-x >= source->width) ||
499 (-y >= source->height)
500 ) {
501 // Out of bounds, no-op.
502 return mp_const_none;
503 }
504
505 // Clip.
506 int x0 = MAX(0, x);
507 int y0 = MAX(0, y);
508 int x1 = MAX(0, -x);
509 int y1 = MAX(0, -y);
510 int x0end = MIN(self->width, x + source->width);
511 int y0end = MIN(self->height, y + source->height);
512
513 for (; y0 < y0end; ++y0) {
514 int cx1 = x1;
515 for (int cx0 = x0; cx0 < x0end; ++cx0) {
516 uint32_t col = getpixel(source, cx1, y1);
517 if (col != (uint32_t)key) {
518 setpixel(self, cx0, y0, col);
519 }
520 ++cx1;
521 }
522 ++y1;
523 }
524 return mp_const_none;
525}
526STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_obj, 4, 5, framebuf_blit);
527
528STATIC mp_obj_t framebuf_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ystep_in) {
529 mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in);
530 mp_int_t xstep = mp_obj_get_int(xstep_in);
531 mp_int_t ystep = mp_obj_get_int(ystep_in);
532 int sx, y, xend, yend, dx, dy;
533 if (xstep < 0) {
534 sx = 0;
535 xend = self->width + xstep;
536 dx = 1;
537 } else {
538 sx = self->width - 1;
539 xend = xstep - 1;
540 dx = -1;
541 }
542 if (ystep < 0) {
543 y = 0;
544 yend = self->height + ystep;
545 dy = 1;
546 } else {
547 y = self->height - 1;
548 yend = ystep - 1;
549 dy = -1;
550 }
551 for (; y != yend; y += dy) {
552 for (int x = sx; x != xend; x += dx) {
553 setpixel(self, x, y, getpixel(self, x - xstep, y - ystep));
554 }
555 }
556 return mp_const_none;
557}
558STATIC MP_DEFINE_CONST_FUN_OBJ_3(framebuf_scroll_obj, framebuf_scroll);
559
560STATIC mp_obj_t framebuf_text(size_t n_args, const mp_obj_t *args) {
561 // extract arguments
562 mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
563 const char *str = mp_obj_str_get_str(args[1]);
564 mp_int_t x0 = mp_obj_get_int(args[2]);
565 mp_int_t y0 = mp_obj_get_int(args[3]);
566 mp_int_t col = 1;
567 if (n_args >= 5) {
568 col = mp_obj_get_int(args[4]);
569 }
570
571 // loop over chars
572 for (; *str; ++str) {
573 // get char and make sure its in range of font
574 int chr = *(uint8_t *)str;
575 if (chr < 32 || chr > 127) {
576 chr = 127;
577 }
578 // get char data
579 const uint8_t *chr_data = &font_petme128_8x8[(chr - 32) * 8];
580 // loop over char data
581 for (int j = 0; j < 8; j++, x0++) {
582 if (0 <= x0 && x0 < self->width) { // clip x
583 uint vline_data = chr_data[j]; // each byte is a column of 8 pixels, LSB at top
584 for (int y = y0; vline_data; vline_data >>= 1, y++) { // scan over vertical column
585 if (vline_data & 1) { // only draw if pixel set
586 if (0 <= y && y < self->height) { // clip y
587 setpixel(self, x0, y, col);
588 }
589 }
590 }
591 }
592 }
593 }
594 return mp_const_none;
595}
596STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_text_obj, 4, 5, framebuf_text);
597
598#if !MICROPY_ENABLE_DYNRUNTIME
599STATIC const mp_rom_map_elem_t framebuf_locals_dict_table[] = {
600 { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&framebuf_fill_obj) },
601 { MP_ROM_QSTR(MP_QSTR_fill_rect), MP_ROM_PTR(&framebuf_fill_rect_obj) },
602 { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&framebuf_pixel_obj) },
603 { MP_ROM_QSTR(MP_QSTR_hline), MP_ROM_PTR(&framebuf_hline_obj) },
604 { MP_ROM_QSTR(MP_QSTR_vline), MP_ROM_PTR(&framebuf_vline_obj) },
605 { MP_ROM_QSTR(MP_QSTR_rect), MP_ROM_PTR(&framebuf_rect_obj) },
606 { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&framebuf_line_obj) },
607 { MP_ROM_QSTR(MP_QSTR_blit), MP_ROM_PTR(&framebuf_blit_obj) },
608 { MP_ROM_QSTR(MP_QSTR_scroll), MP_ROM_PTR(&framebuf_scroll_obj) },
609 { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&framebuf_text_obj) },
610};
611STATIC MP_DEFINE_CONST_DICT(framebuf_locals_dict, framebuf_locals_dict_table);
612
613STATIC const mp_obj_type_t mp_type_framebuf = {
614 { &mp_type_type },
615 .name = MP_QSTR_FrameBuffer,
616 .make_new = framebuf_make_new,
617 .buffer_p = { .get_buffer = framebuf_get_buffer },
618 .locals_dict = (mp_obj_dict_t *)&framebuf_locals_dict,
619};
620#endif
621
622// this factory function is provided for backwards compatibility with old FrameBuffer1 class
623STATIC mp_obj_t legacy_framebuffer1(size_t n_args, const mp_obj_t *args) {
624 mp_obj_framebuf_t *o = m_new_obj(mp_obj_framebuf_t);
625 o->base.type = &mp_type_framebuf;
626
627 mp_buffer_info_t bufinfo;
628 mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_WRITE);
629 o->buf = bufinfo.buf;
630
631 o->width = mp_obj_get_int(args[1]);
632 o->height = mp_obj_get_int(args[2]);
633 o->format = FRAMEBUF_MVLSB;
634 if (n_args >= 4) {
635 o->stride = mp_obj_get_int(args[3]);
636 } else {
637 o->stride = o->width;
638 }
639
640 return MP_OBJ_FROM_PTR(o);
641}
642STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(legacy_framebuffer1_obj, 3, 4, legacy_framebuffer1);
643
644#if !MICROPY_ENABLE_DYNRUNTIME
645STATIC const mp_rom_map_elem_t framebuf_module_globals_table[] = {
646 { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_framebuf) },
647 { MP_ROM_QSTR(MP_QSTR_FrameBuffer), MP_ROM_PTR(&mp_type_framebuf) },
648 { MP_ROM_QSTR(MP_QSTR_FrameBuffer1), MP_ROM_PTR(&legacy_framebuffer1_obj) },
649 { MP_ROM_QSTR(MP_QSTR_MVLSB), MP_ROM_INT(FRAMEBUF_MVLSB) },
650 { MP_ROM_QSTR(MP_QSTR_MONO_VLSB), MP_ROM_INT(FRAMEBUF_MVLSB) },
651 { MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(FRAMEBUF_RGB565) },
652 { MP_ROM_QSTR(MP_QSTR_GS2_HMSB), MP_ROM_INT(FRAMEBUF_GS2_HMSB) },
653 { MP_ROM_QSTR(MP_QSTR_GS4_HMSB), MP_ROM_INT(FRAMEBUF_GS4_HMSB) },
654 { MP_ROM_QSTR(MP_QSTR_GS8), MP_ROM_INT(FRAMEBUF_GS8) },
655 { MP_ROM_QSTR(MP_QSTR_MONO_HLSB), MP_ROM_INT(FRAMEBUF_MHLSB) },
656 { MP_ROM_QSTR(MP_QSTR_MONO_HMSB), MP_ROM_INT(FRAMEBUF_MHMSB) },
657};
658
659STATIC MP_DEFINE_CONST_DICT(framebuf_module_globals, framebuf_module_globals_table);
660
661const mp_obj_module_t mp_module_framebuf = {
662 .base = { &mp_type_module },
663 .globals = (mp_obj_dict_t *)&framebuf_module_globals,
664};
665#endif
666
667#endif // MICROPY_PY_FRAMEBUF
668