]> Repositories - hackapet/Adafruit_Blinka_Displayio.git/blobdiff - displayio/_colorconverter.py
convert tuple to hex color first
[hackapet/Adafruit_Blinka_Displayio.git] / displayio / _colorconverter.py
index c3c548100b0e9aef46caa8a449b8c97bc95bf7e6..7fd12bb998bdea9e15788f46c72be5738f41afae 100644 (file)
@@ -21,7 +21,8 @@ __version__ = "0.0.0+auto.0"
 __repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
 
 from ._colorspace import Colorspace
-from ._structs import ColorspaceStruct
+from ._structs import ColorspaceStruct, InputPixelStruct, OutputPixelStruct
+from ._helpers import clamp, bswap16
 
 
 class ColorConverter:
@@ -44,25 +45,19 @@ class ColorConverter:
         self._cached_colorspace = None
         self._cached_input_pixel = None
         self._cached_output_color = None
+        self._needs_refresh = False
 
     @staticmethod
-    def _clamp(value, min_value, max_value):
-        return max(min(max_value, value), min_value)
-
-    @staticmethod
-    def _bswap16(value):
-        # ABCD -> 00DC
-        return (value & 0xFF00) >> 8 | (value & 0x00FF) << 8
-
-    def _dither_noise_1(self, noise):
+    def _dither_noise_1(noise):
         noise = (noise >> 13) ^ noise
         more_noise = (
             noise * (noise * noise * 60493 + 19990303) + 1376312589
         ) & 0x7FFFFFFF
-        return self._clamp(int((more_noise / (1073741824.0 * 2)) * 255), 0, 0xFFFFFFFF)
+        return clamp(int((more_noise / (1073741824.0 * 2)) * 255), 0, 0xFFFFFFFF)
 
-    def _dither_noise_2(self, x, y):
-        return self._dither_noise_1(x + y * 0xFFFF)
+    @staticmethod
+    def _dither_noise_2(x, y):
+        return ColorConverter._dither_noise_1(x + y * 0xFFFF)
 
     @staticmethod
     def _compute_rgb565(color_rgb888: int):
@@ -90,7 +85,7 @@ class ColorConverter:
         red8 = color_rgb888 >> 16
         grn8 = (color_rgb888 >> 8) & 0xFF
         blu8 = color_rgb888 & 0xFF
-        return (red8 * 19 + grn8 * 182 + blu8 + 54) / 255
+        return (red8 * 19 + grn8 * 182 + blu8 * 54) // 255
 
     @staticmethod
     def _compute_chroma(color_rgb888: int):
@@ -110,21 +105,22 @@ class ColorConverter:
             return 0
         hue = 0
         if max_color == red8:
