LCOV - code coverage report
Current view: top level - extmod - modframebuf.c (source / functions) Hit Total Coverage
Test: unix_coverage_v1.19.1-724-gfb7d21153.info Lines: 459 460 99.8 %
Date: 2022-12-01 09:37:31 Functions: 39 39 100.0 %
Branches: 216 231 93.5 %

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

Generated by: LCOV version 1.15-5-g462f71d