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