Skip to main content

hayro_interpret/
shading.rs

1//! PDF shadings.
2
3#![allow(clippy::needless_range_loop)]
4
5use crate::CacheKey;
6use crate::cache::Cache;
7use crate::color::{ColorComponents, ColorSpace};
8use crate::function::{Function, StitchingBounds, Values, interpolate};
9use crate::util::{Float32Ext, PointExt, RectExt};
10use hayro_syntax::bit_reader::BitReader;
11use hayro_syntax::object::Array;
12use hayro_syntax::object::Dict;
13use hayro_syntax::object::Object;
14use hayro_syntax::object::Rect;
15use hayro_syntax::object::Stream;
16use hayro_syntax::object::dict::keys::{
17    BACKGROUND, BBOX, BITS_PER_COMPONENT, BITS_PER_COORDINATE, BITS_PER_FLAG, COLORSPACE, COORDS,
18    DECODE, DOMAIN, EXTEND, FUNCTION, MATRIX, SHADING_TYPE, VERTICES_PER_ROW,
19};
20use kurbo::{Affine, BezPath, CubicBez, ParamCurve, Point, Shape};
21use smallvec::{SmallVec, smallvec};
22use std::sync::Arc;
23
24/// The function supplied to a shading.
25#[derive(Debug, Clone)]
26pub enum ShadingFunction {
27    /// A single function, which should be used to evaluate all components of the shading.
28    Single(Function),
29    /// Multiple functions, one for each color component.
30    Multiple(SmallVec<[Function; 4]>),
31}
32
33impl ShadingFunction {
34    /// Evaluate the shading function.
35    pub fn eval(&self, input: &Values) -> Option<Values> {
36        match self {
37            Self::Single(s) => s.eval(input.clone()),
38            Self::Multiple(m) => {
39                // 1-in, 1-out function for each color component.
40
41                let mut out = smallvec![];
42
43                for func in m {
44                    out.push(*func.eval(input.clone())?.first()?);
45                }
46
47                Some(out)
48            }
49        }
50    }
51
52    pub(crate) fn stitching_bounds(&self) -> StitchingBounds {
53        let mut bounds = StitchingBounds::new();
54
55        match self {
56            Self::Single(function) => bounds.extend(function.stitching_bounds()),
57            Self::Multiple(functions) => {
58                for function in functions {
59                    bounds.extend(function.stitching_bounds());
60                }
61            }
62        }
63
64        bounds.sort_by(f32::total_cmp);
65        bounds.dedup_by(|a, b| (*a - *b).abs() <= f32::EPSILON);
66        bounds
67    }
68}
69
70/// A type of shading.
71#[derive(Debug)]
72pub enum ShadingType {
73    /// A function-based shading.
74    FunctionBased {
75        /// The domain of the function.
76        domain: [f32; 4],
77        /// A transform to apply to the shading.
78        matrix: Affine,
79        /// The function that should be used to evaluate the shading.
80        function: ShadingFunction,
81    },
82    /// A radial-axial shading.
83    RadialAxial {
84        /// The coordinates of the shading.
85        ///
86        /// For axial shadings, only the first 4 entries are relevant, representing the x/y coordinates
87        /// of the first point and the coordinates for the second point.
88        ///
89        /// For radial shadings, the coordinates contain the x/y coordinates as well as the radius
90        /// for both circles.
91        coords: [f32; 6],
92        /// The domain of the shading.
93        domain: [f32; 2],
94        /// The function forming the basis of the shading.
95        function: ShadingFunction,
96        /// The extends in the left/right direction of the shading.
97        extend: [bool; 2],
98        /// Whether the shading is axial or radial.
99        axial: bool,
100    },
101    /// A triangle-mesh shading.
102    TriangleMesh {
103        /// The triangles making up the shading.
104        triangles: Vec<Triangle>,
105        /// An optional function used for calculating the sampled color values.
106        function: Option<ShadingFunction>,
107    },
108    /// A coons-patch-mesh shading.
109    CoonsPatchMesh {
110        /// The patches that make up the shading.
111        patches: Vec<CoonsPatch>,
112        /// An optional function used for calculating the sampled color values.
113        function: Option<ShadingFunction>,
114    },
115    /// A tensor-product-patch-mesh shading.
116    TensorProductPatchMesh {
117        /// The patches that make up the shading.
118        patches: Vec<TensorProductPatch>,
119        /// An optional function used for calculating the sampled color values.
120        function: Option<ShadingFunction>,
121    },
122    /// A dummy shading that should just be drawn transparent.
123    Dummy,
124}
125
126/// A PDF shading.
127#[derive(Clone, Debug)]
128pub struct Shading {
129    cache_key: u128,
130    /// The type of shading.
131    pub shading_type: Arc<ShadingType>,
132    /// The color space of the shading.
133    pub color_space: ColorSpace,
134    /// A clip path that should be applied to the shading.
135    pub clip_path: Option<BezPath>,
136    /// The background color of the shading.
137    pub background: Option<SmallVec<[f32; 4]>>,
138}
139
140impl Shading {
141    pub(crate) fn new(dict: &Dict<'_>, stream: Option<&Stream<'_>>, cache: &Cache) -> Option<Self> {
142        let cache_key = dict.cache_key();
143
144        let shading_num = dict.get::<u8>(SHADING_TYPE)?;
145
146        let color_space = ColorSpace::new(dict.get(COLORSPACE)?, cache)?;
147
148        let shading_type = match shading_num {
149            1 => {
150                let domain = dict.get::<[f32; 4]>(DOMAIN).unwrap_or([0.0, 1.0, 0.0, 1.0]);
151                let matrix = dict
152                    .get::<[f64; 6]>(MATRIX)
153                    .map(Affine::new)
154                    .unwrap_or_default();
155                let function = read_function(dict, &color_space)?;
156
157                ShadingType::FunctionBased {
158                    domain,
159                    matrix,
160                    function,
161                }
162            }
163            2 | 3 => {
164                let domain = dict.get::<[f32; 2]>(DOMAIN).unwrap_or([0.0, 1.0]);
165                let function = read_function(dict, &color_space)?;
166                let extend = dict.get::<[bool; 2]>(EXTEND).unwrap_or([false, false]);
167                let (coords, invalid) = if shading_num == 2 {
168                    let read = dict.get::<[f32; 4]>(COORDS)?;
169                    let invalid = (read[0] - read[2]).is_nearly_zero()
170                        && (read[1] - read[3]).is_nearly_zero();
171                    ([read[0], read[1], read[2], read[3], 0.0, 0.0], invalid)
172                } else {
173                    let read = dict.get::<[f32; 6]>(COORDS)?;
174                    let invalid = (read[0] - read[3]).is_nearly_zero()
175                        && (read[1] - read[4]).is_nearly_zero()
176                        && (read[2] - read[5]).is_nearly_zero();
177                    (read, invalid)
178                };
179
180                let axial = shading_num == 2;
181
182                if invalid {
183                    ShadingType::Dummy
184                } else {
185                    ShadingType::RadialAxial {
186                        domain,
187                        function,
188                        extend,
189                        coords,
190                        axial,
191                    }
192                }
193            }
194            4 => {
195                let stream = stream?;
196                let stream_data = stream.decoded().ok()?;
197                let bp_coord = dict.get::<u8>(BITS_PER_COORDINATE)?;
198                let bp_comp = dict.get::<u8>(BITS_PER_COMPONENT)?;
199                let bpf = dict.get::<u8>(BITS_PER_FLAG)?;
200                let function = read_function(dict, &color_space);
201                let decode = dict
202                    .get::<Array<'_>>(DECODE)?
203                    .iter::<f32>()
204                    .collect::<Vec<_>>();
205
206                let triangles = read_free_form_triangles(
207                    stream_data.as_ref(),
208                    bpf,
209                    bp_coord,
210                    bp_comp,
211                    function.is_some(),
212                    &decode,
213                )?;
214
215                ShadingType::TriangleMesh {
216                    triangles,
217                    function,
218                }
219            }
220            5 => {
221                let stream = stream?;
222                let stream_data = stream.decoded().ok()?;
223                let bp_coord = dict.get::<u8>(BITS_PER_COORDINATE)?;
224                let bp_comp = dict.get::<u8>(BITS_PER_COMPONENT)?;
225                let function = read_function(dict, &color_space);
226                let decode = dict
227                    .get::<Array<'_>>(DECODE)?
228                    .iter::<f32>()
229                    .collect::<Vec<_>>();
230                let vertices_per_row = dict.get::<u32>(VERTICES_PER_ROW)?;
231
232                let triangles = read_lattice_triangles(
233                    stream_data.as_ref(),
234                    bp_coord,
235                    bp_comp,
236                    function.is_some(),
237                    vertices_per_row,
238                    &decode,
239                )?;
240
241                ShadingType::TriangleMesh {
242                    triangles,
243                    function,
244                }
245            }
246            6 => {
247                let stream = stream?;
248                let stream_data = stream.decoded().ok()?;
249                let bp_coord = dict.get::<u8>(BITS_PER_COORDINATE)?;
250                let bp_comp = dict.get::<u8>(BITS_PER_COMPONENT)?;
251                let bpf = dict.get::<u8>(BITS_PER_FLAG)?;
252                let function = read_function(dict, &color_space);
253                let decode = dict
254                    .get::<Array<'_>>(DECODE)?
255                    .iter::<f32>()
256                    .collect::<Vec<_>>();
257
258                let patches = read_coons_patch_mesh(
259                    stream_data.as_ref(),
260                    bpf,
261                    bp_coord,
262                    bp_comp,
263                    function.is_some(),
264                    &decode,
265                )?;
266
267                ShadingType::CoonsPatchMesh { patches, function }
268            }
269            7 => {
270                let stream = stream?;
271                let stream_data = stream.decoded().ok()?;
272                let bp_coord = dict.get::<u8>(BITS_PER_COORDINATE)?;
273                let bp_comp = dict.get::<u8>(BITS_PER_COMPONENT)?;
274                let bpf = dict.get::<u8>(BITS_PER_FLAG)?;
275                let function = read_function(dict, &color_space);
276                let decode = dict
277                    .get::<Array<'_>>(DECODE)?
278                    .iter::<f32>()
279                    .collect::<Vec<_>>();
280
281                let patches = read_tensor_product_patch_mesh(
282                    stream_data.as_ref(),
283                    bpf,
284                    bp_coord,
285                    bp_comp,
286                    function.is_some(),
287                    &decode,
288                )?;
289
290                ShadingType::TensorProductPatchMesh { patches, function }
291            }
292            _ => return None,
293        };
294
295        let bbox = dict.get::<Rect>(BBOX).map(|r| r.to_kurbo());
296        let background = dict
297            .get::<Array<'_>>(BACKGROUND)
298            .map(|a| a.iter::<f32>().collect::<SmallVec<_>>());
299
300        Some(Self {
301            cache_key,
302            shading_type: Arc::new(shading_type),
303            color_space,
304            clip_path: bbox.map(|r| r.to_path(0.1)),
305            background,
306        })
307    }
308}
309
310impl CacheKey for Shading {
311    fn cache_key(&self) -> u128 {
312        self.cache_key
313    }
314}
315
316/// A triangle made up of three vertices.
317#[derive(Clone, Debug)]
318pub struct Triangle {
319    /// The first vertex.
320    pub p0: TriangleVertex,
321    /// The second vertex.
322    pub p1: TriangleVertex,
323    /// The third vertex.
324    pub p2: TriangleVertex,
325    kurbo_tri: kurbo::Triangle,
326    d00: f64,
327    d01: f64,
328    d11: f64,
329}
330
331impl Triangle {
332    /// Create a new triangle.
333    pub fn new(p0: TriangleVertex, p1: TriangleVertex, p2: TriangleVertex) -> Self {
334        let v0 = p1.point - p0.point;
335        let v1 = p2.point - p0.point;
336
337        let d00 = v0.dot(v0);
338        let d01 = v0.dot(v1);
339        let d11 = v1.dot(v1);
340
341        let kurbo_tri = kurbo::Triangle::new(p0.point, p1.point, p2.point);
342
343        Self {
344            p0,
345            p1,
346            kurbo_tri,
347            p2,
348            d00,
349            d01,
350            d11,
351        }
352    }
353
354    /// Get the interpolated colors of the point from the triangle.
355    ///
356    /// Returns `None` if the point is not inside of the triangle.
357    pub fn interpolate(&self, pos: Point) -> ColorComponents {
358        let (u, v, w) = self.barycentric_coords(pos);
359
360        let mut result = smallvec![];
361
362        for i in 0..self.p0.colors.len() {
363            let c0 = self.p0.colors[i];
364            let c1 = self.p1.colors[i];
365            let c2 = self.p2.colors[i];
366            result.push(u * c0 + v * c1 + w * c2);
367        }
368
369        result
370    }
371
372    /// Return whether the point is contained within the triangle.
373    pub fn contains_point(&self, pos: Point) -> bool {
374        self.kurbo_tri.winding(pos) != 0
375    }
376
377    /// Return the bounding box of the triangle.
378    pub fn bounding_box(&self) -> kurbo::Rect {
379        self.kurbo_tri.bounding_box()
380    }
381
382    fn barycentric_coords(&self, p: Point) -> (f32, f32, f32) {
383        let (a, b, c) = (self.p0.point, self.p1.point, self.p2.point);
384        let v0 = b - a;
385        let v1 = c - a;
386        let v2 = p - a;
387
388        let d00 = self.d00;
389        let d01 = self.d01;
390        let d11 = self.d11;
391        let d20 = v2.dot(v0);
392        let d21 = v2.dot(v1);
393
394        let denom = d00 * d11 - d01 * d01;
395        let v = (d11 * d20 - d01 * d21) / denom;
396        let w = (d00 * d21 - d01 * d20) / denom;
397        let u = (1.0 - v - w) as f32;
398
399        (u, v as f32, w as f32)
400    }
401}
402
403/// A triangle vertex.
404#[derive(Clone, Debug)]
405pub struct TriangleVertex {
406    flag: u32,
407    /// The position of the vertex.
408    pub point: Point,
409    /// The color component of the vertex.
410    pub colors: ColorComponents,
411}
412
413/// A coons patch.
414#[derive(Clone, Debug)]
415pub struct CoonsPatch {
416    /// The control points of the coons patch.
417    pub control_points: [Point; 12],
418    /// The colors at each corner of the coons patch.
419    pub colors: [ColorComponents; 4],
420}
421
422/// A tensor-product patch.
423#[derive(Clone, Debug)]
424pub struct TensorProductPatch {
425    /// The control points of the tensor-product patch (4x4 grid = 16 points).
426    pub control_points: [Point; 16],
427    /// The colors at each corner of the tensor-product patch.
428    pub colors: [ColorComponents; 4],
429}
430
431impl CoonsPatch {
432    /// Map the point to the coordinates of the coons patch.
433    pub fn map_coordinate(&self, p: Point) -> Point {
434        let (u, v) = (p.x, p.y);
435
436        let cp = &self.control_points;
437
438        let c1 = CubicBez::new(cp[0], cp[11], cp[10], cp[9]);
439        let c2 = CubicBez::new(cp[3], cp[4], cp[5], cp[6]);
440        let d1 = CubicBez::new(cp[0], cp[1], cp[2], cp[3]);
441        let d2 = CubicBez::new(cp[9], cp[8], cp[7], cp[6]);
442
443        let sc = (1.0 - v) * c1.eval(u).to_vec2() + v * c2.eval(u).to_vec2();
444        let sd = (1.0 - u) * d1.eval(v).to_vec2() + u * d2.eval(v).to_vec2();
445        let sb = (1.0 - v) * ((1.0 - u) * c1.eval(0.0).to_vec2() + u * c1.eval(1.0).to_vec2())
446            + v * ((1.0 - u) * c2.eval(0.0).to_vec2() + u * c2.eval(1.0).to_vec2());
447
448        (sc + sd - sb).to_point()
449    }
450
451    /// Approximate the patch by triangles.
452    pub fn to_triangles(&self, buffer: &mut Vec<Triangle>) {
453        generate_patch_triangles(|p| self.map_coordinate(p), |p| self.interpolate(p), buffer);
454    }
455
456    /// Get the interpolated colors of the point from the patch.
457    pub fn interpolate(&self, pos: Point) -> ColorComponents {
458        let (u, v) = (pos.x, pos.y);
459        let (c0, c1, c2, c3) = {
460            (
461                &self.colors[0],
462                &self.colors[1],
463                &self.colors[2],
464                &self.colors[3],
465            )
466        };
467
468        let mut result = SmallVec::new();
469        for i in 0..c0.len() {
470            let val = (1.0 - u) * (1.0 - v) * c0[i] as f64
471                + u * (1.0 - v) * c3[i] as f64
472                + u * v * c2[i] as f64
473                + (1.0 - u) * v * c1[i] as f64;
474            result.push(val as f32);
475        }
476
477        result
478    }
479}
480
481impl TensorProductPatch {
482    /// Evaluate Bernstein polynomial `B_i(t)` for tensor-product patches.
483    fn bernstein(i: usize, t: f64) -> f64 {
484        match i {
485            0 => (1.0 - t).powi(3),
486            1 => 3.0 * t * (1.0 - t).powi(2),
487            2 => 3.0 * t.powi(2) * (1.0 - t),
488            3 => t.powi(3),
489            _ => 0.0,
490        }
491    }
492
493    /// Map the point to the coordinates of the tensor product patch.
494    pub fn map_coordinate(&self, p: Point) -> Point {
495        let (u, v) = (p.x, p.y);
496
497        let mut x = 0.0;
498        let mut y = 0.0;
499
500        fn idx(i: usize, j: usize) -> usize {
501            match (i, j) {
502                (0, 0) => 0,
503                (0, 1) => 1,
504                (0, 2) => 2,
505                (0, 3) => 3,
506                (1, 0) => 11,
507                (1, 1) => 12,
508                (1, 2) => 13,
509                (1, 3) => 4,
510                (2, 0) => 10,
511                (2, 1) => 15,
512                (2, 2) => 14,
513                (2, 3) => 5,
514                (3, 0) => 9,
515                (3, 1) => 8,
516                (3, 2) => 7,
517                (3, 3) => 6,
518                _ => panic!("Invalid index"),
519            }
520        }
521
522        for i in 0..4 {
523            for j in 0..4 {
524                let control_point_idx = idx(i, j);
525                let basis = Self::bernstein(i, u) * Self::bernstein(j, v);
526
527                x += self.control_points[control_point_idx].x * basis;
528                y += self.control_points[control_point_idx].y * basis;
529            }
530        }
531
532        Point::new(x, y)
533    }
534
535    /// Approximate the tensor product patch mesh by triangles.
536    pub fn to_triangles(&self, buffer: &mut Vec<Triangle>) {
537        generate_patch_triangles(|p| self.map_coordinate(p), |p| self.interpolate(p), buffer);
538    }
539
540    /// Get the interpolated colors of the point from the patch.
541    pub fn interpolate(&self, pos: Point) -> ColorComponents {
542        let (u, v) = (pos.x, pos.y);
543        let (c0, c1, c2, c3) = {
544            (
545                &self.colors[0],
546                &self.colors[1],
547                &self.colors[2],
548                &self.colors[3],
549            )
550        };
551
552        let mut result = SmallVec::new();
553        for i in 0..c0.len() {
554            let val = (1.0 - u) * (1.0 - v) * c0[i] as f64
555                + u * (1.0 - v) * c3[i] as f64
556                + u * v * c2[i] as f64
557                + (1.0 - u) * v * c1[i] as f64;
558            result.push(val as f32);
559        }
560
561        result
562    }
563}
564
565fn read_free_form_triangles(
566    data: &[u8],
567    bpf: u8,
568    bp_cord: u8,
569    bp_comp: u8,
570    has_function: bool,
571    decode: &[f32],
572) -> Option<Vec<Triangle>> {
573    let mut triangles = vec![];
574
575    let ([x_min, x_max, y_min, y_max], decode) = split_decode(decode)?;
576    let mut reader = BitReader::new(data);
577    let helpers = InterpolationHelpers::new(bp_cord, bp_comp, x_min, x_max, y_min, y_max);
578
579    let read_single = |reader: &mut BitReader<'_>| -> Option<TriangleVertex> {
580        helpers.read_triangle_vertex(reader, bpf, has_function, decode)
581    };
582
583    let mut a = None;
584    let mut b = None;
585    let mut c = None;
586
587    loop {
588        let Some(first) = read_single(&mut reader) else {
589            break;
590        };
591
592        if first.flag == 0 {
593            let second = read_single(&mut reader)?;
594            let third = read_single(&mut reader)?;
595
596            a = Some(first.clone());
597            b = Some(second.clone());
598            c = Some(third.clone());
599        } else if first.flag == 1 {
600            a = Some(b.clone()?);
601            b = Some(c.clone()?);
602            c = Some(first);
603        } else if first.flag == 2 {
604            b = Some(c.clone()?);
605            c = Some(first);
606        }
607
608        let (p0, p1, p2) = (a.clone()?, b.clone()?, c.clone()?);
609
610        if p0.point.nearly_same(p1.point) || p1.point.nearly_same(p2.point) {
611            continue;
612        }
613
614        triangles.push(Triangle::new(a.clone()?, b.clone()?, c.clone()?));
615    }
616
617    Some(triangles)
618}
619
620/// Common interpolation functions used across different shading types.
621struct InterpolationHelpers {
622    bp_coord: u8,
623    bp_comp: u8,
624    coord_max: f32,
625    comp_max: f32,
626    x_min: f32,
627    x_max: f32,
628    y_min: f32,
629    y_max: f32,
630}
631
632impl InterpolationHelpers {
633    fn new(bp_coord: u8, bp_comp: u8, x_min: f32, x_max: f32, y_min: f32, y_max: f32) -> Self {
634        let coord_max = 2.0_f32.powi(bp_coord as i32) - 1.0;
635        let comp_max = 2.0_f32.powi(bp_comp as i32) - 1.0;
636        Self {
637            bp_coord,
638            bp_comp,
639            coord_max,
640            comp_max,
641            x_min,
642            x_max,
643            y_min,
644            y_max,
645        }
646    }
647
648    fn interpolate_coord(&self, n: u32, d_min: f32, d_max: f32) -> f32 {
649        interpolate(n as f32, 0.0, self.coord_max, d_min, d_max)
650    }
651
652    fn interpolate_comp(&self, n: u32, d_min: f32, d_max: f32) -> f32 {
653        interpolate(n as f32, 0.0, self.comp_max, d_min, d_max)
654    }
655
656    fn read_point(&self, reader: &mut BitReader<'_>) -> Option<Point> {
657        let x = self.interpolate_coord(reader.read(self.bp_coord)?, self.x_min, self.x_max);
658        let y = self.interpolate_coord(reader.read(self.bp_coord)?, self.y_min, self.y_max);
659        Some(Point::new(x as f64, y as f64))
660    }
661
662    fn read_colors(
663        &self,
664        reader: &mut BitReader<'_>,
665        has_function: bool,
666        decode: &[f32],
667    ) -> Option<ColorComponents> {
668        let mut colors = smallvec![];
669        if has_function {
670            colors.push(self.interpolate_comp(
671                reader.read(self.bp_comp)?,
672                *decode.first()?,
673                *decode.get(1)?,
674            ));
675        } else {
676            let num_components = decode.len() / 2;
677            for (_, decode) in (0..num_components).zip(decode.chunks_exact(2)) {
678                colors.push(self.interpolate_comp(
679                    reader.read(self.bp_comp)?,
680                    decode[0],
681                    decode[1],
682                ));
683            }
684        }
685        Some(colors)
686    }
687
688    fn read_triangle_vertex(
689        &self,
690        reader: &mut BitReader<'_>,
691        bpf: u8,
692        has_function: bool,
693        decode: &[f32],
694    ) -> Option<TriangleVertex> {
695        let flag = reader.read(bpf)?;
696        let point = self.read_point(reader)?;
697        let colors = self.read_colors(reader, has_function, decode)?;
698        reader.align();
699
700        Some(TriangleVertex {
701            flag,
702            point,
703            colors,
704        })
705    }
706}
707
708/// Split decode array into coordinate bounds and component decode values.
709fn split_decode(decode: &[f32]) -> Option<([f32; 4], &[f32])> {
710    decode.split_first_chunk::<4>().map(|(a, b)| (*a, b))
711}
712
713/// Generate triangles from a grid of points using a mapping function.
714fn generate_patch_triangles<F, I>(map_coordinate: F, interpolate: I, buffer: &mut Vec<Triangle>)
715where
716    F: Fn(Point) -> Point,
717    I: Fn(Point) -> ColorComponents,
718{
719    const GRID_SIZE: usize = 20;
720    let mut grid = vec![vec![Point::ZERO; GRID_SIZE]; GRID_SIZE];
721
722    // Create grid by mapping unit square coordinates.
723    for i in 0..GRID_SIZE {
724        for j in 0..GRID_SIZE {
725            let u = i as f64 / (GRID_SIZE - 1) as f64; // 0.0 to 1.0 (left to right).
726            let v = j as f64 / (GRID_SIZE - 1) as f64; // 0.0 to 1.0 (top to bottom).
727
728            // Map unit square coordinate to patch coordinate.
729            let unit_point = Point::new(u, v);
730            grid[i][j] = map_coordinate(unit_point);
731        }
732    }
733
734    for i in 0..(GRID_SIZE - 1) {
735        for j in 0..(GRID_SIZE - 1) {
736            let p00 = grid[i][j];
737            let p10 = grid[i + 1][j];
738            let p01 = grid[i][j + 1];
739            let p11 = grid[i + 1][j + 1];
740
741            // Calculate unit square coordinates for color interpolation.
742            let u0 = i as f64 / (GRID_SIZE - 1) as f64;
743            let u1 = (i + 1) as f64 / (GRID_SIZE - 1) as f64;
744            let v0 = j as f64 / (GRID_SIZE - 1) as f64;
745            let v1 = (j + 1) as f64 / (GRID_SIZE - 1) as f64;
746
747            // Create triangle vertices with interpolated colors.
748            let v00 = TriangleVertex {
749                flag: 0,
750                point: p00,
751                colors: interpolate(Point::new(u0, v0)),
752            };
753            let v10 = TriangleVertex {
754                flag: 0,
755                point: p10,
756                colors: interpolate(Point::new(u1, v0)),
757            };
758            let v01 = TriangleVertex {
759                flag: 0,
760                point: p01,
761                colors: interpolate(Point::new(u0, v1)),
762            };
763            let v11 = TriangleVertex {
764                flag: 0,
765                point: p11,
766                colors: interpolate(Point::new(u1, v1)),
767            };
768
769            let inflate_point = |p: Point, mid: Point| -> Point {
770                const INFLATION_FACTOR: f64 = 1.025;
771                mid + (p - mid) * INFLATION_FACTOR
772            };
773
774            // Converting to triangles is lossy and can lead to gaps. To make this a bit better,
775            // we slightly inflate the triangles.
776            let inflate = |mut triangle: Triangle| {
777                let mid = triangle.kurbo_tri.centroid();
778                triangle.p0.point = inflate_point(triangle.p0.point, mid);
779                triangle.p1.point = inflate_point(triangle.p1.point, mid);
780                triangle.p2.point = inflate_point(triangle.p2.point, mid);
781
782                triangle
783            };
784
785            buffer.push(inflate(Triangle::new(
786                v00.clone(),
787                v10.clone(),
788                v01.clone(),
789            )));
790            buffer.push(inflate(Triangle::new(
791                v10.clone(),
792                v11.clone(),
793                v01.clone(),
794            )));
795        }
796    }
797}
798
799fn read_lattice_triangles(
800    data: &[u8],
801    bp_cord: u8,
802    bp_comp: u8,
803    has_function: bool,
804    vertices_per_row: u32,
805    decode: &[f32],
806) -> Option<Vec<Triangle>> {
807    let mut lattices = vec![];
808
809    let ([x_min, x_max, y_min, y_max], decode) = split_decode(decode)?;
810    let mut reader = BitReader::new(data);
811    let helpers = InterpolationHelpers::new(bp_cord, bp_comp, x_min, x_max, y_min, y_max);
812
813    let read_single = |reader: &mut BitReader<'_>| -> Option<TriangleVertex> {
814        let point = helpers.read_point(reader)?;
815        let colors = helpers.read_colors(reader, has_function, decode)?;
816        reader.align();
817
818        Some(TriangleVertex {
819            flag: 0,
820            point,
821            colors,
822        })
823    };
824
825    'outer: loop {
826        let mut single_row = vec![];
827
828        for _ in 0..vertices_per_row {
829            let Some(next) = read_single(&mut reader) else {
830                break 'outer;
831            };
832
833            single_row.push(next);
834        }
835
836        lattices.push(single_row);
837    }
838
839    let mut triangles = vec![];
840
841    for i in 0..lattices.len().saturating_sub(1) {
842        for j in 0..(vertices_per_row as usize).saturating_sub(1) {
843            triangles.push(Triangle::new(
844                lattices[i][j].clone(),
845                lattices[i + 1][j].clone(),
846                lattices[i][j + 1].clone(),
847            ));
848
849            triangles.push(Triangle::new(
850                lattices[i + 1][j + 1].clone(),
851                lattices[i + 1][j].clone(),
852                lattices[i][j + 1].clone(),
853            ));
854        }
855    }
856
857    Some(triangles)
858}
859
860fn read_coons_patch_mesh(
861    data: &[u8],
862    bpf: u8,
863    bp_coord: u8,
864    bp_comp: u8,
865    has_function: bool,
866    decode: &[f32],
867) -> Option<Vec<CoonsPatch>> {
868    read_patch_mesh(
869        data,
870        bpf,
871        bp_coord,
872        bp_comp,
873        has_function,
874        decode,
875        12,
876        |control_points, colors| {
877            let mut coons_points = [Point::ZERO; 12];
878            coons_points.copy_from_slice(&control_points[0..12]);
879            CoonsPatch {
880                control_points: coons_points,
881                colors,
882            }
883        },
884    )
885}
886
887/// Generic patch mesh reading function that works for both Coons and Tensor Product patches.
888#[allow(clippy::too_many_arguments)]
889fn read_patch_mesh<P, F>(
890    data: &[u8],
891    bpf: u8,
892    bp_coord: u8,
893    bp_comp: u8,
894    has_function: bool,
895    decode: &[f32],
896    control_points_count: usize,
897    create_patch: F,
898) -> Option<Vec<P>>
899where
900    F: Fn([Point; 16], [ColorComponents; 4]) -> P,
901{
902    let ([x_min, x_max, y_min, y_max], decode) = split_decode(decode)?;
903    let mut reader = BitReader::new(data);
904    let helpers = InterpolationHelpers::new(bp_coord, bp_comp, x_min, x_max, y_min, y_max);
905
906    let read_colors = |reader: &mut BitReader<'_>| -> Option<ColorComponents> {
907        helpers.read_colors(reader, has_function, decode)
908    };
909
910    let mut prev_patch_points: Option<Vec<Point>> = None;
911    let mut prev_patch_colors: Option<[ColorComponents; 4]> = None;
912    let mut patches = vec![];
913
914    while let Some(flag) = reader.read(bpf) {
915        let mut control_points = vec![Point::ZERO; 16]; // Always allocate 16, use subset as needed.
916        let mut colors = [smallvec![], smallvec![], smallvec![], smallvec![]];
917
918        match flag {
919            0 => {
920                for i in 0..control_points_count {
921                    control_points[i] = helpers.read_point(&mut reader)?;
922                }
923
924                for i in 0..4 {
925                    colors[i] = read_colors(&mut reader)?;
926                }
927
928                prev_patch_points = Some(control_points.clone());
929                prev_patch_colors = Some(colors.clone());
930            }
931            1..=3 => {
932                let prev_points = prev_patch_points.as_ref()?;
933                let prev_colors = prev_patch_colors.as_ref()?;
934
935                copy_patch_control_points(flag, prev_points, &mut control_points);
936
937                match flag {
938                    1 => {
939                        colors[0] = prev_colors[1].clone();
940                        colors[1] = prev_colors[2].clone();
941                    }
942                    2 => {
943                        colors[0] = prev_colors[2].clone();
944                        colors[1] = prev_colors[3].clone();
945                    }
946                    3 => {
947                        colors[0] = prev_colors[3].clone();
948                        colors[1] = prev_colors[0].clone();
949                    }
950                    _ => unreachable!(),
951                }
952
953                for i in 4..control_points_count {
954                    control_points[i] = helpers.read_point(&mut reader)?;
955                }
956
957                colors[2] = read_colors(&mut reader)?;
958                colors[3] = read_colors(&mut reader)?;
959
960                prev_patch_points = Some(control_points.clone());
961                prev_patch_colors = Some(colors.clone());
962            }
963            _ => break,
964        }
965
966        let mut fixed_points = [Point::ZERO; 16];
967        for i in 0..16 {
968            if i < control_points.len() {
969                fixed_points[i] = control_points[i];
970            }
971        }
972
973        patches.push(create_patch(fixed_points, colors));
974    }
975    Some(patches)
976}
977
978fn copy_patch_control_points(
979    flag: u32,
980    prev_control_points: &[Point],
981    control_points: &mut [Point],
982) {
983    match flag {
984        1 => {
985            control_points[0] = prev_control_points[3];
986            control_points[1] = prev_control_points[4];
987            control_points[2] = prev_control_points[5];
988            control_points[3] = prev_control_points[6];
989        }
990        2 => {
991            control_points[0] = prev_control_points[6];
992            control_points[1] = prev_control_points[7];
993            control_points[2] = prev_control_points[8];
994            control_points[3] = prev_control_points[9];
995        }
996        3 => {
997            control_points[0] = prev_control_points[9];
998            control_points[1] = prev_control_points[10];
999            control_points[2] = prev_control_points[11];
1000            control_points[3] = prev_control_points[0];
1001        }
1002        _ => {}
1003    }
1004}
1005
1006fn read_tensor_product_patch_mesh(
1007    data: &[u8],
1008    bpf: u8,
1009    bp_coord: u8,
1010    bp_comp: u8,
1011    has_function: bool,
1012    decode: &[f32],
1013) -> Option<Vec<TensorProductPatch>> {
1014    read_patch_mesh(
1015        data,
1016        bpf,
1017        bp_coord,
1018        bp_comp,
1019        has_function,
1020        decode,
1021        16,
1022        |control_points, colors| TensorProductPatch {
1023            control_points,
1024            colors,
1025        },
1026    )
1027}
1028
1029fn read_function(dict: &Dict<'_>, color_space: &ColorSpace) -> Option<ShadingFunction> {
1030    if let Some(arr) = dict.get::<Array<'_>>(FUNCTION) {
1031        let arr: Option<SmallVec<_>> = arr
1032            .iter::<Object<'_>>()
1033            .map(|o| Function::new(&o))
1034            .collect();
1035        let arr = arr?;
1036
1037        if arr.len() != color_space.num_components() as usize {
1038            warn!("function array of shading has wrong size");
1039
1040            return None;
1041        }
1042
1043        Some(ShadingFunction::Multiple(arr))
1044    } else if let Some(obj) = dict.get::<Object<'_>>(FUNCTION) {
1045        Some(ShadingFunction::Single(Function::new(&obj)?))
1046    } else {
1047        None
1048    }
1049}