Skip to main content

agg_rust/
span_image_filter.rs

1//! Base image filter span generators.
2//!
3//! Port of `agg_span_image_filter.h` — base structs shared by all image
4//! transformation span generators (nearest, bilinear, general convolution,
5//! and resampling variants).
6
7use crate::basics::uround;
8use crate::image_filters::{ImageFilterLut, IMAGE_SUBPIXEL_SCALE, IMAGE_SUBPIXEL_SHIFT};
9use crate::span_interpolator_linear::SpanInterpolatorLinear;
10use crate::trans_affine::TransAffine;
11
12// ============================================================================
13// SpanImageFilterBase
14// ============================================================================
15
16/// Base state for image filter span generators.
17///
18/// Holds a reference to the interpolator and optional filter LUT,
19/// plus the subpixel offset (dx, dy) applied to coordinates before filtering.
20///
21/// Port of C++ `span_image_filter<Source, Interpolator>` (without the source,
22/// which is handled by the concrete span generator).
23pub struct SpanImageFilterBase<'a, I> {
24    interpolator: &'a mut I,
25    filter: Option<&'a ImageFilterLut>,
26    dx_dbl: f64,
27    dy_dbl: f64,
28    dx_int: u32,
29    dy_int: u32,
30}
31
32impl<'a, I> SpanImageFilterBase<'a, I> {
33    /// Create a new base with interpolator, optional filter, and default 0.5 offset.
34    pub fn new(interpolator: &'a mut I, filter: Option<&'a ImageFilterLut>) -> Self {
35        Self {
36            interpolator,
37            filter,
38            dx_dbl: 0.5,
39            dy_dbl: 0.5,
40            dx_int: IMAGE_SUBPIXEL_SCALE / 2,
41            dy_int: IMAGE_SUBPIXEL_SCALE / 2,
42        }
43    }
44
45    pub fn interpolator(&self) -> &I {
46        self.interpolator
47    }
48
49    pub fn interpolator_mut(&mut self) -> &mut I {
50        self.interpolator
51    }
52
53    pub fn filter(&self) -> Option<&ImageFilterLut> {
54        self.filter
55    }
56
57    pub fn filter_dx_int(&self) -> u32 {
58        self.dx_int
59    }
60
61    pub fn filter_dy_int(&self) -> u32 {
62        self.dy_int
63    }
64
65    pub fn filter_dx_dbl(&self) -> f64 {
66        self.dx_dbl
67    }
68
69    pub fn filter_dy_dbl(&self) -> f64 {
70        self.dy_dbl
71    }
72
73    /// Set the subpixel filter offset.
74    ///
75    /// The offset shifts coordinates before filtering. Default is (0.5, 0.5)
76    /// which centers the filter kernel on pixel centers.
77    pub fn set_filter_offset(&mut self, dx: f64, dy: f64) {
78        self.dx_dbl = dx;
79        self.dy_dbl = dy;
80        self.dx_int = uround(dx * IMAGE_SUBPIXEL_SCALE as f64);
81        self.dy_int = uround(dy * IMAGE_SUBPIXEL_SCALE as f64);
82    }
83
84    /// Set equal filter offset for both axes.
85    pub fn set_filter_offset_uniform(&mut self, d: f64) {
86        self.set_filter_offset(d, d);
87    }
88
89    /// Set the filter LUT.
90    pub fn set_filter(&mut self, filter: &'a ImageFilterLut) {
91        self.filter = Some(filter);
92    }
93
94    /// No-op prepare (matches C++ `span_image_filter::prepare()`).
95    pub fn prepare(&self) {
96        // intentionally empty — subclasses override
97    }
98}
99
100// ============================================================================
101// SpanImageResampleAffine
102// ============================================================================
103
104/// Image resampling state for affine transformations.
105///
106/// Computes scale factors from the interpolator's affine matrix once per
107/// scanline (in `prepare()`), limiting them to avoid excessive filter expansion.
108///
109/// Port of C++ `span_image_resample_affine<Source>`.
110pub struct SpanImageResampleAffine<'a> {
111    base: SpanImageFilterBase<'a, SpanInterpolatorLinear<TransAffine>>,
112    scale_limit: f64,
113    blur_x: f64,
114    blur_y: f64,
115    rx: i32,
116    ry: i32,
117    rx_inv: i32,
118    ry_inv: i32,
119}
120
121impl<'a> SpanImageResampleAffine<'a> {
122    /// Create with interpolator and filter.
123    pub fn new(
124        interpolator: &'a mut SpanInterpolatorLinear<TransAffine>,
125        filter: &'a ImageFilterLut,
126    ) -> Self {
127        Self {
128            base: SpanImageFilterBase::new(interpolator, Some(filter)),
129            scale_limit: 200.0,
130            blur_x: 1.0,
131            blur_y: 1.0,
132            rx: 0,
133            ry: 0,
134            rx_inv: 0,
135            ry_inv: 0,
136        }
137    }
138
139    pub fn base(&self) -> &SpanImageFilterBase<'a, SpanInterpolatorLinear<TransAffine>> {
140        &self.base
141    }
142
143    pub fn base_mut(
144        &mut self,
145    ) -> &mut SpanImageFilterBase<'a, SpanInterpolatorLinear<TransAffine>> {
146        &mut self.base
147    }
148
149    pub fn scale_limit(&self) -> u32 {
150        uround(self.scale_limit)
151    }
152
153    pub fn set_scale_limit(&mut self, v: i32) {
154        self.scale_limit = v as f64;
155    }
156
157    pub fn blur_x(&self) -> f64 {
158        self.blur_x
159    }
160
161    pub fn blur_y(&self) -> f64 {
162        self.blur_y
163    }
164
165    pub fn set_blur_x(&mut self, v: f64) {
166        self.blur_x = v;
167    }
168
169    pub fn set_blur_y(&mut self, v: f64) {
170        self.blur_y = v;
171    }
172
173    pub fn set_blur(&mut self, v: f64) {
174        self.blur_x = v;
175        self.blur_y = v;
176    }
177
178    pub fn rx(&self) -> i32 {
179        self.rx
180    }
181
182    pub fn ry(&self) -> i32 {
183        self.ry
184    }
185
186    pub fn rx_inv(&self) -> i32 {
187        self.rx_inv
188    }
189
190    pub fn ry_inv(&self) -> i32 {
191        self.ry_inv
192    }
193
194    /// Compute scale factors from the affine transformation.
195    ///
196    /// Port of C++ `span_image_resample_affine::prepare()`.
197    pub fn prepare(&mut self) {
198        let (mut scale_x, mut scale_y) = self.base.interpolator.transformer().scaling_abs();
199
200        let scale_xy = scale_x * scale_y;
201        if scale_xy > self.scale_limit {
202            scale_x = scale_x * self.scale_limit / scale_xy;
203            scale_y = scale_y * self.scale_limit / scale_xy;
204        }
205
206        if scale_x < 1.0 {
207            scale_x = 1.0;
208        }
209        if scale_y < 1.0 {
210            scale_y = 1.0;
211        }
212
213        if scale_x > self.scale_limit {
214            scale_x = self.scale_limit;
215        }
216        if scale_y > self.scale_limit {
217            scale_y = self.scale_limit;
218        }
219
220        scale_x *= self.blur_x;
221        scale_y *= self.blur_y;
222
223        if scale_x < 1.0 {
224            scale_x = 1.0;
225        }
226        if scale_y < 1.0 {
227            scale_y = 1.0;
228        }
229
230        self.rx = uround(scale_x * IMAGE_SUBPIXEL_SCALE as f64) as i32;
231        self.rx_inv = uround(1.0 / scale_x * IMAGE_SUBPIXEL_SCALE as f64) as i32;
232
233        self.ry = uround(scale_y * IMAGE_SUBPIXEL_SCALE as f64) as i32;
234        self.ry_inv = uround(1.0 / scale_y * IMAGE_SUBPIXEL_SCALE as f64) as i32;
235    }
236}
237
238// ============================================================================
239// SpanImageResample
240// ============================================================================
241
242/// Image resampling state for generic (non-affine) interpolators.
243///
244/// Unlike the affine variant which computes scale once per scanline, this
245/// version expects per-pixel scale values and provides `adjust_scale()` to
246/// clamp and blur them.
247///
248/// Port of C++ `span_image_resample<Source, Interpolator>`.
249pub struct SpanImageResample<'a, I> {
250    base: SpanImageFilterBase<'a, I>,
251    scale_limit: i32,
252    blur_x: i32,
253    blur_y: i32,
254}
255
256impl<'a, I> SpanImageResample<'a, I> {
257    /// Create with interpolator and filter.
258    pub fn new(interpolator: &'a mut I, filter: &'a ImageFilterLut) -> Self {
259        Self {
260            base: SpanImageFilterBase::new(interpolator, Some(filter)),
261            scale_limit: 20,
262            blur_x: IMAGE_SUBPIXEL_SCALE as i32,
263            blur_y: IMAGE_SUBPIXEL_SCALE as i32,
264        }
265    }
266
267    pub fn base(&self) -> &SpanImageFilterBase<'a, I> {
268        &self.base
269    }
270
271    pub fn base_mut(&mut self) -> &mut SpanImageFilterBase<'a, I> {
272        &mut self.base
273    }
274
275    pub fn scale_limit(&self) -> i32 {
276        self.scale_limit
277    }
278
279    pub fn set_scale_limit(&mut self, v: i32) {
280        self.scale_limit = v;
281    }
282
283    pub fn blur_x(&self) -> f64 {
284        self.blur_x as f64 / IMAGE_SUBPIXEL_SCALE as f64
285    }
286
287    pub fn blur_y(&self) -> f64 {
288        self.blur_y as f64 / IMAGE_SUBPIXEL_SCALE as f64
289    }
290
291    pub fn set_blur_x(&mut self, v: f64) {
292        self.blur_x = uround(v * IMAGE_SUBPIXEL_SCALE as f64) as i32;
293    }
294
295    pub fn set_blur_y(&mut self, v: f64) {
296        self.blur_y = uround(v * IMAGE_SUBPIXEL_SCALE as f64) as i32;
297    }
298
299    pub fn set_blur(&mut self, v: f64) {
300        let iv = uround(v * IMAGE_SUBPIXEL_SCALE as f64) as i32;
301        self.blur_x = iv;
302        self.blur_y = iv;
303    }
304
305    /// Adjust per-pixel scale factors: clamp to limits, apply blur.
306    ///
307    /// Port of C++ `span_image_resample::adjust_scale()`.
308    #[inline]
309    pub fn adjust_scale(&self, rx: &mut i32, ry: &mut i32) {
310        let subpixel = IMAGE_SUBPIXEL_SCALE as i32;
311        if *rx < subpixel {
312            *rx = subpixel;
313        }
314        if *ry < subpixel {
315            *ry = subpixel;
316        }
317        if *rx > subpixel * self.scale_limit {
318            *rx = subpixel * self.scale_limit;
319        }
320        if *ry > subpixel * self.scale_limit {
321            *ry = subpixel * self.scale_limit;
322        }
323        *rx = (*rx * self.blur_x) >> IMAGE_SUBPIXEL_SHIFT;
324        *ry = (*ry * self.blur_y) >> IMAGE_SUBPIXEL_SHIFT;
325        if *rx < subpixel {
326            *rx = subpixel;
327        }
328        if *ry < subpixel {
329            *ry = subpixel;
330        }
331    }
332}
333
334// ============================================================================
335// Tests
336// ============================================================================
337
338#[cfg(test)]
339mod tests {
340    use super::*;
341
342    #[test]
343    fn test_base_default_offsets() {
344        let trans = TransAffine::new();
345        let mut interp = SpanInterpolatorLinear::new(trans);
346        let base = SpanImageFilterBase::new(&mut interp, None);
347        assert_eq!(base.filter_dx_int(), IMAGE_SUBPIXEL_SCALE / 2);
348        assert_eq!(base.filter_dy_int(), IMAGE_SUBPIXEL_SCALE / 2);
349        assert_eq!(base.filter_dx_dbl(), 0.5);
350        assert_eq!(base.filter_dy_dbl(), 0.5);
351    }
352
353    #[test]
354    fn test_base_set_offset() {
355        let trans = TransAffine::new();
356        let mut interp = SpanInterpolatorLinear::new(trans);
357        let mut base = SpanImageFilterBase::new(&mut interp, None);
358        base.set_filter_offset(0.25, 0.75);
359        assert_eq!(base.filter_dx_dbl(), 0.25);
360        assert_eq!(base.filter_dy_dbl(), 0.75);
361        // 0.25 * 256 = 64, 0.75 * 256 = 192
362        assert_eq!(base.filter_dx_int(), 64);
363        assert_eq!(base.filter_dy_int(), 192);
364    }
365
366    #[test]
367    fn test_base_set_offset_uniform() {
368        let trans = TransAffine::new();
369        let mut interp = SpanInterpolatorLinear::new(trans);
370        let mut base = SpanImageFilterBase::new(&mut interp, None);
371        base.set_filter_offset_uniform(0.0);
372        assert_eq!(base.filter_dx_int(), 0);
373        assert_eq!(base.filter_dy_int(), 0);
374    }
375
376    #[test]
377    fn test_base_filter_reference() {
378        let trans = TransAffine::new();
379        let mut interp = SpanInterpolatorLinear::new(trans);
380        let base = SpanImageFilterBase::new(&mut interp, None);
381        assert!(base.filter().is_none());
382    }
383
384    #[test]
385    fn test_base_with_filter() {
386        use crate::image_filters::ImageFilterBilinear;
387        let lut = ImageFilterLut::new_with_filter(&ImageFilterBilinear, true);
388        let trans = TransAffine::new();
389        let mut interp = SpanInterpolatorLinear::new(trans);
390        let base = SpanImageFilterBase::new(&mut interp, Some(&lut));
391        assert!(base.filter().is_some());
392        assert_eq!(base.filter().unwrap().radius(), 1.0);
393    }
394
395    #[test]
396    fn test_resample_affine_defaults() {
397        let trans = TransAffine::new();
398        let mut interp = SpanInterpolatorLinear::new(trans);
399        let lut = ImageFilterLut::new_with_filter(&crate::image_filters::ImageFilterBilinear, true);
400        let r = SpanImageResampleAffine::new(&mut interp, &lut);
401        assert_eq!(r.scale_limit(), 200);
402        assert_eq!(r.blur_x(), 1.0);
403        assert_eq!(r.blur_y(), 1.0);
404    }
405
406    #[test]
407    fn test_resample_affine_prepare_identity() {
408        let trans = TransAffine::new();
409        let mut interp = SpanInterpolatorLinear::new(trans);
410        let lut = ImageFilterLut::new_with_filter(&crate::image_filters::ImageFilterBilinear, true);
411        let mut r = SpanImageResampleAffine::new(&mut interp, &lut);
412        r.prepare();
413        // Identity: scale_x = 1, scale_y = 1
414        // rx = uround(1.0 * 256) = 256
415        assert_eq!(r.rx(), IMAGE_SUBPIXEL_SCALE as i32);
416        assert_eq!(r.ry(), IMAGE_SUBPIXEL_SCALE as i32);
417        // rx_inv = uround(1.0/1.0 * 256) = 256
418        assert_eq!(r.rx_inv(), IMAGE_SUBPIXEL_SCALE as i32);
419        assert_eq!(r.ry_inv(), IMAGE_SUBPIXEL_SCALE as i32);
420    }
421
422    #[test]
423    fn test_resample_affine_prepare_scaled() {
424        let trans = TransAffine::new_scaling(2.0, 3.0);
425        let mut interp = SpanInterpolatorLinear::new(trans);
426        let lut = ImageFilterLut::new_with_filter(&crate::image_filters::ImageFilterBilinear, true);
427        let mut r = SpanImageResampleAffine::new(&mut interp, &lut);
428        r.prepare();
429        // scale_x=2, scale_y=3 (product=6 < 200 limit, both > 1)
430        // rx = uround(2.0 * 256) = 512
431        assert_eq!(r.rx(), 512);
432        // ry = uround(3.0 * 256) = 768
433        assert_eq!(r.ry(), 768);
434        // rx_inv = uround(0.5 * 256) = 128
435        assert_eq!(r.rx_inv(), 128);
436        // ry_inv = uround(1.0/3.0 * 256) = 85
437        assert_eq!(r.ry_inv(), 85);
438    }
439
440    #[test]
441    fn test_resample_affine_blur() {
442        let trans = TransAffine::new();
443        let mut interp = SpanInterpolatorLinear::new(trans);
444        let lut = ImageFilterLut::new_with_filter(&crate::image_filters::ImageFilterBilinear, true);
445        let mut r = SpanImageResampleAffine::new(&mut interp, &lut);
446        r.set_blur(2.0);
447        r.prepare();
448        // Identity scale = 1.0, blur = 2.0 → effective = 2.0
449        assert_eq!(r.rx(), 512);
450        assert_eq!(r.ry(), 512);
451    }
452
453    #[test]
454    fn test_resample_generic_defaults() {
455        let trans = TransAffine::new();
456        let mut interp = SpanInterpolatorLinear::new(trans);
457        let lut = ImageFilterLut::new_with_filter(&crate::image_filters::ImageFilterBilinear, true);
458        let r = SpanImageResample::new(&mut interp, &lut);
459        assert_eq!(r.scale_limit(), 20);
460        assert!((r.blur_x() - 1.0).abs() < 1e-10);
461        assert!((r.blur_y() - 1.0).abs() < 1e-10);
462    }
463
464    #[test]
465    fn test_resample_generic_adjust_scale_clamp_min() {
466        let trans = TransAffine::new();
467        let mut interp = SpanInterpolatorLinear::new(trans);
468        let lut = ImageFilterLut::new_with_filter(&crate::image_filters::ImageFilterBilinear, true);
469        let r = SpanImageResample::new(&mut interp, &lut);
470        let mut rx = 10; // below IMAGE_SUBPIXEL_SCALE (256)
471        let mut ry = 10;
472        r.adjust_scale(&mut rx, &mut ry);
473        // Should be clamped to IMAGE_SUBPIXEL_SCALE
474        assert_eq!(rx, IMAGE_SUBPIXEL_SCALE as i32);
475        assert_eq!(ry, IMAGE_SUBPIXEL_SCALE as i32);
476    }
477
478    #[test]
479    fn test_resample_generic_adjust_scale_clamp_max() {
480        let trans = TransAffine::new();
481        let mut interp = SpanInterpolatorLinear::new(trans);
482        let lut = ImageFilterLut::new_with_filter(&crate::image_filters::ImageFilterBilinear, true);
483        let r = SpanImageResample::new(&mut interp, &lut);
484        // scale_limit=20, IMAGE_SUBPIXEL_SCALE=256 → max = 5120
485        let mut rx = 100_000;
486        let mut ry = 100_000;
487        r.adjust_scale(&mut rx, &mut ry);
488        // After clamping to 5120 and applying blur (1.0 * 256 / 256 = same):
489        assert_eq!(rx, 5120);
490        assert_eq!(ry, 5120);
491    }
492
493    #[test]
494    fn test_resample_generic_adjust_scale_with_blur() {
495        let trans = TransAffine::new();
496        let mut interp = SpanInterpolatorLinear::new(trans);
497        let lut = ImageFilterLut::new_with_filter(&crate::image_filters::ImageFilterBilinear, true);
498        let mut r = SpanImageResample::new(&mut interp, &lut);
499        r.set_blur(2.0); // blur_x = blur_y = uround(2.0 * 256) = 512
500        let mut rx = 256; // 1.0 in subpixel
501        let mut ry = 256;
502        r.adjust_scale(&mut rx, &mut ry);
503        // rx = (256 * 512) >> 8 = 512
504        assert_eq!(rx, 512);
505        assert_eq!(ry, 512);
506    }
507
508    #[test]
509    fn test_resample_generic_set_scale_limit() {
510        let trans = TransAffine::new();
511        let mut interp = SpanInterpolatorLinear::new(trans);
512        let lut = ImageFilterLut::new_with_filter(&crate::image_filters::ImageFilterBilinear, true);
513        let mut r = SpanImageResample::new(&mut interp, &lut);
514        r.set_scale_limit(50);
515        assert_eq!(r.scale_limit(), 50);
516    }
517}