1use crate::basics::{iround, uround};
9use crate::gradient_lut::ColorFunction;
10use crate::math::fast_sqrt;
11use crate::renderer_scanline::SpanGenerator;
12use crate::span_interpolator_linear::{SpanInterpolatorLinear, SUBPIXEL_SHIFT};
13
14pub const GRADIENT_SUBPIXEL_SHIFT: i32 = 4;
19pub const GRADIENT_SUBPIXEL_SCALE: i32 = 1 << GRADIENT_SUBPIXEL_SHIFT;
20pub const GRADIENT_SUBPIXEL_MASK: i32 = GRADIENT_SUBPIXEL_SCALE - 1;
21
22const DOWNSCALE_SHIFT: i32 = SUBPIXEL_SHIFT as i32 - GRADIENT_SUBPIXEL_SHIFT;
24
25pub trait GradientFunction {
34 fn calculate(&self, x: i32, y: i32, d: i32) -> i32;
35}
36
37pub struct GradientX;
45
46impl GradientFunction for GradientX {
47 #[inline]
48 fn calculate(&self, x: i32, _y: i32, _d: i32) -> i32 {
49 x
50 }
51}
52
53pub struct GradientY;
57
58impl GradientFunction for GradientY {
59 #[inline]
60 fn calculate(&self, _x: i32, y: i32, _d: i32) -> i32 {
61 y
62 }
63}
64
65pub struct GradientRadial;
69
70impl GradientFunction for GradientRadial {
71 #[inline]
72 fn calculate(&self, x: i32, y: i32, _d: i32) -> i32 {
73 fast_sqrt((x * x + y * y) as u32) as i32
74 }
75}
76
77pub struct GradientRadialD;
81
82impl GradientFunction for GradientRadialD {
83 #[inline]
84 fn calculate(&self, x: i32, y: i32, _d: i32) -> i32 {
85 uround(((x as f64) * (x as f64) + (y as f64) * (y as f64)).sqrt()) as i32
86 }
87}
88
89pub struct GradientDiamond;
93
94impl GradientFunction for GradientDiamond {
95 #[inline]
96 fn calculate(&self, x: i32, y: i32, _d: i32) -> i32 {
97 let ax = x.abs();
98 let ay = y.abs();
99 if ax > ay {
100 ax
101 } else {
102 ay
103 }
104 }
105}
106
107pub struct GradientXY;
111
112impl GradientFunction for GradientXY {
113 #[inline]
114 fn calculate(&self, x: i32, y: i32, d: i32) -> i32 {
115 x.abs() * y.abs() / d
116 }
117}
118
119pub struct GradientSqrtXY;
123
124impl GradientFunction for GradientSqrtXY {
125 #[inline]
126 fn calculate(&self, x: i32, y: i32, _d: i32) -> i32 {
127 fast_sqrt((x.abs() * y.abs()) as u32) as i32
128 }
129}
130
131pub struct GradientConic;
135
136impl GradientFunction for GradientConic {
137 #[inline]
138 fn calculate(&self, x: i32, y: i32, d: i32) -> i32 {
139 uround((y as f64).atan2(x as f64).abs() * (d as f64) / std::f64::consts::PI) as i32
140 }
141}
142
143pub struct GradientRadialFocus {
152 r: i32,
153 fx: i32,
154 fy: i32,
155 r2: f64,
156 fx2: f64,
157 fy2: f64,
158 mul: f64,
159}
160
161impl GradientRadialFocus {
162 pub fn new_default() -> Self {
163 let mut s = Self {
164 r: 100 * GRADIENT_SUBPIXEL_SCALE,
165 fx: 0,
166 fy: 0,
167 r2: 0.0,
168 fx2: 0.0,
169 fy2: 0.0,
170 mul: 0.0,
171 };
172 s.update_values();
173 s
174 }
175
176 pub fn new(r: f64, fx: f64, fy: f64) -> Self {
177 let mut s = Self {
178 r: iround(r * GRADIENT_SUBPIXEL_SCALE as f64),
179 fx: iround(fx * GRADIENT_SUBPIXEL_SCALE as f64),
180 fy: iround(fy * GRADIENT_SUBPIXEL_SCALE as f64),
181 r2: 0.0,
182 fx2: 0.0,
183 fy2: 0.0,
184 mul: 0.0,
185 };
186 s.update_values();
187 s
188 }
189
190 pub fn init(&mut self, r: f64, fx: f64, fy: f64) {
191 self.r = iround(r * GRADIENT_SUBPIXEL_SCALE as f64);
192 self.fx = iround(fx * GRADIENT_SUBPIXEL_SCALE as f64);
193 self.fy = iround(fy * GRADIENT_SUBPIXEL_SCALE as f64);
194 self.update_values();
195 }
196
197 pub fn radius(&self) -> f64 {
198 self.r as f64 / GRADIENT_SUBPIXEL_SCALE as f64
199 }
200
201 pub fn focus_x(&self) -> f64 {
202 self.fx as f64 / GRADIENT_SUBPIXEL_SCALE as f64
203 }
204
205 pub fn focus_y(&self) -> f64 {
206 self.fy as f64 / GRADIENT_SUBPIXEL_SCALE as f64
207 }
208
209 fn update_values(&mut self) {
210 self.r2 = (self.r as f64) * (self.r as f64);
216 self.fx2 = (self.fx as f64) * (self.fx as f64);
217 self.fy2 = (self.fy as f64) * (self.fy as f64);
218 let mut d = self.r2 - (self.fx2 + self.fy2);
219 if d == 0.0 {
220 if self.fx != 0 {
221 if self.fx < 0 {
222 self.fx += 1;
223 } else {
224 self.fx -= 1;
225 }
226 }
227 if self.fy != 0 {
228 if self.fy < 0 {
229 self.fy += 1;
230 } else {
231 self.fy -= 1;
232 }
233 }
234 self.fx2 = (self.fx as f64) * (self.fx as f64);
235 self.fy2 = (self.fy as f64) * (self.fy as f64);
236 d = self.r2 - (self.fx2 + self.fy2);
237 }
238 self.mul = self.r as f64 / d;
239 }
240}
241
242impl GradientFunction for GradientRadialFocus {
243 fn calculate(&self, x: i32, y: i32, _d: i32) -> i32 {
244 let dx = x as f64 - self.fx as f64;
245 let dy = y as f64 - self.fy as f64;
246 let d2 = dx * self.fy as f64 - dy * self.fx as f64;
247 let d3 = self.r2 * (dx * dx + dy * dy) - d2 * d2;
248 iround((dx * self.fx as f64 + dy * self.fy as f64 + d3.abs().sqrt()) * self.mul)
249 }
250}
251
252pub struct GradientRepeatAdaptor<G> {
260 gradient: G,
261}
262
263impl<G: GradientFunction> GradientRepeatAdaptor<G> {
264 pub fn new(gradient: G) -> Self {
265 Self { gradient }
266 }
267}
268
269impl<G: GradientFunction> GradientFunction for GradientRepeatAdaptor<G> {
270 #[inline]
271 fn calculate(&self, x: i32, y: i32, d: i32) -> i32 {
272 let mut ret = self.gradient.calculate(x, y, d) % d;
273 if ret < 0 {
274 ret += d;
275 }
276 ret
277 }
278}
279
280pub struct GradientReflectAdaptor<G> {
284 gradient: G,
285}
286
287impl<G: GradientFunction> GradientReflectAdaptor<G> {
288 pub fn new(gradient: G) -> Self {
289 Self { gradient }
290 }
291}
292
293impl<G: GradientFunction> GradientFunction for GradientReflectAdaptor<G> {
294 #[inline]
295 fn calculate(&self, x: i32, y: i32, d: i32) -> i32 {
296 let d2 = d << 1;
297 let mut ret = self.gradient.calculate(x, y, d) % d2;
298 if ret < 0 {
299 ret += d2;
300 }
301 if ret >= d {
302 ret = d2 - ret;
303 }
304 ret
305 }
306}
307
308pub struct SpanGradient<'a, G, F> {
320 interpolator: SpanInterpolatorLinear,
321 gradient_function: G,
322 color_function: &'a F,
323 d1: i32,
324 d2: i32,
325}
326
327impl<'a, G: GradientFunction, F: ColorFunction> SpanGradient<'a, G, F> {
328 pub fn new(
329 interpolator: SpanInterpolatorLinear,
330 gradient_function: G,
331 color_function: &'a F,
332 d1: f64,
333 d2: f64,
334 ) -> Self {
335 Self {
336 interpolator,
337 gradient_function,
338 color_function,
339 d1: iround(d1 * GRADIENT_SUBPIXEL_SCALE as f64),
340 d2: iround(d2 * GRADIENT_SUBPIXEL_SCALE as f64),
341 }
342 }
343
344 pub fn interpolator(&self) -> &SpanInterpolatorLinear {
345 &self.interpolator
346 }
347
348 pub fn interpolator_mut(&mut self) -> &mut SpanInterpolatorLinear {
349 &mut self.interpolator
350 }
351
352 pub fn gradient_function(&self) -> &G {
353 &self.gradient_function
354 }
355
356 pub fn color_function(&self) -> &F {
357 self.color_function
358 }
359
360 pub fn d1(&self) -> f64 {
361 self.d1 as f64 / GRADIENT_SUBPIXEL_SCALE as f64
362 }
363
364 pub fn d2(&self) -> f64 {
365 self.d2 as f64 / GRADIENT_SUBPIXEL_SCALE as f64
366 }
367
368 pub fn set_d1(&mut self, v: f64) {
369 self.d1 = iround(v * GRADIENT_SUBPIXEL_SCALE as f64);
370 }
371
372 pub fn set_d2(&mut self, v: f64) {
373 self.d2 = iround(v * GRADIENT_SUBPIXEL_SCALE as f64);
374 }
375}
376
377impl<'a, G, F> SpanGenerator for SpanGradient<'a, G, F>
378where
379 G: GradientFunction,
380 F: ColorFunction,
381 F::Color: Copy,
382{
383 type Color = F::Color;
384
385 fn prepare(&mut self) {}
386
387 fn generate(&mut self, span: &mut [F::Color], x: i32, y: i32, len: u32) {
388 let d1 = self.d1;
392 let d2 = self.d2;
393 let dd = (d2 - d1).max(1);
394 let color_size = self.color_function.size() as i32;
395 let color_max = color_size - 1;
396 self.interpolator.begin(x as f64 + 0.5, y as f64 + 0.5, len);
397 for pixel in span.iter_mut().take(len as usize) {
398 let mut ix = 0i32;
399 let mut iy = 0i32;
400 self.interpolator.coordinates(&mut ix, &mut iy);
401 let d = self.gradient_function.calculate(
402 ix >> DOWNSCALE_SHIFT,
403 iy >> DOWNSCALE_SHIFT,
404 d2,
405 );
406 let d = (((d - d1) * color_size) / dd).clamp(0, color_max);
407 *pixel = self.color_function.get(d as usize);
408 self.interpolator.next();
409 }
410 }
411}
412
413#[cfg(test)]
418mod tests {
419 use super::*;
420 use crate::color::Rgba8;
421 use crate::gradient_lut::{GradientLinearColor, GradientLut};
422 use crate::trans_affine::TransAffine;
423
424 #[test]
427 fn test_gradient_x() {
428 let g = GradientX;
429 assert_eq!(g.calculate(100, 200, 500), 100);
430 assert_eq!(g.calculate(-50, 200, 500), -50);
431 }
432
433 #[test]
434 fn test_gradient_y() {
435 let g = GradientY;
436 assert_eq!(g.calculate(100, 200, 500), 200);
437 assert_eq!(g.calculate(100, -50, 500), -50);
438 }
439
440 #[test]
441 fn test_gradient_radial() {
442 let g = GradientRadial;
443 assert_eq!(g.calculate(3, 4, 100), 5);
445 assert_eq!(g.calculate(0, 0, 100), 0);
447 }
448
449 #[test]
450 fn test_gradient_radial_d() {
451 let g = GradientRadialD;
452 assert_eq!(g.calculate(3, 4, 100), 5);
454 }
455
456 #[test]
457 fn test_gradient_diamond() {
458 let g = GradientDiamond;
459 assert_eq!(g.calculate(3, 5, 100), 5);
460 assert_eq!(g.calculate(7, 5, 100), 7);
461 assert_eq!(g.calculate(-3, 5, 100), 5);
462 assert_eq!(g.calculate(3, -8, 100), 8);
463 }
464
465 #[test]
466 fn test_gradient_xy() {
467 let g = GradientXY;
468 assert_eq!(g.calculate(10, 20, 100), 2); assert_eq!(g.calculate(-10, 20, 100), 2);
470 }
471
472 #[test]
473 fn test_gradient_sqrt_xy() {
474 let g = GradientSqrtXY;
475 assert_eq!(g.calculate(100, 100, 500), 100);
477 }
478
479 #[test]
480 fn test_gradient_conic() {
481 let g = GradientConic;
482 assert_eq!(g.calculate(1, 0, 100), 0);
484 assert_eq!(g.calculate(0, 1, 100), 50);
486 assert_eq!(g.calculate(-1, 0, 100), 100);
488 }
489
490 #[test]
491 fn test_gradient_radial_focus_default() {
492 let g = GradientRadialFocus::new_default();
493 assert_eq!(g.radius(), 100.0);
494 assert_eq!(g.focus_x(), 0.0);
495 assert_eq!(g.focus_y(), 0.0);
496 }
497
498 #[test]
499 fn test_gradient_radial_focus_centered() {
500 let g = GradientRadialFocus::new(100.0, 0.0, 0.0);
501 assert_eq!(g.calculate(0, 0, 1600), 0);
503 let d = g.calculate(1600, 0, 1600);
505 assert!((d - 1600).abs() <= 2, "d={}", d);
506 }
507
508 #[test]
509 fn test_gradient_radial_focus_init() {
510 let mut g = GradientRadialFocus::new_default();
511 g.init(50.0, 10.0, 5.0);
512 assert_eq!(g.radius(), 50.0);
513 assert!((g.focus_x() - 10.0).abs() < 0.1);
515 assert!((g.focus_y() - 5.0).abs() < 0.1);
516 }
517
518 #[test]
521 fn test_gradient_repeat_adaptor() {
522 let g = GradientRepeatAdaptor::new(GradientX);
523 assert_eq!(g.calculate(150, 0, 100), 50);
525 assert_eq!(g.calculate(-50, 0, 100), 50);
527 assert_eq!(g.calculate(250, 0, 100), 50);
529 }
530
531 #[test]
532 fn test_gradient_reflect_adaptor() {
533 let g = GradientReflectAdaptor::new(GradientX);
534 assert_eq!(g.calculate(50, 0, 100), 50);
536 assert_eq!(g.calculate(150, 0, 100), 50);
538 assert_eq!(g.calculate(250, 0, 100), 50);
540 }
541
542 #[test]
545 fn test_span_gradient_linear_x() {
546 let trans = TransAffine::new();
547 let interp = SpanInterpolatorLinear::new(trans);
548 let gc = GradientLinearColor::new(
549 Rgba8::new(0, 0, 0, 255),
550 Rgba8::new(255, 255, 255, 255),
551 256,
552 );
553 let mut sg = SpanGradient::new(interp, GradientX, &gc, 0.0, 100.0);
554
555 let mut span = vec![Rgba8::default(); 10];
556 sg.generate(&mut span, 0, 0, 10);
557
558 assert!(span[0].r < span[9].r, "s0={} s9={}", span[0].r, span[9].r);
560 }
561
562 #[test]
563 fn test_span_gradient_d1_d2() {
564 let trans = TransAffine::new();
565 let interp = SpanInterpolatorLinear::new(trans);
566 let gc = GradientLinearColor::new(
567 Rgba8::new(0, 0, 0, 255),
568 Rgba8::new(255, 255, 255, 255),
569 256,
570 );
571 let sg = SpanGradient::new(interp, GradientX, &gc, 10.0, 200.0);
572 assert!((sg.d1() - 10.0).abs() < 0.1);
573 assert!((sg.d2() - 200.0).abs() < 0.1);
574 }
575
576 #[test]
577 fn test_span_gradient_with_lut() {
578 let trans = TransAffine::new();
579 let interp = SpanInterpolatorLinear::new(trans);
580 let mut lut = GradientLut::new_default();
581 lut.add_color(0.0, Rgba8::new(255, 0, 0, 255));
582 lut.add_color(1.0, Rgba8::new(0, 0, 255, 255));
583 lut.build_lut();
584
585 let mut sg = SpanGradient::new(interp, GradientX, &lut, 0.0, 100.0);
586
587 let mut span = vec![Rgba8::default(); 5];
588 sg.generate(&mut span, 0, 0, 5);
589
590 assert!(span[0].r > 200, "r={}", span[0].r);
592 }
593
594 #[test]
595 fn test_span_gradient_clamping() {
596 let trans = TransAffine::new();
598 let interp = SpanInterpolatorLinear::new(trans);
599 let gc = GradientLinearColor::new(
600 Rgba8::new(0, 0, 0, 255),
601 Rgba8::new(255, 255, 255, 255),
602 256,
603 );
604 let mut sg = SpanGradient::new(interp, GradientX, &gc, 90.0, 100.0);
606
607 let mut span = vec![Rgba8::default(); 5];
608 sg.generate(&mut span, 0, 0, 5);
609 for c in &span {
611 assert_eq!(c.r, 0, "Expected black, got r={}", c.r);
612 }
613 }
614
615 #[test]
616 fn test_span_gradient_constants() {
617 assert_eq!(GRADIENT_SUBPIXEL_SHIFT, 4);
618 assert_eq!(GRADIENT_SUBPIXEL_SCALE, 16);
619 assert_eq!(GRADIENT_SUBPIXEL_MASK, 15);
620 }
621}