1 # The MIT License (MIT)
 
   3 # Copyright (c) 2020 Melissa LeBlanc-Williams for Adafruit Industries
 
   5 # Permission is hereby granted, free of charge, to any person obtaining a copy
 
   6 # of this software and associated documentation files (the "Software"), to deal
 
   7 # in the Software without restriction, including without limitation the rights
 
   8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
   9 # copies of the Software, and to permit persons to whom the Software is
 
  10 # furnished to do so, subject to the following conditions:
 
  12 # The above copyright notice and this permission notice shall be included in
 
  13 # all copies or substantial portions of the Software.
 
  15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
  16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
  17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
  18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
  19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
  20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
  25 ================================================================================
 
  29 **Software and Dependencies:**
 
  32   https://github.com/adafruit/Adafruit_Blinka/releases
 
  34 * Author(s): Melissa LeBlanc-Williams
 
  38 from recordclass import recordclass
 
  41 __version__ = "0.0.0-auto.0"
 
  42 __repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
 
  44 Rectangle = recordclass("Rectangle", "x1 y1 x2 y2")
 
  48     """Stores values of a certain size in a 2D array"""
 
  50     def __init__(self, width, height, value_count):
 
  51         """Create a Bitmap object with the given fixed size. Each pixel stores a value that is
 
  52         used to index into a corresponding palette. This enables differently colored sprites to
 
  53         share the underlying Bitmap. value_count is used to minimize the memory used to store
 
  58         self._read_only = False
 
  61             raise ValueError("value_count must be > 0")
 
  64         while (value_count - 1) >> bits:
 
  70         self._bits_per_value = bits
 
  73             self._bits_per_value > 8
 
  74             and self._bits_per_value != 16
 
  75             and self._bits_per_value != 32
 
  77             raise NotImplementedError("Invalid bits per value")
 
  79         self._image = Image.new("P", (width, height), 0)
 
  80         self._dirty_area = Rectangle(0, 0, width, height)
 
  82     def __getitem__(self, index):
 
  84         Returns the value at the given index. The index can either be
 
  85         an x,y tuple or an int equal to `y * width + x`.
 
  87         if isinstance(index, (tuple, list)):
 
  89         elif isinstance(index, int):
 
  90             x = index % self._width
 
  91             y = index // self._width
 
  93             raise TypeError("Index is not an int, list, or tuple")
 
  95         if x > self._image.width or y > self._image.height:
 
  96             raise ValueError("Index {} is out of range".format(index))
 
  97         return self._image.getpixel((x, y))
 
  99     def __setitem__(self, index, value):
 
 101         Sets the value at the given index. The index can either be
 
 102         an x,y tuple or an int equal to `y * width + x`.
 
 105             raise RuntimeError("Read-only object")
 
 106         if isinstance(index, (tuple, list)):
 
 109             index = y * self._width + x
 
 110         elif isinstance(index, int):
 
 111             x = index % self._width
 
 112             y = index // self._width
 
 113         self._image.putpixel((x, y), value)
 
 114         if self._dirty_area.x1 == self._dirty_area.x2:
 
 115             self._dirty_area.x1 = x
 
 116             self._dirty_area.x2 = x + 1
 
 117             self._dirty_area.y1 = y
 
 118             self._dirty_area.y2 = y + 1
 
 120             if x < self._dirty_area.x1:
 
 121                 self._dirty_area.x1 = x
 
 122             elif x >= self._dirty_area.x2:
 
 123                 self._dirty_area.x2 = x + 1
 
 124             if y < self._dirty_area.y1:
 
 125                 self._dirty_area.y1 = y
 
 126             elif y >= self._dirty_area.y2:
 
 127                 self._dirty_area.y2 = y + 1
 
 129     def _finish_refresh(self):
 
 130         self._dirty_area.x1 = 0
 
 131         self._dirty_area.x2 = 0
 
 133     def fill(self, value):
 
 134         """Fills the bitmap with the supplied palette index value."""
 
 135         self._image = Image.new("P", (self._width, self._height), value)
 
 136         self._dirty_area = Rectangle(0, 0, self._width, self._height)
 
 140         """Width of the bitmap. (read only)"""
 
 145         """Height of the bitmap. (read only)"""