-            hue = (((grn8 - blu8) * 40) / chroma) % 240
+            hue = (((grn8 - blu8) * 40) // chroma) % 240
         elif max_color == grn8:
-            hue = (((blu8 - red8) + (2 * chroma)) * 40) / chroma
+            hue = (((blu8 - red8) + (2 * chroma)) * 40) // chroma
         elif max_color == blu8:
-            hue = (((red8 - grn8) + (4 * chroma)) * 40) / chroma
+            hue = (((red8 - grn8) + (4 * chroma)) * 40) // chroma
         if hue < 0:
             hue += 240
 
         return hue
 
-    def _compute_sevencolor(self, color_rgb888: int):
+    @staticmethod
+    def _compute_sevencolor(color_rgb888: int):
         # pylint: disable=too-many-return-statements
-        chroma = self._compute_chroma(color_rgb888)
+        chroma = ColorConverter._compute_chroma(color_rgb888)
         if chroma >= 64:
-            hue = self._compute_hue(color_rgb888)
+            hue = ColorConverter._compute_hue(color_rgb888)
             # Red 0
             if hue < 10:
                 return 0x4
@@ -142,15 +138,13 @@ class ColorConverter:
                 return 0x3
             # The rest is red to 255
             return 0x4
-        luma = self._compute_luma(color_rgb888)
+        luma = ColorConverter._compute_luma(color_rgb888)
         if luma >= 128:
             return 0x1  # White
         return 0x0  # Black
 
     @staticmethod
-    def _compute_tricolor(
-        colorspace: ColorspaceStruct, pixel_hue: int, color: int
-    ) -> int:
+    def _compute_tricolor(colorspace: ColorspaceStruct, pixel_hue: int) -> int:
         hue_diff = colorspace.tricolor_hue - pixel_hue
         if -10 <= hue_diff <= 10 or hue_diff <= -220 or hue_diff >= 220:
             if colorspace.grayscale:
@@ -173,51 +167,61 @@ class ColorConverter:
         else:
             raise ValueError("Color must be an integer or 3 or 4 value tuple")
 
-        input_pixel = {
-            "pixel": color,
-            "x": 0,
-            "y": 0,
-            "tile": 0,
-            "tile_x": 0,
-            "tile_y": 0,
-        }
+        input_pixel = InputPixelStruct(pixel=color)
+        output_pixel = OutputPixelStruct()
 
-        output_pixel = {"pixel": 0, "opaque": False}
+        self._convert(self._output_colorspace, input_pixel, output_pixel)
 
-        if input_pixel["pixel"] == self._transparent_color:
-            return output_pixel["pixel"]
+        return output_pixel.pixel
+
+    def _convert(
+        self,
+        colorspace: Colorspace,
+        input_pixel: InputPixelStruct,
+        output_color: OutputPixelStruct,
+    ) -> None:
+        pixel = input_pixel.pixel
+
+        if self._transparent_color == pixel:
+            output_color.opaque = False
+            return
 
         if (
             not self._dither
-            and self._cached_colorspace == self._output_colorspace
-            and self._cached_input_pixel == input_pixel["pixel"]
+            and self._cached_colorspace == colorspace
+            and self._cached_input_pixel == input_pixel.pixel
         ):
-            return self._cached_output_color
+            output_color.pixel = self._cached_output_color
+            return
 
         rgb888_pixel = input_pixel
-        rgb888_pixel["pixel"] = self._convert_pixel(
-            self._input_colorspace, input_pixel["pixel"]
-        )
-        self._convert_color(
-            self._output_colorspace, self._dither, rgb888_pixel, output_pixel
+        rgb888_pixel.pixel = self._convert_pixel(
+            self._input_colorspace, input_pixel.pixel
         )
+        self._convert_color(colorspace, self._dither, rgb888_pixel, output_color)
 
         if not self._dither:
-            self._cached_colorspace = self._output_colorspace
-            self._cached_input_pixel = input_pixel["pixel"]
-            self._cached_output_color = output_pixel["pixel"]
+            self._cached_colorspace = colorspace
+            self._cached_input_pixel = input_pixel.pixel
+            self._cached_output_color = output_color.pixel
 
-        return output_pixel["pixel"]
+    @staticmethod
+    def _rgbtuple_to_hex(color_tuple):
+        """Convert rgb tuple with 0-255 values to hex color value"""
+        return color_tuple[0] << 16 | color_tuple[1] << 8 | color_tuple[2]
 
-    def _convert_pixel(self, colorspace: Colorspace, pixel: int) -> int:
-        pixel = self._clamp(pixel, 0, 0xFFFFFFFF)
+    @staticmethod
+    def _convert_pixel(colorspace: Colorspace, pixel: int) -> int:
+        if isinstance(pixel, tuple):
+            pixel = ColorConverter._rgbtuple_to_hex(pixel)
+        pixel = clamp(pixel, 0, 0xFFFFFFFF)
         if colorspace in (
             Colorspace.RGB565_SWAPPED,
             Colorspace.RGB555_SWAPPED,
             Colorspace.BGR565_SWAPPED,
             Colorspace.BGR555_SWAPPED,
         ):
-            pixel = self._bswap16(pixel)
+            pixel = bswap16(pixel)
         if colorspace in (Colorspace.RGB565, Colorspace.RGB565_SWAPPED):
             red8 = (pixel >> 11) << 3
             grn8 = ((pixel >> 5) << 2) & 0xFF
@@ -242,19 +246,25 @@ class ColorConverter:
             return (pixel & 0xFF) & 0x01010101
         return pixel
 
+    @staticmethod
     def _convert_color(
-        self,
         colorspace: ColorspaceStruct,
         dither: bool,
-        input_pixel: dict,
-        output_color: dict,
+        input_pixel: InputPixelStruct,
+        output_color: OutputPixelStruct,
     ) -> None:
         # pylint: disable=too-many-return-statements, too-many-branches, too-many-statements
-        pixel = input_pixel["pixel"]
+        pixel = input_pixel.pixel
         if dither:
-            rand_red = self._dither_noise_2(input_pixel["x"], input_pixel["y"])
-            rand_grn = self._dither_noise_2(input_pixel["x"] + 33, input_pixel["y"])
-            rand_blu = self._dither_noise_2(input_pixel["x"], input_pixel["y"] + 33)
+            rand_red = ColorConverter._dither_noise_2(
+                input_pixel.tile_x, input_pixel.tile_y
+            )
+            rand_grn = ColorConverter._dither_noise_2(
+                input_pixel.tile_x + 33, input_pixel.tile_y
+            )
+            rand_blu = ColorConverter._dither_noise_2(
+                input_pixel.tile_x, input_pixel.tile_y + 33
+            )
 
             red8 = pixel >> 16
             grn8 = (pixel >> 8) & 0xFF
@@ -272,49 +282,49 @@ class ColorConverter:
             pixel = (red8 << 16) | (grn8 << 8) | blu8
 
         if colorspace.depth == 16:
-            packed = self._compute_rgb565(pixel)
+            packed = ColorConverter._compute_rgb565(pixel)
             if colorspace.reverse_bytes_in_word:
-                packed = self._bswap16(packed)
-            output_color["pixel"] = packed
-            output_color["opaque"] = True
+                packed = bswap16(packed)
+            output_color.pixel = packed
+            output_color.opaque = True
             return
         if colorspace.tricolor:
-            output_color["pixel"] = self._compute_luma(pixel) >> (8 - colorspace.depth)
-            if self._compute_chroma(pixel) <= 16:
+            output_color.pixel = ColorConverter._compute_luma(pixel) >> (
+                8 - colorspace.depth
+            )
+            if ColorConverter._compute_chroma(pixel) <= 16:
                 if not colorspace.grayscale:
-                    output_color["pixel"] = 0
-                output_color["opaque"] = True
+                    output_color.pixel = 0
+                output_color.opaque = True
                 return
-            pixel_hue = self._compute_hue(pixel)
-            output_color["pixel"] = self._compute_tricolor(
-                colorspace, pixel_hue, output_color["pixel"]
-            )
+            pixel_hue = ColorConverter._compute_hue(pixel)
+            output_color.pixel = ColorConverter._compute_tricolor(colorspace, pixel_hue)
             return
         if colorspace.grayscale and colorspace.depth <= 8:
             bitmask = (1 << colorspace.depth) - 1
-            output_color["pixel"] = (
-                self._compute_luma(pixel) >> colorspace.grayscale_bit
+            output_color.pixel = (
+                ColorConverter._compute_luma(pixel) >> colorspace.grayscale_bit
             ) & bitmask
-            output_color["opaque"] = True
+            output_color.opaque = True
             return
         if colorspace.depth == 32:
-            output_color["pixel"] = pixel
-            output_color["opaque"] = True
+            output_color.pixel = pixel
+            output_color.opaque = True
             return
         if colorspace.depth == 8 and colorspace.grayscale:
-            packed = self._compute_rgb332(pixel)
-            output_color["pixel"] = packed
-            output_color["opaque"] = True
+            packed = ColorConverter._compute_rgb332(pixel)
+            output_color.pixel = packed
+            output_color.opaque = True
             return
         if colorspace.depth == 4:
             if colorspace.sevencolor:
-                packed = self._compute_sevencolor(pixel)
+                packed = ColorConverter._compute_sevencolor(pixel)
             else:
-                packed = self._compute_rgbd(pixel)
-            output_color["pixel"] = packed
-            output_color["opaque"] = True
+                packed = ColorConverter._compute_rgbd(pixel)
+            output_color.pixel = packed
+            output_color.opaque = True
             return
-        output_color["opaque"] = False
+        output_color.opaque = False
 
     def make_transparent(self, color: int) -> None:
         """Set the transparent color or index for the ColorConverter. This will
@@ -326,6 +336,9 @@ class ColorConverter:
         """Make the ColorConverter be opaque and have no transparent pixels."""
         self._transparent_color = None
 
+    def _finish_refresh(self) -> None:
+        pass
+
     @property
     def dither(self) -> bool:
         """When true the color converter dithers the output by adding