libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
color.h
Go to the documentation of this file.
1
8#pragma once
9
10
11#include <promeki/config.h>
12#if PROMEKI_ENABLE_CORE
13#include <cstdint>
14#include <promeki/colormodel.h>
15#include <promeki/list.h>
16#include <promeki/namespace.h>
17#include <promeki/result.h>
18#include <promeki/string.h>
19#include <promeki/datatype.h>
20
21PROMEKI_NAMESPACE_BEGIN
22
23class DataStream;
24
112class Color {
113 public:
114 PROMEKI_DATATYPE(Color, DataTypeColor, 1)
115
116
117 Error writeToStream(DataStream &s) const;
119 template <uint32_t V> static Result<Color> readFromStream(DataStream &s);
120
126 enum StringFormat {
127 ModelFormat,
128 HexFormat,
129 CSVFormat,
130 FloatFormat,
131 };
132
136 enum AlphaMode {
137 AlphaAuto,
138 AlphaAlways,
139 AlphaNever,
140 };
141
144 static const Color Black;
145 static const Color White;
146 static const Color Red;
147 static const Color Green;
148 static const Color Blue;
149 static const Color Yellow;
150 static const Color Cyan;
151 static const Color Magenta;
152 static const Color DarkGray;
153 static const Color LightGray;
154 static const Color Orange;
155 static const Color Transparent;
156 static const Color Ignored;
158
159 // --- Static factory helpers (one per well-known model) ---
160
162 static Color srgb(float r, float g, float b, float a = 1.0f) {
163 return Color(ColorModel::sRGB, r, g, b, a);
164 }
165
167 static Color linearSrgb(float r, float g, float b, float a = 1.0f) {
168 return Color(ColorModel::LinearSRGB, r, g, b, a);
169 }
170
172 static Color rec709(float r, float g, float b, float a = 1.0f) {
173 return Color(ColorModel::Rec709, r, g, b, a);
174 }
175
177 static Color hsv(float h, float s, float v, float a = 1.0f) {
178 return Color(ColorModel::HSV_sRGB, h, s, v, a);
179 }
180
182 static Color hsl(float h, float s, float l, float a = 1.0f) {
183 return Color(ColorModel::HSL_sRGB, h, s, l, a);
184 }
185
187 static Color ycbcr709(float y, float cb, float cr, float a = 1.0f) {
188 return Color(ColorModel::YCbCr_Rec709, y, cb, cr, a);
189 }
190
192 static Color ycbcr601(float y, float cb, float cr, float a = 1.0f) {
193 return Color(ColorModel::YCbCr_Rec601, y, cb, cr, a);
194 }
195
197 static Color xyz(float x, float y, float z, float a = 1.0f) {
198 return Color(ColorModel::CIEXYZ, x, y, z, a);
199 }
200
202 static Color lab(float l, float a, float b, float alpha = 1.0f) {
203 return Color(ColorModel::CIELab, l, a, b, alpha);
204 }
205
211 static Color fromHex(const String &hex);
212
229 static Result<Color> fromString(const String &str);
230
240 static Color fromNative(const ColorModel &model, float n0, float n1, float n2, float n3 = 1.0f);
241
242 // --- Constructors ---
243
245 Color() = default;
246
255 Color(const ColorModel &model, float c0, float c1, float c2, float c3 = 1.0f)
256 : _c{c0, c1, c2, c3}, _model(model) {}
257
266 Color(ColorModel::ID id, float c0, float c1, float c2, float c3 = 1.0f)
267 : _c{c0, c1, c2, c3}, _model(id) {}
268
274 Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255)
275 : _c{r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f}, _model(ColorModel::sRGB) {}
276
277 // --- State ---
278
280 bool isValid() const { return _model.isValid(); }
281
283 const ColorModel &model() const { return _model; }
284
285 // --- Generic component access ---
286
288 float comp(size_t index) const { return index < 4 ? _c[index] : 0.0f; }
289
291 void setComp(size_t index, float val) {
292 if (index < 4) _c[index] = val;
293 }
294
296 float alpha() const { return _c[3]; }
297
299 float a() const { return _c[3]; }
300
302 void setAlpha(float val) { _c[3] = val; }
303
305 void setA(float val) { _c[3] = val; }
306
307 // --- RGB accessors (model must be TypeRGB) ---
308
310 float r() const { return _c[0]; }
311
313 float g() const { return _c[1]; }
314
316 float b() const { return _c[2]; }
317
319 void setR(float val) { _c[0] = val; }
320
322 void setG(float val) { _c[1] = val; }
323
325 void setB(float val) { _c[2] = val; }
326
327 // --- 8-bit sRGB accessors (convert to sRGB if needed) ---
328
330 uint8_t r8() const;
331
333 uint8_t g8() const;
334
336 uint8_t b8() const;
337
339 uint8_t a8() const;
340
341 // --- HSV accessors ---
342
344 float h() const { return _c[0]; }
345
347 float s() const { return _c[1]; }
348
350 float v() const { return _c[2]; }
351
353 float l() const { return _c[0]; }
354
355 // --- YCbCr accessors ---
356
358 float y() const { return _c[0]; }
359
361 float cb() const { return _c[1]; }
362
364 float cr() const { return _c[2]; }
365
366 // --- Conversion ---
367
373 Color convert(const ColorModel &target) const;
374
376 Color toRGB() const;
377
379 Color toLinearRGB() const;
380
382 Color toHSV() const;
383
385 Color toHSL() const;
386
388 Color toYCbCr709() const;
389
391 Color toXYZ() const;
392
394 Color toLab() const;
395
396 // --- String conversion ---
397
410 String toString(StringFormat fmt = ModelFormat, AlphaMode alpha = AlphaAuto) const;
411
420 String toHex(bool includeAlpha = false) const;
421
422 // --- Color operations ---
423
430 Color lerp(const Color &other, double t) const;
431
437 Color inverted() const;
438
444 double luminance() const;
445
451 Color contrastingBW() const;
452
458 Color complementary() const;
459
465 float toNative(size_t comp) const;
466
473 bool isClose(const Color &other, float epsilon = 1e-5f) const;
474
476 bool operator==(const Color &other) const {
477 return _model == other._model && _c[0] == other._c[0] && _c[1] == other._c[1] &&
478 _c[2] == other._c[2] && _c[3] == other._c[3];
479 }
480
482 bool operator!=(const Color &other) const { return !(*this == other); }
483
488 using List = ::promeki::List<Color>;
489
512 size_t nearestPaletteIndex(const Color *palette, size_t n) const;
513
515 size_t nearestPaletteIndex(const List &palette) const {
516 return nearestPaletteIndex(palette.data(), palette.size());
517 }
518
522 Color nearestPaletteColor(const List &palette) const {
523 if (palette.isEmpty()) return Color();
524 return palette[nearestPaletteIndex(palette)];
525 }
526
527 private:
528 float _c[4] = {0.0f, 0.0f, 0.0f, 0.0f};
529 ColorModel _model;
530
531 // Helper: get sRGB version of this color (for string/hex output)
532 Color ensureSRGB() const;
533};
534
535inline const Color Color::Black{(uint8_t)0, (uint8_t)0, (uint8_t)0};
536inline const Color Color::White{(uint8_t)255, (uint8_t)255, (uint8_t)255};
537inline const Color Color::Red{(uint8_t)255, (uint8_t)0, (uint8_t)0};
538inline const Color Color::Green{(uint8_t)0, (uint8_t)255, (uint8_t)0};
539inline const Color Color::Blue{(uint8_t)0, (uint8_t)0, (uint8_t)255};
540inline const Color Color::Yellow{(uint8_t)255, (uint8_t)255, (uint8_t)0};
541inline const Color Color::Cyan{(uint8_t)0, (uint8_t)255, (uint8_t)255};
542inline const Color Color::Magenta{(uint8_t)255, (uint8_t)0, (uint8_t)255};
543inline const Color Color::DarkGray{(uint8_t)64, (uint8_t)64, (uint8_t)64};
544inline const Color Color::LightGray{(uint8_t)192, (uint8_t)192, (uint8_t)192};
545inline const Color Color::Orange{(uint8_t)255, (uint8_t)165, (uint8_t)0};
546inline const Color Color::Transparent{(uint8_t)0, (uint8_t)0, (uint8_t)0, (uint8_t)0};
547inline const Color Color::Ignored{};
548
549PROMEKI_NAMESPACE_END
550
551PROMEKI_FORMAT_VIA_TOSTRING(promeki::Color);
552
553#endif // PROMEKI_ENABLE_CORE