]> Repositories - hackapet/Adafruit_Blinka_Displayio.git/commitdiff
Image processing optimizations
authorMelissa LeBlanc-Williams <melissa@adafruit.com>
Thu, 25 Jun 2020 21:47:21 +0000 (14:47 -0700)
committerMelissa LeBlanc-Williams <melissa@adafruit.com>
Thu, 25 Jun 2020 21:47:21 +0000 (14:47 -0700)
displayio/bitmap.py
displayio/display.py
displayio/palette.py
displayio/tilegrid.py

index 1bc90aa52331f478f36430b281ed060ab8ee0e7a..7ac075ce9cb74b9f2ac41c831c8e0d0bbf3ed135 100644 (file)
@@ -36,6 +36,7 @@ displayio for Blinka
 """
 
 from recordclass import recordclass
+from PIL import Image
 
 __version__ = "0.0.0-auto.0"
 __repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
@@ -75,7 +76,7 @@ class Bitmap:
         ):
             raise NotImplementedError("Invalid bits per value")
 
-        self._data = (width * height) * [0]
+        self._image = Image.new("P", (width, height), 0)
         self._dirty_area = Rectangle(0, 0, width, height)
 
     def __getitem__(self, index):
@@ -84,10 +85,16 @@ class Bitmap:
         an x,y tuple or an int equal to `y * width + x`.
         """
         if isinstance(index, (tuple, list)):
-            index = (index[1] * self._width) + index[0]
-        if index >= len(self._data):
+            x, y = index
+        elif isinstance(index, int):
+            x = index % self._width
+            y = index // self._width
+        else:
+            raise TypeError("Index is not an int, list, or tuple")
+
+        if x > self._image.width or y > self._image.height:
             raise ValueError("Index {} is out of range".format(index))
-        return self._data[index]
+        return self._image.getpixel((x, y))
 
     def __setitem__(self, index, value):
         """
@@ -103,7 +110,7 @@ class Bitmap:
         elif isinstance(index, int):
             x = index % self._width
             y = index // self._width
-        self._data[index] = value
+        self._image.putpixel((x, y), value)
         if self._dirty_area.x1 == self._dirty_area.x2:
             self._dirty_area.x1 = x
             self._dirty_area.x2 = x + 1
@@ -125,7 +132,7 @@ class Bitmap:
 
     def fill(self, value):
         """Fills the bitmap with the supplied palette index value."""
-        self._data = (self._width * self._height) * [value]
+        self._image = Image.new("P", (self._width, self._height), value)
         self._dirty_area = Rectangle(0, 0, self._width, self._height)
 
     @property
index 84cdc25ee985d0c51755db33d06b32687024527f..eb3d6356d90ac092bed4dc8041212d3ae8abe5af 100644 (file)
@@ -353,7 +353,7 @@ class Display:
         if 0 <= float(value) <= 1.0:
             self._brightness = value
             if self._backlight_type == BACKLIGHT_IN_OUT:
-                self._backlight.value = int(round(self._brightness))
+                self._backlight.value = round(self._brightness)
             # PWM not currently implemented
             # Command-based brightness not implemented
         else:
index 1812dbf93c19f6eeac426b471596d4e3cf514dca..ae3d27c47b3c1128df8ff1d7c4b71121a5c2d959 100644 (file)
@@ -111,3 +111,19 @@ class Palette:
         """Set the palette index to be an opaque color"""
         self._colors[palette_index]["transparent"] = False
         self._update_rgba(palette_index)
+
+    def _get_palette(self):
+        """Generate a palette for use with PIL"""
+        palette = []
+        for color in self._colors:
+            palette += color["rgba"][0:3]
+        return palette
+
+    def _get_alpha_palette(self):
+        """Generate an alpha channel palette with white being
+        opaque and black being transparent"""
+        palette = []
+        for color in self._colors:
+            for _ in range(3):
+                palette += [0 if color["transparent"] else 255]
+        return palette
index 7308342e105bf741261d8f0727b5a1fc11cf4c7e..861384e3cbee38559b1d25e65421179082a4fe96 100644 (file)
@@ -197,6 +197,25 @@ class TileGrid:
             return self._pixel_shader.convert(pixel_value)
         return pixel_value
 
+    def _apply_palette(self, image):
+        if isinstance(self._pixel_shader, Palette):
+            image.putpalette(
+                self._pixel_shader._get_palette()
+            )  # pylint: disable=protected-access
+        if isinstance(self._pixel_shader, ColorConverter):
+            # This will be needed for eInks, grayscale, and monochrome displays
+            pass
+
+    def _add_alpha(self, image):
+        if isinstance(self._pixel_shader, Palette):
+            alpha = self._bitmap._image.copy().convert(
+                "P"
+            )  # pylint: disable=protected-access
+            alpha.putpalette(
+                self._pixel_shader._get_alpha_palette()
+            )  # pylint: disable=protected-access
+            image.putalpha(alpha.convert("L"))
+
     # pylint: disable=too-many-locals
     def _fill_area(self, buffer):
         """Draw onto the image"""
@@ -218,14 +237,20 @@ class TileGrid:
                 tile_index = self._tiles[tile_y * self._width + tile_x]
                 tile_index_x = tile_index % tile_count_x
                 tile_index_y = tile_index // tile_count_x
-                for pixel_x in range(self._tile_width):
-                    for pixel_y in range(self._tile_height):
-                        image_x = (tile_x * self._tile_width) + pixel_x
-                        image_y = (tile_y * self._tile_height) + pixel_y
-                        bitmap_x = (tile_index_x * self._tile_width) + pixel_x
-                        bitmap_y = (tile_index_y * self._tile_height) + pixel_y
-                        pixel_color = self._shade(self._bitmap[bitmap_x, bitmap_y])
-                        image.putpixel((image_x, image_y), pixel_color)
+                tile_image = self._bitmap._image.copy().convert(
+                    "P"
+                )  # pylint: disable=protected-access
+                self._apply_palette(tile_image)
+                tile_image = tile_image.convert("RGBA")
+                self._add_alpha(tile_image)
+                image.alpha_composite(
+                    tile_image,
+                    dest=(tile_x * self._tile_width, tile_y * self._tile_height),
+                    source=(
+                        tile_index_x * self._tile_width,
+                        tile_index_y * self._tile_height,
+                    ),
+                )
 
         if self._absolute_transform is not None:
             if self._absolute_transform.scale > 1: