]> Repositories - hackapet/Adafruit_Blinka_Displayio.git/blob - displayio/colorconverter.py
Add pre-commit support.
[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
24 class ColorConverter:
25     """Converts one color format to another. Color converter based on original displayio
26     code for consistency.
27     """
28
29     def __init__(self, *, dither=False):
30         """Create a ColorConverter object to convert color formats.
31         Only supports rgb888 to RGB565 currently.
32         :param bool dither: Adds random noise to dither the output image
33         """
34         self._dither = dither
35         self._depth = 16
36         self._rgba = False
37
38     # pylint: disable=no-self-use
39     def _compute_rgb565(self, color):
40         self._depth = 16
41         return (color[0] & 0xF8) << 8 | (color[1] & 0xFC) << 3 | color[2] >> 3
42
43     def _compute_luma(self, color):
44         red = color >> 16
45         green = (color >> 8) & 0xFF
46         blue = color & 0xFF
47         return (red * 19) / 255 + (green * 182) / 255 + (blue + 54) / 255
48
49     def _compute_chroma(self, color):
50         red = color >> 16
51         green = (color >> 8) & 0xFF
52         blue = color & 0xFF
53         return max(red, green, blue) - min(red, green, blue)
54
55     def _compute_hue(self, color):
56         red = color >> 16
57         green = (color >> 8) & 0xFF
58         blue = color & 0xFF
59         max_color = max(red, green, blue)
60         chroma = self._compute_chroma(color)
61         if chroma == 0:
62             return 0
63         hue = 0
64         if max_color == red:
65             hue = (((green - blue) * 40) / chroma) % 240
66         elif max_color == green:
67             hue = (((blue - red) + (2 * chroma)) * 40) / chroma
68         elif max_color == blue:
69             hue = (((red - green) + (4 * chroma)) * 40) / chroma
70         if hue < 0:
71             hue += 240
72
73         return hue
74
75     def _dither_noise_1(self, noise):
76         noise = (noise >> 13) ^ noise
77         more_noise = (
78             noise * (noise * noise * 60493 + 19990303) + 1376312589
79         ) & 0x7FFFFFFF
80         return (more_noise / (1073741824.0 * 2)) * 255
81
82     def _dither_noise_2(self, x, y):
83         return self._dither_noise_1(x + y * 0xFFFF)
84
85     def _compute_tricolor(self):
86         pass
87
88     def convert(self, color):
89         "Converts the given rgb888 color to RGB565"
90         if isinstance(color, int):
91             color = ((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF, 255)
92         elif isinstance(color, tuple):
93             if len(color) == 3:
94                 color = (color[0], color[1], color[2], 255)
95             elif len(color) != 4:
96                 raise ValueError("Color must be a 3 or 4 value tuple")
97         else:
98             raise ValueError("Color must be an integer or 3 or 4 value tuple")
99
100         if self._dither:
101             return color  # To Do: return a dithered color
102         if self._rgba:
103             return color
104         return self._compute_rgb565(color)
105
106     # pylint: enable=no-self-use
107
108     @property
109     def dither(self):
110         """When true the color converter dithers the output by adding
111         random noise when truncating to display bitdepth
112         """
113         return self._dither
114
115     @dither.setter
116     def dither(self, value):
117         if not isinstance(value, bool):
118             raise ValueError("Value should be boolean")
119         self._dither = value