ESPHome 2026.9.0-dev
Loading...
Searching...
No Matches
colorconv.h
Go to the documentation of this file.
1#pragma once
2
3#include <cstdint>
4#include <algorithm>
6
7/* Utility for converting internal \a Color RGB representation to supported IC hardware color keys
8 *
9 * Focus in driver layer is on efficiency.
10 * For optimum output quality on RGB inputs consider offline color keying/dithering.
11 * Also see e.g. Image component.
12 */
13
15
17static constexpr uint8_t COLORCONV_GRAY_THRESHOLD = 50;
18
30template<typename NATIVE_COLOR>
31constexpr NATIVE_COLOR color_to_bwyr(Color color, NATIVE_COLOR hw_black, NATIVE_COLOR hw_white, NATIVE_COLOR hw_yellow,
32 NATIVE_COLOR hw_red) {
33 // --- Step 1: Check for Grayscale (Black or White) ---
34 // We define "grayscale" as a color where the min and max components
35 // are close to each other.
36
37 const auto [min_rgb, max_rgb] = std::minmax({color.r, color.g, color.b});
38
39 if ((max_rgb - min_rgb) < COLORCONV_GRAY_THRESHOLD) {
40 // It's a shade of gray. Map to BLACK or WHITE.
41 // We split the luminance at the halfway point (382 = (255*3)/2)
42 if ((static_cast<int>(color.r) + color.g + color.b) > 382) {
43 return hw_white;
44 }
45 return hw_black;
46 }
47
48 // --- Step 2: Check for Primary/Secondary Colors ---
49 // If it's not gray, it's a color. We check which components are
50 // "on" (over 128) vs "off". This divides the RGB cube into 8 corners.
51 const bool r_on = (color.r > 128);
52 const bool g_on = (color.g > 128);
53 const bool b_on = (color.b > 128);
54
55 if (r_on) {
56 if (!b_on) {
57 return g_on ? hw_yellow : hw_red;
58 }
59
60 // At least red+blue high (but not gray) -> White
61 return hw_white;
62 } else {
63 return (b_on && g_on) ? hw_white : hw_black;
64 }
65}
66
79template<typename NATIVE_COLOR>
80constexpr NATIVE_COLOR color_to_bwr(Color color, NATIVE_COLOR hw_black, NATIVE_COLOR hw_white, NATIVE_COLOR hw_red) {
81 return color_to_bwyr<NATIVE_COLOR>(color, hw_black, hw_white, /*hw_yellow=*/hw_white, hw_red);
82}
83
101template<typename NATIVE_COLOR>
102constexpr NATIVE_COLOR color_to_bwyrgb(Color color, NATIVE_COLOR hw_black, NATIVE_COLOR hw_white,
103 NATIVE_COLOR hw_yellow, NATIVE_COLOR hw_red, NATIVE_COLOR hw_green,
104 NATIVE_COLOR hw_blue) {
105 const auto [min_rgb, max_rgb] = std::minmax({color.r, color.g, color.b});
106
107 if ((max_rgb - min_rgb) < COLORCONV_GRAY_THRESHOLD) {
108 if ((static_cast<int>(color.r) + color.g + color.b) > 382) {
109 return hw_white;
110 }
111 return hw_black;
112 }
113
114 const bool r_on = (color.r > 128);
115 const bool g_on = (color.g > 128);
116 const bool b_on = (color.b > 128);
117
118 if (r_on && g_on && !b_on) {
119 return hw_yellow;
120 }
121 if (r_on && !g_on && !b_on) {
122 return hw_red;
123 }
124 if (!r_on && g_on && !b_on) {
125 return hw_green;
126 }
127 if (!r_on && !g_on && b_on) {
128 return hw_blue;
129 }
130 // Handle "impure" colors (cyan, magenta) by folding into the closest primary.
131 if (!r_on && g_on && b_on) {
132 return hw_green; // cyan
133 }
134 if (r_on && !g_on) {
135 return hw_red; // magenta
136 }
137 if (r_on) {
138 // All high (but not gray) -> white
139 return hw_white;
140 }
141 // !r_on && !g_on && !b_on
142 // All low (but not gray) -> black
143 return hw_black;
144}
145
163template<typename NATIVE_COLOR>
164constexpr NATIVE_COLOR color_to_bwyrgbo(Color color, NATIVE_COLOR hw_black, NATIVE_COLOR hw_white,
165 NATIVE_COLOR hw_yellow, NATIVE_COLOR hw_red, NATIVE_COLOR hw_green,
166 NATIVE_COLOR hw_blue, NATIVE_COLOR hw_orange) {
167 const auto [min_rgb, max_rgb] = std::minmax({color.r, color.g, color.b});
168
169 if ((max_rgb - min_rgb) < COLORCONV_GRAY_THRESHOLD) {
170 if ((static_cast<int>(color.r) + color.g + color.b) > 382) {
171 return hw_white;
172 }
173 return hw_black;
174 }
175
176 const bool r_on = (color.r > 128);
177 const bool g_on = (color.g > 128);
178 const bool b_on = (color.b > 128);
179
180 if (r_on && !b_on) {
181 // Between red and yellow: split the gradient in three instead of two, since this panel has a
182 // dedicated orange ink. Named orange (e.g. 0xFFA500) has g close to the midpoint, so the
183 // plain g_on (>128) threshold used by color_to_bwyrgb can't tell it apart from yellow.
184 if (color.g > 170) {
185 return hw_yellow;
186 }
187 if (color.g > 85) {
188 return hw_orange;
189 }
190 return hw_red;
191 }
192 if (!r_on && g_on && !b_on) {
193 return hw_green;
194 }
195 if (!r_on && !g_on && b_on) {
196 return hw_blue;
197 }
198 // Handle "impure" colors (cyan, magenta) by folding into the closest primary.
199 if (!r_on && g_on && b_on) {
200 return hw_green; // cyan
201 }
202 if (r_on && !g_on) {
203 return hw_red; // magenta
204 }
205 if (r_on) {
206 // All high (but not gray) -> white
207 return hw_white;
208 }
209 // !r_on && !g_on && !b_on
210 // All low (but not gray) -> black
211 return hw_black;
212}
213
214} // namespace esphome::epaper_spi
constexpr NATIVE_COLOR color_to_bwyrgbo(Color color, NATIVE_COLOR hw_black, NATIVE_COLOR hw_white, NATIVE_COLOR hw_yellow, NATIVE_COLOR hw_red, NATIVE_COLOR hw_green, NATIVE_COLOR hw_blue, NATIVE_COLOR hw_orange)
Map RGB color to discrete BWYRGBO hex 7 color key.
Definition colorconv.h:164
constexpr NATIVE_COLOR color_to_bwr(Color color, NATIVE_COLOR hw_black, NATIVE_COLOR hw_white, NATIVE_COLOR hw_red)
Map RGB color to discrete BWR (black/white/red) 3 color key.
Definition colorconv.h:80
constexpr NATIVE_COLOR color_to_bwyrgb(Color color, NATIVE_COLOR hw_black, NATIVE_COLOR hw_white, NATIVE_COLOR hw_yellow, NATIVE_COLOR hw_red, NATIVE_COLOR hw_green, NATIVE_COLOR hw_blue)
Map RGB color to discrete BWYRGB hex 6 color key.
Definition colorconv.h:102
constexpr NATIVE_COLOR color_to_bwyr(Color color, NATIVE_COLOR hw_black, NATIVE_COLOR hw_white, NATIVE_COLOR hw_yellow, NATIVE_COLOR hw_red)
Map RGB color to discrete BWYR hex 4 color key.
Definition colorconv.h:31
uint8_t g
Definition color.h:34
uint8_t b
Definition color.h:38
uint8_t r
Definition color.h:30