1use crate::basics::uround;
7use crate::color::Rgba8;
8use crate::dda_line::DdaLineInterpolator;
9
10pub trait ColorFunction {
18 type Color;
19
20 fn size(&self) -> usize;
21 fn get(&self, index: usize) -> Self::Color;
22}
23
24struct ColorInterpolatorGeneric<C> {
32 c1: C,
33 c2: C,
34 len: u32,
35 count: u32,
36}
37
38impl<C: Clone> ColorInterpolatorGeneric<C> {
39 fn new(c1: &C, c2: &C, len: u32) -> Self {
40 Self {
41 c1: c1.clone(),
42 c2: c2.clone(),
43 len,
44 count: 0,
45 }
46 }
47
48 fn inc(&mut self) {
49 self.count += 1;
50 }
51}
52
53impl ColorInterpolatorGeneric<Rgba8> {
54 fn color(&self) -> Rgba8 {
55 self.c1
56 .gradient(&self.c2, self.count as f64 / self.len as f64)
57 }
58}
59
60struct ColorInterpolatorRgba8 {
68 r: DdaLineInterpolator<14, 0>,
69 g: DdaLineInterpolator<14, 0>,
70 b: DdaLineInterpolator<14, 0>,
71 a: DdaLineInterpolator<14, 0>,
72}
73
74impl ColorInterpolatorRgba8 {
75 fn new(c1: &Rgba8, c2: &Rgba8, len: u32) -> Self {
76 Self {
77 r: DdaLineInterpolator::new(c1.r as i32, c2.r as i32, len),
78 g: DdaLineInterpolator::new(c1.g as i32, c2.g as i32, len),
79 b: DdaLineInterpolator::new(c1.b as i32, c2.b as i32, len),
80 a: DdaLineInterpolator::new(c1.a as i32, c2.a as i32, len),
81 }
82 }
83
84 fn inc(&mut self) {
85 self.r.inc();
86 self.g.inc();
87 self.b.inc();
88 self.a.inc();
89 }
90
91 fn color(&self) -> Rgba8 {
92 Rgba8::new(
93 self.r.y() as u32,
94 self.g.y() as u32,
95 self.b.y() as u32,
96 self.a.y() as u32,
97 )
98 }
99}
100
101#[derive(Clone)]
107struct ColorPoint {
108 offset: f64,
109 color: Rgba8,
110}
111
112impl ColorPoint {
113 fn new(offset: f64, color: Rgba8) -> Self {
114 Self {
115 offset: offset.clamp(0.0, 1.0),
116 color,
117 }
118 }
119}
120
121pub struct GradientLut {
128 color_profile: Vec<ColorPoint>,
129 color_lut: Vec<Rgba8>,
130 lut_size: usize,
131 use_fast_interpolator: bool,
132}
133
134impl GradientLut {
135 pub fn new(lut_size: usize) -> Self {
137 Self {
138 color_profile: Vec::new(),
139 color_lut: vec![Rgba8::default(); lut_size],
140 lut_size,
141 use_fast_interpolator: true,
142 }
143 }
144
145 pub fn new_default() -> Self {
147 Self::new(256)
148 }
149
150 pub fn set_use_fast_interpolator(&mut self, fast: bool) {
152 self.use_fast_interpolator = fast;
153 }
154
155 pub fn remove_all(&mut self) {
157 self.color_profile.clear();
158 }
159
160 pub fn add_color(&mut self, offset: f64, color: Rgba8) {
162 self.color_profile.push(ColorPoint::new(offset, color));
163 }
164
165 pub fn build_lut(&mut self) {
170 self.color_profile
172 .sort_by(|a, b| a.offset.partial_cmp(&b.offset).unwrap());
173 self.color_profile
175 .dedup_by(|a, b| (a.offset - b.offset).abs() < 1e-10);
176
177 if self.color_profile.len() < 2 {
178 return;
179 }
180
181 let size = self.lut_size;
182 let mut start = uround(self.color_profile[0].offset * size as f64) as usize;
183
184 let c = self.color_profile[0].color;
186 for i in 0..start.min(size) {
187 self.color_lut[i] = c;
188 }
189
190 for i in 1..self.color_profile.len() {
192 let end = uround(self.color_profile[i].offset * size as f64) as usize;
193 let seg_len = if end > start { (end - start - 1).max(1) } else { 1 };
202
203 if self.use_fast_interpolator {
204 let mut ci = ColorInterpolatorRgba8::new(
205 &self.color_profile[i - 1].color,
206 &self.color_profile[i].color,
207 seg_len as u32,
208 );
209 while start < end && start < size {
210 self.color_lut[start] = ci.color();
211 ci.inc();
212 start += 1;
213 }
214 } else {
215 let mut ci = ColorInterpolatorGeneric::new(
216 &self.color_profile[i - 1].color,
217 &self.color_profile[i].color,
218 seg_len as u32,
219 );
220 while start < end && start < size {
221 self.color_lut[start] = ci.color();
222 ci.inc();
223 start += 1;
224 }
225 }
226 }
227
228 let c = self.color_profile.last().unwrap().color;
230 let mut end = start;
231 while end < size {
232 self.color_lut[end] = c;
233 end += 1;
234 }
235 }
236}
237
238impl ColorFunction for GradientLut {
239 type Color = Rgba8;
240
241 fn size(&self) -> usize {
242 self.lut_size
243 }
244
245 #[inline]
246 fn get(&self, index: usize) -> Rgba8 {
247 self.color_lut[index]
248 }
249}
250
251pub struct GradientLinearColor {
261 c1: Rgba8,
262 c2: Rgba8,
263 size: usize,
264}
265
266impl GradientLinearColor {
267 pub fn new(c1: Rgba8, c2: Rgba8, size: usize) -> Self {
268 Self { c1, c2, size }
269 }
270
271 pub fn colors(&mut self, c1: Rgba8, c2: Rgba8) {
272 self.c1 = c1;
273 self.c2 = c2;
274 }
275}
276
277impl ColorFunction for GradientLinearColor {
278 type Color = Rgba8;
279
280 fn size(&self) -> usize {
281 self.size
282 }
283
284 fn get(&self, index: usize) -> Rgba8 {
285 self.c1
286 .gradient(&self.c2, index as f64 / (self.size - 1).max(1) as f64)
287 }
288}
289
290#[cfg(test)]
295mod tests {
296 use super::*;
297
298 #[test]
299 fn test_gradient_lut_new() {
300 let lut = GradientLut::new_default();
301 assert_eq!(lut.size(), 256);
302 }
303
304 #[test]
305 fn test_gradient_lut_two_stops() {
306 let mut lut = GradientLut::new_default();
307 lut.add_color(0.0, Rgba8::new(255, 0, 0, 255));
308 lut.add_color(1.0, Rgba8::new(0, 0, 255, 255));
309 lut.build_lut();
310
311 let c0 = lut.get(0);
313 assert_eq!(c0.r, 255);
314 assert_eq!(c0.b, 0);
315
316 let c255 = lut.get(255);
319 assert_eq!(c255.r, 0, "c255.r={}", c255.r);
320 assert_eq!(c255.b, 255, "c255.b={}", c255.b);
321
322 let c128 = lut.get(128);
324 assert!(c128.r > 50 && c128.r < 200, "Mid r={}", c128.r);
325 assert!(c128.b > 50 && c128.b < 200, "Mid b={}", c128.b);
326 }
327
328 #[test]
347 fn gradient_lut_two_stop_ramp_reaches_its_end_color_exactly() {
348 let mut lut = GradientLut::new_default();
349 lut.add_color(0.0, Rgba8::new(0, 0, 0, 255)); lut.add_color(1.0, Rgba8::new(255, 255, 255, 255)); lut.build_lut();
352
353 let first = lut.get(0);
355 assert_eq!((first.r, first.g, first.b), (0, 0, 0), "first = {first:?}");
356
357 let last = lut.get(255);
359 assert_eq!(
360 (last.r, last.g, last.b),
361 (255, 255, 255),
362 "the ramp must reach pure white; got {last:?}",
363 );
364 }
365
366 #[test]
367 fn test_gradient_lut_three_stops() {
368 let mut lut = GradientLut::new_default();
369 lut.add_color(0.0, Rgba8::new(255, 0, 0, 255));
370 lut.add_color(0.5, Rgba8::new(0, 255, 0, 255));
371 lut.add_color(1.0, Rgba8::new(0, 0, 255, 255));
372 lut.build_lut();
373
374 assert_eq!(lut.get(0).r, 255);
376 assert!(lut.get(255).b >= 254, "last.b={}", lut.get(255).b);
380 let mid = lut.get(128);
382 assert!(mid.g > 128, "Mid green={}", mid.g);
383 }
384
385 #[test]
386 fn test_gradient_lut_remove_all() {
387 let mut lut = GradientLut::new_default();
388 lut.add_color(0.0, Rgba8::new(255, 0, 0, 255));
389 lut.remove_all();
390 lut.add_color(0.0, Rgba8::new(0, 255, 0, 255));
391 lut.add_color(1.0, Rgba8::new(0, 255, 0, 255));
392 lut.build_lut();
393 assert_eq!(lut.get(0).g, 255);
394 }
395
396 #[test]
397 fn test_gradient_lut_generic_interpolator() {
398 let mut lut = GradientLut::new_default();
399 lut.set_use_fast_interpolator(false);
400 lut.add_color(0.0, Rgba8::new(255, 0, 0, 255));
401 lut.add_color(1.0, Rgba8::new(0, 0, 255, 255));
402 lut.build_lut();
403
404 let c0 = lut.get(0);
405 assert_eq!(c0.r, 255);
406 let c255 = lut.get(255);
407 assert_eq!(c255.b, 255);
411 assert_eq!(c255.r, 0);
412 }
413
414 #[test]
415 fn test_gradient_lut_custom_size() {
416 let mut lut = GradientLut::new(64);
417 lut.add_color(0.0, Rgba8::new(0, 0, 0, 255));
418 lut.add_color(1.0, Rgba8::new(255, 255, 255, 255));
419 lut.build_lut();
420 assert_eq!(lut.size(), 64);
421 assert_eq!(lut.get(0).r, 0);
422 assert!(lut.get(63).r >= 244, "last.r={}", lut.get(63).r);
423 }
424
425 #[test]
426 fn test_gradient_linear_color() {
427 let gc = GradientLinearColor::new(
428 Rgba8::new(0, 0, 0, 255),
429 Rgba8::new(255, 255, 255, 255),
430 256,
431 );
432 assert_eq!(gc.size(), 256);
433
434 let c0 = gc.get(0);
435 assert_eq!(c0.r, 0);
436
437 let c255 = gc.get(255);
438 assert_eq!(c255.r, 255);
439
440 let c128 = gc.get(128);
441 assert!(c128.r > 100 && c128.r < 160, "c128.r={}", c128.r);
442 }
443
444 #[test]
445 fn test_gradient_lut_unsorted_stops() {
446 let mut lut = GradientLut::new_default();
447 lut.add_color(1.0, Rgba8::new(0, 0, 255, 255));
448 lut.add_color(0.0, Rgba8::new(255, 0, 0, 255));
449 lut.build_lut();
450
451 assert_eq!(lut.get(0).r, 255);
453 assert_eq!(lut.get(255).b, 255);
455 assert_eq!(lut.get(255).r, 0);
456 }
457
458 #[test]
459 fn test_color_interpolator_rgba8_fast() {
460 let c1 = Rgba8::new(0, 0, 0, 255);
461 let c2 = Rgba8::new(255, 255, 255, 255);
462 let mut ci = ColorInterpolatorRgba8::new(&c1, &c2, 10);
463
464 let first = ci.color();
465 assert_eq!(first.r, 0);
466
467 for _ in 0..10 {
468 ci.inc();
469 }
470 let last = ci.color();
471 assert_eq!(last.r, 255);
472 }
473
474 #[test]
475 fn test_gradient_linear_color_set_colors() {
476 let mut gc = GradientLinearColor::new(
477 Rgba8::new(0, 0, 0, 255),
478 Rgba8::new(255, 255, 255, 255),
479 256,
480 );
481 gc.colors(Rgba8::new(255, 0, 0, 255), Rgba8::new(0, 255, 0, 255));
482 assert_eq!(gc.get(0).r, 255);
483 assert_eq!(gc.get(0).g, 0);
484 assert_eq!(gc.get(255).r, 0);
485 assert_eq!(gc.get(255).g, 255);
486 }
487}