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 dd = (self.d2 - self.d1).max(1);
389 self.interpolator.begin(x as f64 + 0.5, y as f64 + 0.5, len);
390 let color_size = self.color_function.size() as i32;
391 for pixel in span.iter_mut().take(len as usize) {
392 let mut ix = 0i32;
393 let mut iy = 0i32;
394 self.interpolator.coordinates(&mut ix, &mut iy);
395 let d = self.gradient_function.calculate(
396 ix >> DOWNSCALE_SHIFT,
397 iy >> DOWNSCALE_SHIFT,
398 self.d2,
399 );
400 let d = (((d - self.d1) * color_size) / dd).clamp(0, color_size - 1);
401 *pixel = self.color_function.get(d as usize);
402 self.interpolator.next();
403 }
404 }
405}
406
407#[cfg(test)]
412mod tests {
413 use super::*;
414 use crate::color::Rgba8;
415 use crate::gradient_lut::{GradientLinearColor, GradientLut};
416 use crate::trans_affine::TransAffine;
417
418 #[test]
421 fn test_gradient_x() {
422 let g = GradientX;
423 assert_eq!(g.calculate(100, 200, 500), 100);
424 assert_eq!(g.calculate(-50, 200, 500), -50);
425 }
426
427 #[test]
428 fn test_gradient_y() {
429 let g = GradientY;
430 assert_eq!(g.calculate(100, 200, 500), 200);
431 assert_eq!(g.calculate(100, -50, 500), -50);
432 }
433
434 #[test]
435 fn test_gradient_radial() {
436 let g = GradientRadial;
437 assert_eq!(g.calculate(3, 4, 100), 5);
439 assert_eq!(g.calculate(0, 0, 100), 0);
441 }
442
443 #[test]
444 fn test_gradient_radial_d() {
445 let g = GradientRadialD;
446 assert_eq!(g.calculate(3, 4, 100), 5);
448 }
449
450 #[test]
451 fn test_gradient_diamond() {
452 let g = GradientDiamond;
453 assert_eq!(g.calculate(3, 5, 100), 5);
454 assert_eq!(g.calculate(7, 5, 100), 7);
455 assert_eq!(g.calculate(-3, 5, 100), 5);
456 assert_eq!(g.calculate(3, -8, 100), 8);
457 }
458
459 #[test]
460 fn test_gradient_xy() {
461 let g = GradientXY;
462 assert_eq!(g.calculate(10, 20, 100), 2); assert_eq!(g.calculate(-10, 20, 100), 2);
464 }
465
466 #[test]
467 fn test_gradient_sqrt_xy() {
468 let g = GradientSqrtXY;
469 assert_eq!(g.calculate(100, 100, 500), 100);
471 }
472
473 #[test]
474 fn test_gradient_conic() {
475 let g = GradientConic;
476 assert_eq!(g.calculate(1, 0, 100), 0);
478 assert_eq!(g.calculate(0, 1, 100), 50);
480 assert_eq!(g.calculate(-1, 0, 100), 100);
482 }
483
484 #[test]
485 fn test_gradient_radial_focus_default() {
486 let g = GradientRadialFocus::new_default();
487 assert_eq!(g.radius(), 100.0);
488 assert_eq!(g.focus_x(), 0.0);
489 assert_eq!(g.focus_y(), 0.0);
490 }
491
492 #[test]
493 fn test_gradient_radial_focus_centered() {
494 let g = GradientRadialFocus::new(100.0, 0.0, 0.0);
495 assert_eq!(g.calculate(0, 0, 1600), 0);
497 let d = g.calculate(1600, 0, 1600);
499 assert!((d - 1600).abs() <= 2, "d={}", d);
500 }
501
502 #[test]
503 fn test_gradient_radial_focus_init() {
504 let mut g = GradientRadialFocus::new_default();
505 g.init(50.0, 10.0, 5.0);
506 assert_eq!(g.radius(), 50.0);
507 assert!((g.focus_x() - 10.0).abs() < 0.1);
509 assert!((g.focus_y() - 5.0).abs() < 0.1);
510 }
511
512 #[test]
515 fn test_gradient_repeat_adaptor() {
516 let g = GradientRepeatAdaptor::new(GradientX);
517 assert_eq!(g.calculate(150, 0, 100), 50);
519 assert_eq!(g.calculate(-50, 0, 100), 50);
521 assert_eq!(g.calculate(250, 0, 100), 50);
523 }
524
525 #[test]
526 fn test_gradient_reflect_adaptor() {
527 let g = GradientReflectAdaptor::new(GradientX);
528 assert_eq!(g.calculate(50, 0, 100), 50);
530 assert_eq!(g.calculate(150, 0, 100), 50);
532 assert_eq!(g.calculate(250, 0, 100), 50);
534 }
535
536 #[test]
539 fn test_span_gradient_linear_x() {
540 let trans = TransAffine::new();
541 let interp = SpanInterpolatorLinear::new(trans);
542 let gc = GradientLinearColor::new(
543 Rgba8::new(0, 0, 0, 255),
544 Rgba8::new(255, 255, 255, 255),
545 256,
546 );
547 let mut sg = SpanGradient::new(interp, GradientX, &gc, 0.0, 100.0);
548
549 let mut span = vec![Rgba8::default(); 10];
550 sg.generate(&mut span, 0, 0, 10);
551
552 assert!(span[0].r < span[9].r, "s0={} s9={}", span[0].r, span[9].r);
554 }
555
556 #[test]
557 fn test_span_gradient_d1_d2() {
558 let trans = TransAffine::new();
559 let interp = SpanInterpolatorLinear::new(trans);
560 let gc = GradientLinearColor::new(
561 Rgba8::new(0, 0, 0, 255),
562 Rgba8::new(255, 255, 255, 255),
563 256,
564 );
565 let sg = SpanGradient::new(interp, GradientX, &gc, 10.0, 200.0);
566 assert!((sg.d1() - 10.0).abs() < 0.1);
567 assert!((sg.d2() - 200.0).abs() < 0.1);
568 }
569
570 #[test]
571 fn test_span_gradient_with_lut() {
572 let trans = TransAffine::new();
573 let interp = SpanInterpolatorLinear::new(trans);
574 let mut lut = GradientLut::new_default();
575 lut.add_color(0.0, Rgba8::new(255, 0, 0, 255));
576 lut.add_color(1.0, Rgba8::new(0, 0, 255, 255));
577 lut.build_lut();
578
579 let mut sg = SpanGradient::new(interp, GradientX, &lut, 0.0, 100.0);
580
581 let mut span = vec![Rgba8::default(); 5];
582 sg.generate(&mut span, 0, 0, 5);
583
584 assert!(span[0].r > 200, "r={}", span[0].r);
586 }
587
588 #[test]
589 fn test_span_gradient_clamping() {
590 let trans = TransAffine::new();
592 let interp = SpanInterpolatorLinear::new(trans);
593 let gc = GradientLinearColor::new(
594 Rgba8::new(0, 0, 0, 255),
595 Rgba8::new(255, 255, 255, 255),
596 256,
597 );
598 let mut sg = SpanGradient::new(interp, GradientX, &gc, 90.0, 100.0);
600
601 let mut span = vec![Rgba8::default(); 5];
602 sg.generate(&mut span, 0, 0, 5);
603 for c in &span {
605 assert_eq!(c.r, 0, "Expected black, got r={}", c.r);
606 }
607 }
608
609 #[test]
610 fn test_span_gradient_constants() {
611 assert_eq!(GRADIENT_SUBPIXEL_SHIFT, 4);
612 assert_eq!(GRADIENT_SUBPIXEL_SCALE, 16);
613 assert_eq!(GRADIENT_SUBPIXEL_MASK, 15);
614 }
615}