VisuTwin Canvas
C++ 3D Engine — Metal Backend
Loading...
Searching...
No Matches
color.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025-2026 Arnis Lektauers
3#include "color.h"
4#include <stdexcept>
5#include <cmath>
6#include <iomanip>
7#include <sstream>
8
9namespace visutwin::canvas
10{
11 // Static color constants
12 const Color Color::BLACK(0.0f, 0.0f, 0.0f, 1.0f);
13 const Color Color::BLUE(0.0f, 0.0f, 1.0f, 1.0f);
14 const Color Color::CYAN(0.0f, 1.0f, 1.0f, 1.0f);
15 const Color Color::GRAY(0.5f, 0.5f, 0.5f, 1.0f);
16 const Color Color::GREEN(0.0f, 1.0f, 0.0f, 1.0f);
17 const Color Color::MAGENTA(1.0f, 0.0f, 1.0f, 1.0f);
18 const Color Color::RED(1.0f, 0.0f, 0.0f, 1.0f);
19 const Color Color::WHITE(1.0f, 1.0f, 1.0f, 1.0f);
20 const Color Color::YELLOW(1.0f, 1.0f, 0.0f, 1.0f);
21
22 Color::Color(const std::vector<float>& arr)
23 {
24 if (arr.size() >= 3)
25 {
26 r = arr[0];
27 g = arr[1];
28 b = arr[2];
29 a = arr.size() >= 4 ? arr[3] : 1.0f;
30 }
31 else
32 {
33 throw std::invalid_argument("Array must have at least 3 elements");
34 }
35 }
36
37 Color::Color(const float* arr, size_t size)
38 {
39 if (size >= 3 && arr != nullptr)
40 {
41 r = arr[0];
42 g = arr[1];
43 b = arr[2];
44 a = size >= 4 ? arr[3] : 1.0f;
45 }
46 else
47 {
48 throw std::invalid_argument("Array must have at least 3 elements");
49 }
50 }
51
53 {
54 return Color(r, g, b, a);
55 }
56
57 Color& Color::copy(const Color& rhs)
58 {
59 r = rhs.r;
60 g = rhs.g;
61 b = rhs.b;
62 a = rhs.a;
63 return *this;
64 }
65
66 bool Color::equals(const Color& rhs) const
67 {
68 return r == rhs.r && g == rhs.g && b == rhs.b && a == rhs.a;
69 }
70
71 Color& Color::set(float r, float g, float b, float a)
72 {
73 this->r = r;
74 this->g = g;
75 this->b = b;
76 this->a = a;
77 return *this;
78 }
79
80 Color& Color::lerp(const Color& lhs, const Color& rhs, float alpha)
81 {
82 r = lhs.r + alpha * (rhs.r - lhs.r);
83 g = lhs.g + alpha * (rhs.g - lhs.g);
84 b = lhs.b + alpha * (rhs.b - lhs.b);
85 a = lhs.a + alpha * (rhs.a - lhs.a);
86 return *this;
87 }
88
90 {
91 const Color* source = src ? src : this;
92 r = std::pow(source->r, 2.2f);
93 g = std::pow(source->g, 2.2f);
94 b = std::pow(source->b, 2.2f);
95 a = source->a;
96 return *this;
97 }
98
100 {
101 const Color* source = src ? src : this;
102 r = std::pow(source->r, 1.0f / 2.2f);
103 g = std::pow(source->g, 1.0f / 2.2f);
104 b = std::pow(source->b, 1.0f / 2.2f);
105 a = source->a;
106 return *this;
107 }
108
109 Color& Color::mulScalar(float scalar)
110 {
111 r *= scalar;
112 g *= scalar;
113 b *= scalar;
114 return *this;
115 }
116
117 Color& Color::fromString(const std::string& hex)
118 {
119 if (hex.empty() || hex[0] != '#')
120 {
121 throw std::invalid_argument("Invalid hex color format");
122 }
123
124 std::string hexStr = hex.substr(1); // Remove '#'
125 uint32_t i = std::stoul(hexStr, nullptr, 16);
126
127 std::array<uint8_t, 4> bytes;
128 if (hexStr.length() > 6)
129 {
130 auto bytes32 = intToBytes32(i);
131 bytes[0] = bytes32[0];
132 bytes[1] = bytes32[1];
133 bytes[2] = bytes32[2];
134 bytes[3] = bytes32[3];
135 }
136 else
137 {
138 auto bytes24 = intToBytes24(i);
139 bytes[0] = bytes24[0];
140 bytes[1] = bytes24[1];
141 bytes[2] = bytes24[2];
142 bytes[3] = 255;
143 }
144
145 set(bytes[0] / 255.0f, bytes[1] / 255.0f, bytes[2] / 255.0f, bytes[3] / 255.0f);
146 return *this;
147 }
148
149 Color& Color::fromArray(const std::vector<float>& arr, size_t offset)
150 {
151 if (arr.size() > offset) r = arr[offset];
152 if (arr.size() > offset + 1) g = arr[offset + 1];
153 if (arr.size() > offset + 2) b = arr[offset + 2];
154 if (arr.size() > offset + 3) a = arr[offset + 3];
155 return *this;
156 }
157
158 Color& Color::fromArray(const float* arr, size_t size, size_t offset)
159 {
160 if (arr && size > offset) r = arr[offset];
161 if (arr && size > offset + 1) g = arr[offset + 1];
162 if (arr && size > offset + 2) b = arr[offset + 2];
163 if (arr && size > offset + 3) a = arr[offset + 3];
164 return *this;
165 }
166
167 std::string Color::toString(bool alpha, bool asArray) const
168 {
169 // If any component exceeds 1 (HDR), return the color as an array
170 if (asArray || r > 1.0f || g > 1.0f || b > 1.0f)
171 {
172 std::ostringstream oss;
173 oss << std::fixed << std::setprecision(3);
174 oss << r << ", " << g << ", " << b << ", " << a;
175 return oss.str();
176 }
177
178 auto roundToByte = [](float value) -> uint8_t
179 {
180 return static_cast<uint8_t>(std::round(clamp(value, 0.0f, 1.0f) * 255.0f));
181 };
182
183 uint8_t rByte = roundToByte(r);
184 uint8_t gByte = roundToByte(g);
185 uint8_t bByte = roundToByte(b);
186
187 std::ostringstream oss;
188 oss << "#" << std::hex << std::setfill('0');
189 oss << std::setw(2) << static_cast<int>(rByte);
190 oss << std::setw(2) << static_cast<int>(gByte);
191 oss << std::setw(2) << static_cast<int>(bByte);
192
193 if (alpha)
194 {
195 uint8_t aByte = roundToByte(a);
196 oss << std::setw(2) << static_cast<int>(aByte);
197 }
198
199 return oss.str();
200 }
201
202 std::vector<float> Color::toArray(std::vector<float> arr, const size_t offset, const bool alpha) const
203 {
204 // Ensure the array is large enough
205 if (const size_t requiredSize = offset + (alpha ? 4 : 3); arr.size() < requiredSize)
206 {
207 arr.resize(requiredSize);
208 }
209
210 arr[offset] = r;
211 arr[offset + 1] = g;
212 arr[offset + 2] = b;
213 if (alpha)
214 {
215 arr[offset + 3] = a;
216 }
217 return arr;
218 }
219
220 void Color::toArray(float* arr, size_t size, size_t offset, bool alpha) const
221 {
222 if (!arr)
223 {
224 return;
225 }
226
227 if (const size_t requiredSize = offset + (alpha ? 4 : 3); size < requiredSize)
228 {
229 return;
230 }
231
232 arr[offset] = r;
233 arr[offset + 1] = g;
234 arr[offset + 2] = b;
235 if (alpha)
236 {
237 arr[offset + 3] = a;
238 }
239 }
240
241 bool Color::operator==(const Color& other) const
242 {
243 return equals(other);
244 }
245
246 bool Color::operator!=(const Color& other) const
247 {
248 return !equals(other);
249 }
250
251 std::array<uint8_t, 3> Color::intToBytes24(uint32_t i)
252 {
253 uint8_t r = (i >> 16) & 0xFF;
254 uint8_t g = (i >> 8) & 0xFF;
255 uint8_t b = i & 0xFF;
256 return {r, g, b};
257 }
258
259 std::array<uint8_t, 4> Color::intToBytes32(uint32_t i)
260 {
261 const uint8_t r = (i >> 24) & 0xFF;
262 uint8_t g = (i >> 16) & 0xFF;
263 uint8_t b = (i >> 8) & 0xFF;
264 uint8_t a = i & 0xFF;
265 return {r, g, b, a};
266 }
267
268 float Color::clamp(float value, float min, float max)
269 {
270 if (value >= max) return max;
271 if (value <= min) return min;
272 return value;
273 }
274}
RGBA color with floating-point components in [0, 1].
Definition color.h:18
Color(const float r=0.0f, const float g=0.0f, const float b=0.0f, const float a=1.0f)
Definition color.h:27
Color & lerp(const Color &lhs, const Color &rhs, float alpha)
Definition color.cpp:80
static const Color BLACK
Definition color.h:174
std::string toString(bool alpha=false, bool asArray=false) const
Definition color.cpp:167
static const Color YELLOW
Definition color.h:182
Color & mulScalar(float scalar)
Definition color.cpp:109
static const Color MAGENTA
Definition color.h:179
Color & copy(const Color &rhs)
Definition color.cpp:57
static const Color WHITE
Definition color.h:181
bool operator!=(const Color &other) const
Definition color.cpp:246
bool operator==(const Color &other) const
Definition color.cpp:241
Color clone() const
Definition color.cpp:52
static const Color BLUE
Definition color.h:175
bool equals(const Color &rhs) const
Definition color.cpp:66
static const Color CYAN
Definition color.h:176
Color & gamma(const Color *src=nullptr)
Definition color.cpp:99
static const Color GREEN
Definition color.h:178
std::vector< float > toArray(std::vector< float > arr={}, size_t offset=0, bool alpha=true) const
Definition color.cpp:202
Color & linear(const Color *src=nullptr)
Definition color.cpp:89
static const Color RED
Definition color.h:180
static const Color GRAY
Definition color.h:177
Color & fromString(const std::string &hex)
Definition color.cpp:117
Color & fromArray(const std::vector< float > &arr, size_t offset=0)
Definition color.cpp:149
Color & set(float r, float g, float b, float a=1.0f)
Definition color.cpp:71