2 from typing import Optional, Tuple
 
   4 from displayio import Bitmap
 
   5 import circuitpython_typing
 
   8 def fill_region(dest_bitmap: Bitmap, x1: int, y1: int, x2: int, y2: int, value: int):
 
   9     for y in range(y1, y2):
 
  10         for x in range(x1, x2):
 
  11             dest_bitmap[x, y] = value
 
  14 def draw_line(dest_bitmap: Bitmap, x1: int, y1: int, x2: int, y2: int, value: int):
 
  16     sx = 1 if x1 < x2 else -1
 
  18     sy = 1 if y1 < y2 else -1
 
  22         dest_bitmap[x1, y1] = value
 
  23         if x1 == x2 and y1 == y2:
 
  34 def draw_circle(dest_bitmap: Bitmap, x: int, y: int, radius: int, value: int):
 
  35     x = max(0, min(x, dest_bitmap.width - 1))
 
  36     y = max(0, min(y, dest_bitmap.height - 1))
 
  42     # Bresenham's circle algorithm
 
  44         dest_bitmap[xb + x, yb + y] = value
 
  45         dest_bitmap[-xb + x, -yb + y] = value
 
  46         dest_bitmap[-xb + x, yb + y] = value
 
  47         dest_bitmap[xb + x, -yb + y] = value
 
  48         dest_bitmap[yb + x, xb + y] = value
 
  49         dest_bitmap[-yb + x, xb + y] = value
 
  50         dest_bitmap[-yb + x, -xb + y] = value
 
  51         dest_bitmap[yb + x, -xb + y] = value
 
  56             d = d + 4 * (xb - yb) + 10
 
  61 def draw_polygon(dest_bitmap: Bitmap,
 
  62                  xs: circuitpython_typing.ReadableBuffer,
 
  63                  ys: circuitpython_typing.ReadableBuffer,
 
  64                  value: int, close: bool | None = True):
 
  65     if len(xs) != len(ys):
 
  66         raise ValueError("Length of xs and ys must be equal.")
 
  68     for i in range(len(xs) - 1):
 
  69         cur_point = (xs[i], ys[i])
 
  70         next_point = (xs[i + 1], ys[i + 1])
 
  71         print(f"cur: {cur_point}, next: {next_point}")
 
  72         draw_line(dest_bitmap=dest_bitmap,
 
  73                   x1=cur_point[0], y1=cur_point[1],
 
  74                   x2=next_point[0], y2=next_point[1],
 
  78         print(f"close: {(xs[0], ys[0])} - {(xs[-1], ys[-1])}")
 
  79         draw_line(dest_bitmap=dest_bitmap,
 
  85 def blit(dest_bitmap: Bitmap, source_bitmap: Bitmap,
 
  87          x1: int = 0, y1: int = 0,
 
  88          x2: int | None = None, y2: int | None = None,
 
  89          skip_source_index: int | None = None,
 
  90          skip_dest_index: int | None = None):
 
  91     """Inserts the source_bitmap region defined by rectangular boundaries"""
 
  92     # pylint: disable=invalid-name
 
  94         x2 = source_bitmap.width
 
  96         y2 = source_bitmap.height
 
  98     # Rearrange so that x1 < x2 and y1 < y2
 
 104     # Ensure that x2 and y2 are within source bitmap size
 
 105     x2 = min(x2, source_bitmap.width)
 
 106     y2 = min(y2, source_bitmap.height)
 
 108     for y_count in range(y2 - y1):
 
 109         for x_count in range(x2 - x1):
 
 110             x_placement = x + x_count
 
 111             y_placement = y + y_count
 
 113             if (dest_bitmap.width > x_placement >= 0) and (
 
 114                     dest_bitmap.height > y_placement >= 0
 
 115             ):  # ensure placement is within target bitmap
 
 116                 # get the palette index from the source bitmap
 
 117                 this_pixel_color = source_bitmap[
 
 118                     y1 + (y_count * source_bitmap.width) + x1 + x_count
 
 121                 if (skip_source_index is None) or (this_pixel_color != skip_source_index):
 
 122                     if (skip_dest_index is None) or (
 
 123                             dest_bitmap[y_placement * dest_bitmap.width + x_placement] != skip_dest_index):
 
 124                         dest_bitmap[  # Direct index into a bitmap array is speedier than [x,y] tuple
 
 125                             y_placement * dest_bitmap.width + x_placement
 
 127             elif y_placement > dest_bitmap.height:
 
 137         dest_clip0: Tuple[int, int],
 
 138         dest_clip1: Tuple[int, int],
 
 141         source_clip0: Tuple[int, int],
 
 142         source_clip1: Tuple[int, int],
 
 147     dest_clip0_x, dest_clip0_y = dest_clip0
 
 148     dest_clip1_x, dest_clip1_y = dest_clip1
 
 149     source_clip0_x, source_clip0_y = source_clip0
 
 150     source_clip1_x, source_clip1_y = source_clip1
 
 157     sin_angle = math.sin(angle)
 
 158     cos_angle = math.cos(angle)
 
 160     def update_bounds(dx, dy):
 
 161         nonlocal minx, maxx, miny, maxy
 
 171     w = source_bitmap.width
 
 172     h = source_bitmap.height
 
 174     dx = -cos_angle * px * scale + sin_angle * py * scale + ox
 
 175     dy = -sin_angle * px * scale - cos_angle * py * scale + oy
 
 176     update_bounds(dx, dy)
 
 178     dx = cos_angle * (w - px) * scale + sin_angle * py * scale + ox
 
 179     dy = sin_angle * (w - px) * scale - cos_angle * py * scale + oy
 
 180     update_bounds(dx, dy)
 
 182     dx = cos_angle * (w - px) * scale - sin_angle * (h - py) * scale + ox
 
 183     dy = sin_angle * (w - px) * scale + cos_angle * (h - py) * scale + oy
 
 184     update_bounds(dx, dy)
 
 186     dx = -cos_angle * px * scale - sin_angle * (h - py) * scale + ox
 
 187     dy = -sin_angle * px * scale + cos_angle * (h - py) * scale + oy
 
 188     update_bounds(dx, dy)
 
 190     # Clip to destination area
 
 191     minx = max(minx, dest_clip0_x)
 
 192     maxx = min(maxx, dest_clip1_x - 1)
 
 193     miny = max(miny, dest_clip0_y)
 
 194     maxy = min(maxy, dest_clip1_y - 1)
 
 196     dv_col = cos_angle / scale
 
 197     du_col = sin_angle / scale
 
 201     startu = px - (ox * dv_col + oy * du_col)
 
 202     startv = py - (ox * dv_row + oy * du_row)
 
 204     rowu = startu + miny * du_col
 
 205     rowv = startv + miny * dv_col
 
 207     for y in range(miny, maxy + 1):
 
 208         u = rowu + minx * du_row
 
 209         v = rowv + minx * dv_row
 
 210         for x in range(minx, maxx + 1):
 
 211             if (source_clip0_x <= u < source_clip1_x) and (source_clip0_y <= v < source_clip1_y):
 
 212                 c = source_bitmap[int(u), int(v)]
 
 213                 if skip_index is None or c != skip_index:
 
 214                     dest_bitmap[x, y] = c
 
 223         data: circuitpython_typing.ReadableBuffer,
 
 224         x1: int = 0, y1: int = 0,
 
 225         x2: int | None = None, y2: int | None = None,
 
 226         skip_index: int | None = None):
 
 233     _value_count = 2 ** bitmap._bits_per_value
 
 234     for y in range(y1, y2):
 
 235         for x in range(x1, x2):
 
 236             i = y * (x2 - x1) + x
 
 237             value = int(data[i] % _value_count)
 
 238             if skip_index is None or value != skip_index: