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