1 # SPDX-FileCopyrightText: 2020 Melissa LeBlanc-Williams for Adafruit Industries
3 # SPDX-License-Identifier: MIT
7 ================================================================================
11 **Software and Dependencies:**
14 https://github.com/adafruit/Adafruit_Blinka/releases
16 * Author(s): Melissa LeBlanc-Williams
20 from __future__ import annotations
21 from typing import Union, Tuple
22 from recordclass import recordclass
25 __version__ = "0.0.0-auto.0"
26 __repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
28 Rectangle = recordclass("Rectangle", "x1 y1 x2 y2")
32 """Stores values of a certain size in a 2D array"""
34 def __init__(self, width: int, height: int, value_count: int):
35 """Create a Bitmap object with the given fixed size. Each pixel stores a value that is
36 used to index into a corresponding palette. This enables differently colored sprites to
37 share the underlying Bitmap. value_count is used to minimize the memory used to store
40 self._bmp_width = width
41 self._bmp_height = height
42 self._read_only = False
45 raise ValueError("value_count must be > 0")
48 while (value_count - 1) >> bits:
54 self._bits_per_value = bits
57 self._bits_per_value > 8
58 and self._bits_per_value != 16
59 and self._bits_per_value != 32
61 raise NotImplementedError("Invalid bits per value")
63 self._image = Image.new("P", (width, height), 0)
64 self._dirty_area = Rectangle(0, 0, width, height)
66 def __getitem__(self, index: Union[Tuple[int, int], int]) -> int:
68 Returns the value at the given index. The index can either be
69 an x,y tuple or an int equal to `y * width + x`.
71 if isinstance(index, (tuple, list)):
73 elif isinstance(index, int):
74 x = index % self._bmp_width
75 y = index // self._bmp_width
77 raise TypeError("Index is not an int, list, or tuple")
79 if x > self._image.width or y > self._image.height:
80 raise ValueError(f"Index {index} is out of range")
81 return self._image.getpixel((x, y))
83 def __setitem__(self, index: Union[Tuple[int, int], int], value: int) -> None:
85 Sets the value at the given index. The index can either be
86 an x,y tuple or an int equal to `y * width + x`.
89 raise RuntimeError("Read-only object")
90 if isinstance(index, (tuple, list)):
93 index = y * self._bmp_width + x
94 elif isinstance(index, int):
95 x = index % self._bmp_width
96 y = index // self._bmp_width
97 self._image.putpixel((x, y), value)
98 if self._dirty_area.x1 == self._dirty_area.x2:
99 self._dirty_area.x1 = x
100 self._dirty_area.x2 = x + 1
101 self._dirty_area.y1 = y
102 self._dirty_area.y2 = y + 1
104 if x < self._dirty_area.x1:
105 self._dirty_area.x1 = x
106 elif x >= self._dirty_area.x2:
107 self._dirty_area.x2 = x + 1
108 if y < self._dirty_area.y1:
109 self._dirty_area.y1 = y
110 elif y >= self._dirty_area.y2:
111 self._dirty_area.y2 = y + 1
113 def _finish_refresh(self):
114 self._dirty_area.x1 = 0
115 self._dirty_area.x2 = 0
117 def fill(self, value: int) -> None:
118 """Fills the bitmap with the supplied palette index value."""
119 self._image = Image.new("P", (self._bmp_width, self._bmp_height), value)
120 self._dirty_area = Rectangle(0, 0, self._bmp_width, self._bmp_height)
126 source_bitmap: Bitmap,
134 # pylint: disable=unnecessary-pass
135 """Inserts the source_bitmap region defined by rectangular boundaries"""
138 def dirty(self, x1: int = 0, y1: int = 0, x2: int = -1, y2: int = -1) -> None:
139 # pylint: disable=unnecessary-pass
140 """Inform displayio of bitmap updates done via the buffer protocol."""
144 def width(self) -> int:
145 """Width of the bitmap. (read only)"""
146 return self._bmp_width
149 def height(self) -> int:
150 """Height of the bitmap. (read only)"""
151 return self._bmp_height