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 } else { 1 };
194
195 if self.use_fast_interpolator {
196 let mut ci = ColorInterpolatorRgba8::new(
197 &self.color_profile[i - 1].color,
198 &self.color_profile[i].color,
199 seg_len as u32,
200 );
201 while start < end && start < size {
202 self.color_lut[start] = ci.color();
203 ci.inc();
204 start += 1;
205 }
206 } else {
207 let mut ci = ColorInterpolatorGeneric::new(
208 &self.color_profile[i - 1].color,
209 &self.color_profile[i].color,
210 seg_len as u32,
211 );
212 while start < end && start < size {
213 self.color_lut[start] = ci.color();
214 ci.inc();
215 start += 1;
216 }
217 }
218 }
219
220 let c = self.color_profile.last().unwrap().color;
222 let mut end = start;
223 while end < size {
224 self.color_lut[end] = c;
225 end += 1;
226 }
227 }
228}
229
230impl ColorFunction for GradientLut {
231 type Color = Rgba8;
232
233 fn size(&self) -> usize {
234 self.lut_size
235 }
236
237 fn get(&self, index: usize) -> Rgba8 {
238 self.color_lut[index]
239 }
240}
241
242pub struct GradientLinearColor {
252 c1: Rgba8,
253 c2: Rgba8,
254 size: usize,
255}
256
257impl GradientLinearColor {
258 pub fn new(c1: Rgba8, c2: Rgba8, size: usize) -> Self {
259 Self { c1, c2, size }
260 }
261
262 pub fn colors(&mut self, c1: Rgba8, c2: Rgba8) {
263 self.c1 = c1;
264 self.c2 = c2;
265 }
266}
267
268impl ColorFunction for GradientLinearColor {
269 type Color = Rgba8;
270
271 fn size(&self) -> usize {
272 self.size
273 }
274
275 fn get(&self, index: usize) -> Rgba8 {
276 self.c1
277 .gradient(&self.c2, index as f64 / (self.size - 1).max(1) as f64)
278 }
279}
280
281#[cfg(test)]
286mod tests {
287 use super::*;
288
289 #[test]
290 fn test_gradient_lut_new() {
291 let lut = GradientLut::new_default();
292 assert_eq!(lut.size(), 256);
293 }
294
295 #[test]
296 fn test_gradient_lut_two_stops() {
297 let mut lut = GradientLut::new_default();
298 lut.add_color(0.0, Rgba8::new(255, 0, 0, 255));
299 lut.add_color(1.0, Rgba8::new(0, 0, 255, 255));
300 lut.build_lut();
301
302 let c0 = lut.get(0);
304 assert_eq!(c0.r, 255);
305 assert_eq!(c0.b, 0);
306
307 let c255 = lut.get(255);
310 assert!(c255.r <= 3, "c255.r={}", c255.r);
311 assert!(c255.b >= 252, "c255.b={}", c255.b);
312
313 let c128 = lut.get(128);
315 assert!(c128.r > 50 && c128.r < 200, "Mid r={}", c128.r);
316 assert!(c128.b > 50 && c128.b < 200, "Mid b={}", c128.b);
317 }
318
319 #[test]
320 fn test_gradient_lut_three_stops() {
321 let mut lut = GradientLut::new_default();
322 lut.add_color(0.0, Rgba8::new(255, 0, 0, 255));
323 lut.add_color(0.5, Rgba8::new(0, 255, 0, 255));
324 lut.add_color(1.0, Rgba8::new(0, 0, 255, 255));
325 lut.build_lut();
326
327 assert_eq!(lut.get(0).r, 255);
329 assert!(lut.get(255).b >= 248, "last.b={}", lut.get(255).b);
331 let mid = lut.get(128);
333 assert!(mid.g > 128, "Mid green={}", mid.g);
334 }
335
336 #[test]
337 fn test_gradient_lut_remove_all() {
338 let mut lut = GradientLut::new_default();
339 lut.add_color(0.0, Rgba8::new(255, 0, 0, 255));
340 lut.remove_all();
341 lut.add_color(0.0, Rgba8::new(0, 255, 0, 255));
342 lut.add_color(1.0, Rgba8::new(0, 255, 0, 255));
343 lut.build_lut();
344 assert_eq!(lut.get(0).g, 255);
345 }
346
347 #[test]
348 fn test_gradient_lut_generic_interpolator() {
349 let mut lut = GradientLut::new_default();
350 lut.set_use_fast_interpolator(false);
351 lut.add_color(0.0, Rgba8::new(255, 0, 0, 255));
352 lut.add_color(1.0, Rgba8::new(0, 0, 255, 255));
353 lut.build_lut();
354
355 let c0 = lut.get(0);
356 assert_eq!(c0.r, 255);
357 let c255 = lut.get(255);
358 assert!(c255.b >= 252, "c255.b={}", c255.b);
359 }
360
361 #[test]
362 fn test_gradient_lut_custom_size() {
363 let mut lut = GradientLut::new(64);
364 lut.add_color(0.0, Rgba8::new(0, 0, 0, 255));
365 lut.add_color(1.0, Rgba8::new(255, 255, 255, 255));
366 lut.build_lut();
367 assert_eq!(lut.size(), 64);
368 assert_eq!(lut.get(0).r, 0);
369 assert!(lut.get(63).r >= 244, "last.r={}", lut.get(63).r);
370 }
371
372 #[test]
373 fn test_gradient_linear_color() {
374 let gc = GradientLinearColor::new(
375 Rgba8::new(0, 0, 0, 255),
376 Rgba8::new(255, 255, 255, 255),
377 256,
378 );
379 assert_eq!(gc.size(), 256);
380
381 let c0 = gc.get(0);
382 assert_eq!(c0.r, 0);
383
384 let c255 = gc.get(255);
385 assert_eq!(c255.r, 255);
386
387 let c128 = gc.get(128);
388 assert!(c128.r > 100 && c128.r < 160, "c128.r={}", c128.r);
389 }
390
391 #[test]
392 fn test_gradient_lut_unsorted_stops() {
393 let mut lut = GradientLut::new_default();
394 lut.add_color(1.0, Rgba8::new(0, 0, 255, 255));
395 lut.add_color(0.0, Rgba8::new(255, 0, 0, 255));
396 lut.build_lut();
397
398 assert_eq!(lut.get(0).r, 255);
400 assert!(lut.get(255).b >= 252, "last.b={}", lut.get(255).b);
401 }
402
403 #[test]
404 fn test_color_interpolator_rgba8_fast() {
405 let c1 = Rgba8::new(0, 0, 0, 255);
406 let c2 = Rgba8::new(255, 255, 255, 255);
407 let mut ci = ColorInterpolatorRgba8::new(&c1, &c2, 10);
408
409 let first = ci.color();
410 assert_eq!(first.r, 0);
411
412 for _ in 0..10 {
413 ci.inc();
414 }
415 let last = ci.color();
416 assert_eq!(last.r, 255);
417 }
418
419 #[test]
420 fn test_gradient_linear_color_set_colors() {
421 let mut gc = GradientLinearColor::new(
422 Rgba8::new(0, 0, 0, 255),
423 Rgba8::new(255, 255, 255, 255),
424 256,
425 );
426 gc.colors(Rgba8::new(255, 0, 0, 255), Rgba8::new(0, 255, 0, 255));
427 assert_eq!(gc.get(0).r, 255);
428 assert_eq!(gc.get(0).g, 0);
429 assert_eq!(gc.get(255).r, 0);
430 assert_eq!(gc.get(255).g, 255);
431 }
432}