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