1use crate::{Paint, Point, Scalar, Transform, Units, simd::f32x4};
2use bytemuck::{Pod, Zeroable};
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Deserializer, Serialize, de::DeserializeSeed};
5use std::{
6 collections::HashMap,
7 fmt,
8 ops::{Add, Mul},
9 str::FromStr,
10 sync::LazyLock,
11};
12
13pub static SVG_COLORS: LazyLock<HashMap<String, RGBA>> = LazyLock::new(|| {
14 let empty = HashMap::new(); include_str!("./svg-colors.txt")
16 .lines()
17 .map(|line| {
18 let mut iter = line.split(' ');
19 let name = iter.next()?;
20 let color = RGBA::from_str_named(iter.next()?, &empty).ok()?;
21 Some((name.to_owned(), color))
22 })
23 .collect::<Option<HashMap<String, RGBA>>>()
24 .expect("failed to parse embedded svg colors")
25});
26
27pub trait Color: Copy {
29 fn blend_over(self, other: Self) -> Self;
31
32 fn with_alpha(self, alpha: Scalar) -> Self;
34
35 fn to_rgba(self) -> [u8; 4];
37
38 fn to_rgb(self) -> [u8; 3] {
40 let [r, g, b, _] = self.to_rgba();
41 [r, g, b]
42 }
43
44 fn luma(self) -> f32 {
46 let [r, g, b] = self.to_rgb();
47 0.2126 * (r as f32 / 255.0) + 0.7152 * (g as f32 / 255.0) + 0.0722 * (b as f32 / 255.0)
48 }
49
50 fn best_contrast(self, c0: Self, c1: Self) -> Self {
52 let luma = self.luma();
53 if (luma - c0.luma()).abs() < (luma - c1.luma()).abs() {
54 c1
55 } else {
56 c0
57 }
58 }
59
60 fn lerp(self, other: Self, t: f32) -> Self;
62}
63
64#[repr(transparent)]
66#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Pod, Zeroable)]
67pub struct RGBA([u8; 4]);
68
69impl RGBA {
70 pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
72 Self([r, g, b, a])
73 }
74
75 pub const fn red(self) -> u8 {
77 self.0[0]
78 }
79
80 pub const fn green(self) -> u8 {
82 self.0[1]
83 }
84
85 pub const fn blue(self) -> u8 {
87 self.0[2]
88 }
89
90 pub const fn alpha(self) -> u8 {
92 self.0[3]
93 }
94
95 pub fn from_str_named(color: &str, colors: &HashMap<String, RGBA>) -> Result<Self, ColorError> {
97 let (color, alpha) = match color.rfind('/') {
99 None => (color, None),
100 Some(alpha_offset) => {
101 let alpha: f32 = color[alpha_offset + 1..]
102 .parse()
103 .map_err(|_| ColorError::InvalidAlpha)?;
104 (&color[..alpha_offset], Some(alpha))
105 }
106 };
107 let rgba = if color.starts_with('#') && (color.len() == 7 || color.len() == 9) {
109 let bytes: &[u8] = color[1..].as_ref();
110 let digit = |byte| match byte {
111 b'A'..=b'F' => Ok(byte - b'A' + 10),
112 b'a'..=b'f' => Ok(byte - b'a' + 10),
113 b'0'..=b'9' => Ok(byte - b'0'),
114 _ => Err(ColorError::HexExpected),
115 };
116 let mut hex = bytes
117 .chunks(2)
118 .map(|pair| Ok((digit(pair[0])? << 4) | digit(pair[1])?));
119 RGBA::new(
120 hex.next().unwrap_or(Ok(0))?,
121 hex.next().unwrap_or(Ok(0))?,
122 hex.next().unwrap_or(Ok(0))?,
123 hex.next().unwrap_or(Ok(255))?,
124 )
125 } else {
126 colors
127 .get(color)
128 .copied()
129 .ok_or_else(|| ColorError::UnkownColor(color.to_owned()))?
130 };
131 match alpha {
133 None => Ok(rgba),
134 Some(alpha) => Ok(RGBA::new(
135 rgba.red(),
136 rgba.green(),
137 rgba.blue(),
138 (rgba.alpha() as f32 * alpha) as u8,
139 )),
140 }
141 }
142}
143
144impl Color for RGBA {
145 fn to_rgba(self) -> [u8; 4] {
146 self.0
147 }
148
149 fn blend_over(self, other: Self) -> Self {
150 LinColor::from(self)
151 .blend_over(LinColor::from(other))
152 .into()
153 }
154
155 fn with_alpha(self, alpha: Scalar) -> Self {
156 LinColor::from(self).with_alpha(alpha).into()
157 }
158
159 fn lerp(self, other: Self, t: f32) -> Self {
160 LinColor::from(self).lerp(LinColor::from(other), t).into()
161 }
162}
163
164impl From<LinColor> for RGBA {
165 #[inline(always)]
166 fn from(lin: LinColor) -> Self {
167 let [r, g, b, _]: [f32; 4] = crate::simd::l2s(lin.unmultiply()).into();
168 RGBA::new(
169 (r * 255.0 + 0.5) as u8,
170 (g * 255.0 + 0.5) as u8,
171 (b * 255.0 + 0.5) as u8,
172 (lin.alpha() * 255.0 + 0.5) as u8,
173 )
174 }
175}
176
177impl From<[u8; 4]> for RGBA {
178 #[inline]
179 fn from(rgba: [u8; 4]) -> Self {
180 RGBA(rgba)
181 }
182}
183
184impl From<[u8; 3]> for RGBA {
185 #[inline]
186 fn from([r, g, b]: [u8; 3]) -> Self {
187 RGBA::new(r, g, b, 255)
188 }
189}
190
191impl fmt::Debug for RGBA {
192 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
193 let [bg_r, bg_g, bg_b] = self.to_rgb();
194 let [fg_r, fg_g, fg_b] = self
195 .best_contrast(RGBA::new(255, 255, 255, 255), RGBA::new(0, 0, 0, 255))
196 .to_rgb();
197 write!(
198 fmt,
199 "\x1b[38;2;{};{};{};48;2;{};{};{}m",
200 fg_r, fg_g, fg_b, bg_r, bg_g, bg_b
201 )?;
202 write!(fmt, "{}", self)?;
203 write!(fmt, "\x1b[m")
204 }
205}
206
207impl fmt::Display for RGBA {
208 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209 let [r, g, b, a] = self.to_rgba();
210 write!(f, "#{:02x}{:02x}{:02x}", r, g, b)?;
211 if a != 255 {
212 write!(f, "{:02x}", a)?;
213 }
214 Ok(())
215 }
216}
217
218impl FromStr for RGBA {
219 type Err = ColorError;
220
221 fn from_str(color: &str) -> Result<Self, Self::Err> {
222 RGBA::from_str_named(color, &SVG_COLORS)
223 }
224}
225
226#[cfg(feature = "serde")]
227impl Serialize for RGBA {
228 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
229 where
230 S: serde::Serializer,
231 {
232 serializer.collect_str(self)
233 }
234}
235
236#[cfg(feature = "serde")]
237#[derive(Clone)]
238pub struct RGBADeserializer<'a> {
239 pub colors: &'a HashMap<String, RGBA>,
240}
241
242#[cfg(feature = "serde")]
243impl<'de> Deserialize<'de> for RGBA {
244 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
245 where
246 D: Deserializer<'de>,
247 {
248 RGBADeserializer {
249 colors: &SVG_COLORS,
250 }
251 .deserialize(deserializer)
252 }
253}
254
255#[cfg(feature = "serde")]
256impl<'de> DeserializeSeed<'de> for RGBADeserializer<'_> {
257 type Value = RGBA;
258
259 fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
260 where
261 D: Deserializer<'de>,
262 {
263 let color = std::borrow::Cow::<'de, str>::deserialize(deserializer)?;
264 RGBA::from_str_named(color.as_ref(), self.colors).map_err(serde::de::Error::custom)
265 }
266}
267
268#[repr(transparent)]
270#[derive(Debug, Clone, Copy, PartialEq, Default, Pod, Zeroable)]
271pub struct LinColor(crate::simd::f32x4);
272
273impl LinColor {
274 #[inline(always)]
275 pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
276 LinColor(f32x4::new(r, g, b, a))
277 }
278
279 #[inline(always)]
280 pub fn red(self) -> f32 {
281 self.0.x0()
282 }
283
284 #[inline(always)]
285 pub fn green(self) -> f32 {
286 self.0.x1()
287 }
288
289 #[inline(always)]
290 pub fn blue(self) -> f32 {
291 self.0.x2()
292 }
293
294 #[inline(always)]
295 pub fn alpha(self) -> f32 {
296 self.0.x3()
297 }
298
299 #[inline(always)]
300 pub fn distance(self, other: Self) -> f32 {
301 let diff = self.unmultiply() - other.unmultiply();
302 diff.dot(diff).sqrt()
303 }
304
305 #[inline(always)]
308 pub fn unmultiply(self) -> f32x4 {
309 let alpha = self.alpha();
310 if alpha <= 1e-6 {
311 f32x4::zero()
313 } else {
314 self.0 / f32x4::splat(alpha)
315 }
316 }
317
318 #[inline(always)]
322 pub(crate) fn into_srgb(self) -> Self {
323 Self(crate::simd::l2s(self.unmultiply()) * f32x4::splat(self.alpha()))
324 }
325
326 #[inline(always)]
330 pub(crate) fn into_linear(self) -> Self {
331 Self(crate::simd::s2l(self.unmultiply()) * f32x4::splat(self.alpha()))
332 }
333}
334
335impl Color for LinColor {
336 #[inline(always)]
337 fn to_rgba(self) -> [u8; 4] {
338 RGBA::from(self).to_rgba()
339 }
340
341 #[inline(always)]
342 fn blend_over(self, other: Self) -> Self {
343 other + self * (1.0 - other.alpha())
344 }
345
346 #[inline(always)]
347 fn with_alpha(self, alpha: Scalar) -> Self {
348 self * (alpha as f32)
349 }
350
351 #[inline(always)]
352 fn lerp(self, other: Self, t: f32) -> Self {
353 other * t + self * (1.0 - t)
354 }
355}
356
357impl Paint for LinColor {
358 fn at(&self, _: Point) -> LinColor {
359 *self
360 }
361
362 fn units(&self) -> Option<Units> {
363 None
364 }
365
366 fn transform(&self) -> Transform {
367 Transform::identity()
368 }
369
370 #[cfg(feature = "serde")]
371 fn to_json(&self) -> Result<serde_json::Value, crate::SvgParserError> {
372 Ok(serde_json::Value::String(self.to_string()))
373 }
374}
375
376impl Add<Self> for LinColor {
377 type Output = Self;
378
379 #[inline(always)]
380 fn add(self, other: Self) -> Self::Output {
381 Self(self.0 + other.0)
382 }
383}
384
385impl Mul<f32> for LinColor {
386 type Output = Self;
387
388 #[inline(always)]
389 fn mul(self, scale: f32) -> Self::Output {
390 Self(self.0 * scale)
391 }
392}
393
394impl From<RGBA> for LinColor {
395 fn from(color: RGBA) -> Self {
396 let a = color.alpha() as f32 / 255.0;
401 let r = srgb_to_linear(color.red() as f32 / 255.0) * a;
402 let g = srgb_to_linear(color.green() as f32 / 255.0) * a;
403 let b = srgb_to_linear(color.blue() as f32 / 255.0) * a;
404 LinColor::new(r, g, b, a)
405 }
406}
407
408impl From<LinColor> for [f32; 4] {
409 fn from(color: LinColor) -> Self {
410 color.0.into()
411 }
412}
413
414impl FromStr for LinColor {
415 type Err = ColorError;
416
417 fn from_str(color: &str) -> Result<Self, Self::Err> {
418 Ok(RGBA::from_str(color)?.into())
419 }
420}
421
422impl fmt::Display for LinColor {
423 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
424 RGBA::from(*self).fmt(f)
425 }
426}
427
428impl Color for Scalar {
429 fn to_rgba(self) -> [u8; 4] {
430 let color = (linear_to_srgb(1.0 - (self as f32)) * 255.0 + 0.5) as u8;
431 [color, color, color, 255]
432 }
433
434 fn blend_over(self, other: Self) -> Self {
435 other + self * (1.0 - other)
436 }
437
438 fn with_alpha(self, alpha: Scalar) -> Self {
439 self * alpha
440 }
441
442 fn lerp(self, other: Self, t: f32) -> Self {
443 let t = t as Scalar;
444 self * (1.0 - t) + other * t
445 }
446}
447
448#[inline]
465pub fn linear_to_srgb(x0: f32) -> f32 {
466 if x0 <= 0.0031308 {
467 x0 * 12.92
468 } else {
469 let x1 = x0.sqrt();
473 let x2 = x1.sqrt();
474 let x3 = x2.sqrt();
475 -0.01848558 * x0 + 0.6445592 * x1 + 0.70994765 * x2 - 0.33605254 * x3
476 }
477}
478
479#[inline]
480pub fn srgb_to_linear(value: f32) -> f32 {
481 if value <= 0.04045 {
482 value / 12.92
483 } else {
484 ((value + 0.055) / 1.055).powf(2.4)
485 }
486}
487
488#[derive(Debug, Clone)]
489pub enum ColorError {
490 HexExpected,
491 InvalidAlpha,
492 UnkownColor(String),
493}
494
495impl fmt::Display for ColorError {
496 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
497 write!(f, "Color format (#RRGGBB(AA)?|<name>)(/<float alpha>)? :")?;
498 match self {
499 ColorError::HexExpected => write!(f, "Hex value expected"),
500 ColorError::InvalidAlpha => write!(f, "Alpha must be float"),
501 ColorError::UnkownColor(name) => write!(f, "Unkown named color: {}", name),
502 }
503 }
504}
505
506impl std::error::Error for ColorError {}
507
508#[cfg(test)]
509mod tests {
510 use super::*;
511 use crate::assert_approx_eq;
512
513 #[test]
514 fn test_color_rgba() {
515 let c = RGBA::new(1, 2, 3, 4);
516 assert_eq!([1, 2, 3, 4], c.to_rgba());
517 assert_eq!(1, c.red());
518 assert_eq!(2, c.green());
519 assert_eq!(3, c.blue());
520 assert_eq!(4, c.alpha());
521 }
522
523 #[test]
524 fn test_color_parse() -> Result<(), ColorError> {
525 assert_eq!(RGBA::new(1, 2, 3, 4), "#01020304".parse::<RGBA>()?);
526 assert_eq!(RGBA::new(170, 187, 204, 255), "#aabbcc".parse::<RGBA>()?);
527 assert_eq!(RGBA::new(0, 0, 0, 255), "#000000".parse::<RGBA>()?);
528 assert_eq!(RGBA::new(1, 2, 3, 63), "#010203/.25".parse::<RGBA>()?);
529 assert_eq!(RGBA::new(0xff, 0x7f, 0x50, 0xff), "coral".parse::<RGBA>()?);
530 assert_eq!(
531 RGBA::new(0xfe, 0x80, 0x19, 0xff),
532 "gruv-orange-2".parse::<RGBA>()?
533 );
534 assert_eq!(SVG_COLORS.len(), 185);
535 Ok(())
536 }
537
538 #[test]
539 fn test_conversion() -> Result<(), ColorError> {
540 let c: RGBA = "#ff804010".parse()?;
541 let l: LinColor = c.into();
542 let r: RGBA = l.into();
543 assert_eq!(c, r);
544 Ok(())
545 }
546
547 #[test]
548 fn test_lin_and_srgb() {
549 for i in 0..255 {
550 let v = i as f32 / 255.0;
551 assert_approx_eq!(v, linear_to_srgb(srgb_to_linear(v)), 1e-4);
552 assert_approx_eq!(v, srgb_to_linear(linear_to_srgb(v)), 1e-4);
553 }
554 }
555
556 #[test]
557 fn test_display_parse() -> Result<(), ColorError> {
558 let c: RGBA = "#01020304".parse()?;
559 assert_eq!(c, RGBA::new(1, 2, 3, 4));
560 assert_eq!(c.to_string(), "#01020304");
561
562 let c: RGBA = "#010203".parse()?;
563 assert_eq!(c, RGBA::new(1, 2, 3, 255));
564 assert_eq!(c.to_string(), "#010203");
565
566 Ok(())
567 }
568
569 #[cfg(feature = "serde")]
570 #[test]
571 fn test_serde() -> Result<(), Box<dyn std::error::Error>> {
572 use serde_json::de::StrRead;
573
574 let mut colors = HashMap::new();
575 colors.insert("aqua".to_owned(), "#008080".parse()?);
576
577 let mut deserializer = serde_json::Deserializer::new(StrRead::new("\"aqua/.5\""));
578 let color = RGBADeserializer { colors: &colors }.deserialize(&mut deserializer)?;
579 assert_eq!(color, "#0080807f".parse()?);
580
581 Ok(())
582 }
583}