Skip to main content

pdf_interpret/
shading.rs

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