LCOV - code coverage report
Current view: top level - extmod - modframebuf.c (source / functions) Hit Total Coverage
Test: unix_coverage_v1.24.0-161-gc73204128.info Lines: 471 471 100.0 %
Date: 2024-12-23 07:29:41 Functions: 39 39 100.0 %
Branches: 230 247 93.1 %

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

Generated by: LCOV version 1.15-5-g462f71d