]> Repositories - hackapet/Adafruit_Blinka_Displayio.git/blob - displayio/_colorconverter.py
Update to pyproject.toml
[hackapet/Adafruit_Blinka_Displayio.git] / displayio / _colorconverter.py
1 # SPDX-FileCopyrightText: 2020 Melissa LeBlanc-Williams for Adafruit Industries
2 #
3 # SPDX-License-Identifier: MIT
4
5 """
6 `displayio.colorconverter`
7 ================================================================================
8
9 displayio for Blinka
10
11 **Software and Dependencies:**
12
13 * Adafruit Blinka:
14   https://github.com/adafruit/Adafruit_Blinka/releases
15
16 * Author(s): Melissa LeBlanc-Williams
17
18 """
19
20 __version__ = "0.0.0+auto.0"
21 __repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
22
23 from ._colorspace import Colorspace
24
25
26 class ColorConverter:
27     """Converts one color format to another. Color converter based on original displayio
28     code for consistency.
29     """
30
31     def __init__(
32         self, *, input_colorspace: Colorspace = Colorspace.RGB888, dither: bool = False
33     ):
34         """Create a ColorConverter object to convert color formats.
35         Only supports rgb888 to RGB565 currently.
36         :param bool dither: Adds random noise to dither the output image
37         """
38         self._dither = dither
39         self._depth = 16
40         self._transparent_color = None
41         self._rgba = False
42         self._input_colorspace = input_colorspace
43
44     def _compute_rgb565(self, color: int):
45         self._depth = 16
46         return (color[0] & 0xF8) << 8 | (color[1] & 0xFC) << 3 | color[2] >> 3
47
48     @staticmethod
49     def _compute_luma(color: int):
50         red = color >> 16
51         green = (color >> 8) & 0xFF
52         blue = color & 0xFF
53         return (red * 19) / 255 + (green * 182) / 255 + (blue + 54) / 255
54
55     @staticmethod
56     def _compute_chroma(color: int):
57         red = color >> 16
58         green = (color >> 8) & 0xFF
59         blue = color & 0xFF
60         return max(red, green, blue) - min(red, green, blue)
61
62     def _compute_hue(self, color: int):
63         red = color >> 16
64         green = (color >> 8) & 0xFF
65         blue = color & 0xFF
66         max_color = max(red, green, blue)
67         chroma = self._compute_chroma(color)
68         if chroma == 0:
69             return 0
70         hue = 0
71         if max_color == red:
72             hue = (((green - blue) * 40) / chroma) % 240
73         elif max_color == green:
74             hue = (((blue - red) + (2 * chroma)) * 40) / chroma
75         elif max_color == blue:
76             hue = (((red - green) + (4 * chroma)) * 40) / chroma
77         if hue < 0:
78             hue += 240
79
80         return hue
81
82     @staticmethod
83     def _dither_noise_1(noise):
84         noise = (noise >> 13) ^ noise
85         more_noise = (
86             noise * (noise * noise * 60493 + 19990303) + 1376312589
87         ) & 0x7FFFFFFF
88         return (more_noise / (1073741824.0 * 2)) * 255
89
90     def _dither_noise_2(self, x, y):
91         return self._dither_noise_1(x + y * 0xFFFF)
92
93     def _compute_tricolor(self):
94         pass
95
96     def convert(self, color: int) -> int:
97         "Converts the given rgb888 color to RGB565"
98         if isinstance(color, int):
99             color = ((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF, 255)
100         elif isinstance(color, tuple):
101             if len(color) == 3:
102                 color = (color[0], color[1], color[2], 255)
103             elif len(color) != 4:
104                 raise ValueError("Color must be a 3 or 4 value tuple")
105         else:
106             raise ValueError("Color must be an integer or 3 or 4 value tuple")
107
108         if self._dither:
109             return color  # To Do: return a dithered color
110         if self._rgba:
111             return color
112         return self._compute_rgb565(color)
113
114     def make_transparent(self, color: int) -> None:
115         """Set the transparent color or index for the ColorConverter. This will
116         raise an Exception if there is already a selected transparent index.
117         """
118         self._transparent_color = color
119
120     def make_opaque(self, color: int) -> None:
121         # pylint: disable=unused-argument
122         """Make the ColorConverter be opaque and have no transparent pixels."""
123         self._transparent_color = None
124
125     @property
126     def dither(self) -> bool:
127         """When true the color converter dithers the output by adding
128         random noise when truncating to display bitdepth
129         """
130         return self._dither
131
132     @dither.setter
133     def dither(self, value: bool):
134         if not isinstance(value, bool):
135             raise ValueError("Value should be boolean")
136         self._dither = value