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 | |
36 | typedef 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 |
45 | STATIC const mp_obj_type_t mp_type_framebuf; |
46 | #endif |
47 | |
48 | typedef void (*setpixel_t)(const mp_obj_framebuf_t *, unsigned int, unsigned int, uint32_t); |
49 | typedef uint32_t (*getpixel_t)(const mp_obj_framebuf_t *, unsigned int, unsigned int); |
50 | typedef void (*fill_rect_t)(const mp_obj_framebuf_t *, unsigned int, unsigned int, unsigned int, unsigned int, uint32_t); |
51 | |
52 | typedef 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 | |
69 | STATIC 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 | |
75 | STATIC 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 | |
81 | 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) { |
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 | |
97 | STATIC 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 | |
103 | STATIC 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 | |
107 | 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) { |
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 | |
121 | STATIC 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 | |
125 | STATIC 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 | |
129 | 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) { |
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 | |
141 | STATIC 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 | |
149 | STATIC 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 | |
155 | 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) { |
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 | |
165 | STATIC 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 | |
175 | STATIC 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 | |
183 | 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) { |
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 | |
216 | STATIC 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 | |
221 | STATIC 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 | |
225 | 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) { |
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 | |
233 | STATIC 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 | |
243 | static 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 | |
247 | static 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 | |
251 | STATIC 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 | |
266 | 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) { |
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 | |
309 | STATIC 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 | |
318 | STATIC 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 | } |
324 | STATIC MP_DEFINE_CONST_FUN_OBJ_2(framebuf_fill_obj, framebuf_fill); |
325 | |
326 | STATIC 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 | } |
340 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_fill_rect_obj, 6, 6, framebuf_fill_rect); |
341 | |
342 | STATIC 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 | } |
357 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_pixel_obj, 3, 4, framebuf_pixel); |
358 | |
359 | STATIC 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 | } |
372 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_hline_obj, 5, 5, framebuf_hline); |
373 | |
374 | STATIC 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 | } |
387 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_vline_obj, 5, 5, framebuf_vline); |
388 | |
389 | STATIC 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 | } |
406 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_rect_obj, 6, 6, framebuf_rect); |
407 | |
408 | STATIC 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 | } |
478 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_line_obj, 6, 6, framebuf_line); |
479 | |
480 | STATIC 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 | } |
526 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_obj, 4, 5, framebuf_blit); |
527 | |
528 | STATIC 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 | } |
558 | STATIC MP_DEFINE_CONST_FUN_OBJ_3(framebuf_scroll_obj, framebuf_scroll); |
559 | |
560 | STATIC 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 | } |
596 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_text_obj, 4, 5, framebuf_text); |
597 | |
598 | #if !MICROPY_ENABLE_DYNRUNTIME |
599 | STATIC 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 | }; |
611 | STATIC MP_DEFINE_CONST_DICT(framebuf_locals_dict, framebuf_locals_dict_table); |
612 | |
613 | STATIC 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 |
623 | STATIC 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 | } |
642 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(legacy_framebuffer1_obj, 3, 4, legacy_framebuffer1); |
643 | |
644 | #if !MICROPY_ENABLE_DYNRUNTIME |
645 | STATIC 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 | |
659 | STATIC MP_DEFINE_CONST_DICT(framebuf_module_globals, framebuf_module_globals_table); |
660 | |
661 | const 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 | |