LCOV - code coverage report
Current view: top level - extmod - modframebuf.c (source / functions) Hit Total Coverage
Test: unix_coverage_v1.22.0-335-g9c7f0659e.info Lines: 463 463 100.0 %
Date: 2024-04-24 08:31:58 Functions: 39 39 100.0 %
Branches: 227 243 93.4 %

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

Generated by: LCOV version 1.15-5-g462f71d