Skip to main content

stet_graphics/
mesh_shading.rs

1// stet - A PostScript Interpreter
2// Copyright (c) 2026 Scott Bowman
3// SPDX-License-Identifier: Apache-2.0 OR MIT
4
5//! Binary mesh and patch parsers for shading Types 4-7.
6//!
7//! Parses the binary DataSource format defined in PLRM §4.9.1. Supports
8//! free-form Gouraud triangle meshes (Type 4), lattice-form Gouraud meshes
9//! (Type 5), Coons patch meshes (Type 6), and tensor-product patch meshes (Type 7).
10
11use crate::color::DeviceColor;
12use crate::device::{ShadingPatch, ShadingTriangle, ShadingVertex};
13
14/// Reads arbitrary bit-width unsigned integers from a byte buffer.
15pub struct BitReader<'a> {
16    data: &'a [u8],
17    bit_pos: usize,
18}
19
20impl<'a> BitReader<'a> {
21    /// Create a new BitReader over the given byte slice.
22    pub fn new(data: &'a [u8]) -> Self {
23        Self { data, bit_pos: 0 }
24    }
25
26    /// Read `n` bits as an unsigned integer (big-endian, MSB first).
27    pub fn read(&mut self, n: usize) -> Option<u32> {
28        if n == 0 {
29            return Some(0);
30        }
31        if n > 32 {
32            return None;
33        }
34        let end_bit = self.bit_pos + n;
35        if end_bit > self.data.len() * 8 {
36            return None;
37        }
38
39        let bit_offset = self.bit_pos & 7;
40        let byte_idx = self.bit_pos >> 3;
41
42        // Fast paths for byte-aligned reads
43        if bit_offset == 0 {
44            let val = match n {
45                8 => {
46                    self.bit_pos = end_bit;
47                    return Some(self.data[byte_idx] as u32);
48                }
49                16 => {
50                    let v = ((self.data[byte_idx] as u32) << 8) | (self.data[byte_idx + 1] as u32);
51                    self.bit_pos = end_bit;
52                    return Some(v);
53                }
54                24 => {
55                    let v = ((self.data[byte_idx] as u32) << 16)
56                        | ((self.data[byte_idx + 1] as u32) << 8)
57                        | (self.data[byte_idx + 2] as u32);
58                    self.bit_pos = end_bit;
59                    return Some(v);
60                }
61                32 => {
62                    let v = ((self.data[byte_idx] as u32) << 24)
63                        | ((self.data[byte_idx + 1] as u32) << 16)
64                        | ((self.data[byte_idx + 2] as u32) << 8)
65                        | (self.data[byte_idx + 3] as u32);
66                    self.bit_pos = end_bit;
67                    return Some(v);
68                }
69                _ => None,
70            };
71            if let Some(v) = val {
72                return Some(v);
73            }
74        }
75
76        // General case: accumulate bytes spanning the bit range
77        let end_byte = (end_bit + 7) >> 3;
78        let mut accum: u64 = 0;
79        for &b in &self.data[byte_idx..end_byte] {
80            accum = (accum << 8) | b as u64;
81        }
82        // Total bits in the accumulated range
83        let total_bits = (end_byte - byte_idx) * 8;
84        // We want n bits starting at bit_offset within the accumulated value
85        let shift = total_bits - bit_offset - n;
86        let mask = (1u64 << n) - 1;
87        let result = ((accum >> shift) & mask) as u32;
88
89        self.bit_pos = end_bit;
90        Some(result)
91    }
92
93    /// Whether all data has been consumed.
94    pub fn exhausted(&self) -> bool {
95        self.bit_pos >> 3 >= self.data.len()
96    }
97}
98
99/// Decode a raw integer to a float using the Decode array mapping.
100/// `value = decode_min + raw * (decode_max - decode_min) / (2^bits - 1)`
101#[inline]
102fn decode_value(raw: u32, scale: f64, min: f64) -> f64 {
103    min + raw as f64 * scale
104}
105
106/// Precompute decode scale: `(max - min) / ((1 << bits) - 1)`.
107#[inline]
108fn decode_scale(bits: usize, min: f64, max: f64) -> f64 {
109    let max_val = ((1u64 << bits) - 1) as f64;
110    if max_val == 0.0 {
111        0.0
112    } else {
113        (max - min) / max_val
114    }
115}
116
117/// Convert color components to DeviceColor based on component count.
118/// Uses ICC CMYK profile when available for 4-component colors.
119fn components_to_color(comps: &[f64]) -> (DeviceColor, Vec<f64>) {
120    let raw = comps.to_vec();
121    let color = match comps.len() {
122        1 => DeviceColor::from_gray(comps[0].clamp(0.0, 1.0)),
123        3 => DeviceColor::from_rgb(
124            comps[0].clamp(0.0, 1.0),
125            comps[1].clamp(0.0, 1.0),
126            comps[2].clamp(0.0, 1.0),
127        ),
128        4 => DeviceColor::from_cmyk(
129            comps[0].clamp(0.0, 1.0),
130            comps[1].clamp(0.0, 1.0),
131            comps[2].clamp(0.0, 1.0),
132            comps[3].clamp(0.0, 1.0),
133        ),
134        _ => {
135            if comps.len() >= 3 {
136                DeviceColor::from_rgb(
137                    comps[0].clamp(0.0, 1.0),
138                    comps[1].clamp(0.0, 1.0),
139                    comps[2].clamp(0.0, 1.0),
140                )
141            } else {
142                DeviceColor::from_gray(0.0)
143            }
144        }
145    };
146    (color, raw)
147}
148
149/// Parse a Type 4 free-form Gouraud-shaded triangle mesh from binary data.
150///
151/// # Parameters
152/// - `data`: Raw binary DataSource bytes
153/// - `bpc`: BitsPerCoordinate
154/// - `bpco`: BitsPerComponent (color)
155/// - `bpfl`: BitsPerFlag
156/// - `decode`: Decode array [xmin, xmax, ymin, ymax, c0min, c0max, ...]
157/// - `n_comps`: Number of color components
158pub fn parse_type4_mesh(
159    data: &[u8],
160    bpc: usize,
161    bpco: usize,
162    bpfl: usize,
163    decode: &[f64],
164    n_comps: usize,
165) -> Vec<ShadingTriangle> {
166    let mut reader = BitReader::new(data);
167    let mut triangles = Vec::new();
168
169    // Precompute decode scales
170    let x_min = decode[0];
171    let x_scale = decode_scale(bpc, decode[0], decode[1]);
172    let y_min = decode[2];
173    let y_scale = decode_scale(bpc, decode[2], decode[3]);
174
175    let mut color_params: Vec<(f64, f64)> = Vec::with_capacity(n_comps);
176    for i in 0..n_comps {
177        let idx = 4 + i * 2;
178        let c_min = decode.get(idx).copied().unwrap_or(0.0);
179        let c_max = decode.get(idx + 1).copied().unwrap_or(1.0);
180        color_params.push((c_min, decode_scale(bpco, c_min, c_max)));
181    }
182
183    let read_vertex = |reader: &mut BitReader| -> Option<(u32, ShadingVertex)> {
184        let flag = reader.read(bpfl)?;
185        let raw_x = reader.read(bpc)?;
186        let raw_y = reader.read(bpc)?;
187        let x = decode_value(raw_x, x_scale, x_min);
188        let y = decode_value(raw_y, y_scale, y_min);
189        let mut comps = vec![0.0f64; n_comps];
190        for (i, comp) in comps.iter_mut().enumerate() {
191            let raw = reader.read(bpco)?;
192            *comp = decode_value(raw, color_params[i].1, color_params[i].0);
193        }
194        let (color, raw_components) = components_to_color(&comps);
195        Some((
196            flag,
197            ShadingVertex {
198                x,
199                y,
200                color,
201                raw_components,
202            },
203        ))
204    };
205
206    // Track the previous triangle's A, B, C vertices for edge connectivity.
207    // Flag 1 reuses edge BC; flag 2 reuses edge AC.
208    let mut prev_a: Option<ShadingVertex> = None;
209    let mut prev_b: Option<ShadingVertex> = None;
210    let mut prev_c: Option<ShadingVertex> = None;
211
212    while !reader.exhausted() {
213        let Some((flag, vertex)) = read_vertex(&mut reader) else {
214            break;
215        };
216
217        match flag {
218            0 => {
219                // New independent triangle: need 2 more vertices
220                let a = vertex;
221                let Some((_, b)) = read_vertex(&mut reader) else {
222                    break;
223                };
224                let Some((_, c)) = read_vertex(&mut reader) else {
225                    break;
226                };
227                triangles.push(ShadingTriangle {
228                    v0: a.clone(),
229                    v1: b.clone(),
230                    v2: c.clone(),
231                });
232                prev_a = Some(a);
233                prev_b = Some(b);
234                prev_c = Some(c);
235            }
236            1 => {
237                // Share edge BC of previous triangle ABC → new triangle BCD
238                if let (Some(b), Some(c)) = (&prev_b, &prev_c) {
239                    let d = vertex;
240                    triangles.push(ShadingTriangle {
241                        v0: b.clone(),
242                        v1: c.clone(),
243                        v2: d.clone(),
244                    });
245                    prev_a = Some(b.clone());
246                    prev_b = Some(c.clone());
247                    prev_c = Some(d);
248                }
249            }
250            2 => {
251                // Share edge AC of previous triangle ABC → new triangle ACD
252                if let (Some(a), Some(c)) = (&prev_a, &prev_c) {
253                    let d = vertex;
254                    triangles.push(ShadingTriangle {
255                        v0: a.clone(),
256                        v1: c.clone(),
257                        v2: d.clone(),
258                    });
259                    prev_a = Some(a.clone());
260                    prev_b = Some(c.clone());
261                    prev_c = Some(d);
262                }
263            }
264            _ => {}
265        }
266    }
267
268    triangles
269}
270
271/// Parse a Type 5 lattice-form Gouraud-shaded triangle mesh from binary data.
272///
273/// # Parameters
274/// - `data`: Raw binary DataSource bytes
275/// - `bpc`: BitsPerCoordinate
276/// - `bpco`: BitsPerComponent (color)
277/// - `decode`: Decode array [xmin, xmax, ymin, ymax, c0min, c0max, ...]
278/// - `n_comps`: Number of color components
279/// - `verts_per_row`: VerticesPerRow
280pub fn parse_type5_mesh(
281    data: &[u8],
282    bpc: usize,
283    bpco: usize,
284    decode: &[f64],
285    n_comps: usize,
286    verts_per_row: usize,
287) -> Vec<ShadingTriangle> {
288    let mut reader = BitReader::new(data);
289    let mut all_vertices = Vec::new();
290
291    // Precompute decode scales
292    let x_min = decode[0];
293    let x_scale = decode_scale(bpc, decode[0], decode[1]);
294    let y_min = decode[2];
295    let y_scale = decode_scale(bpc, decode[2], decode[3]);
296
297    let mut color_params: Vec<(f64, f64)> = Vec::with_capacity(n_comps);
298    for i in 0..n_comps {
299        let idx = 4 + i * 2;
300        let c_min = decode.get(idx).copied().unwrap_or(0.0);
301        let c_max = decode.get(idx + 1).copied().unwrap_or(1.0);
302        color_params.push((c_min, decode_scale(bpco, c_min, c_max)));
303    }
304
305    // Read all vertices (no flags in Type 5)
306    while !reader.exhausted() {
307        let Some(raw_x) = reader.read(bpc) else {
308            break;
309        };
310        let Some(raw_y) = reader.read(bpc) else {
311            break;
312        };
313        let x = decode_value(raw_x, x_scale, x_min);
314        let y = decode_value(raw_y, y_scale, y_min);
315        let mut comps = vec![0.0f64; n_comps];
316        let mut ok = true;
317        for (i, comp) in comps.iter_mut().enumerate() {
318            let Some(raw) = reader.read(bpco) else {
319                ok = false;
320                break;
321            };
322            *comp = decode_value(raw, color_params[i].1, color_params[i].0);
323        }
324        if !ok {
325            break;
326        }
327        let (color, raw_components) = components_to_color(&comps);
328        all_vertices.push(ShadingVertex {
329            x,
330            y,
331            color,
332            raw_components,
333        });
334    }
335
336    // Convert lattice to triangles: each quad → 2 triangles
337    let mut triangles = Vec::new();
338    if verts_per_row < 2 {
339        return triangles;
340    }
341    let num_rows = all_vertices.len() / verts_per_row;
342    if num_rows < 2 {
343        return triangles;
344    }
345
346    for row in 0..num_rows - 1 {
347        for col in 0..verts_per_row - 1 {
348            let i00 = row * verts_per_row + col;
349            let i10 = i00 + 1;
350            let i01 = i00 + verts_per_row;
351            let i11 = i01 + 1;
352            if i11 >= all_vertices.len() {
353                break;
354            }
355            // Upper-left triangle
356            triangles.push(ShadingTriangle {
357                v0: all_vertices[i00].clone(),
358                v1: all_vertices[i10].clone(),
359                v2: all_vertices[i01].clone(),
360            });
361            // Lower-right triangle
362            triangles.push(ShadingTriangle {
363                v0: all_vertices[i10].clone(),
364                v1: all_vertices[i11].clone(),
365                v2: all_vertices[i01].clone(),
366            });
367        }
368    }
369
370    triangles
371}
372
373/// Parse a Type 6 Coons patch mesh from binary data.
374pub fn parse_type6_patches(
375    data: &[u8],
376    bpc: usize,
377    bpco: usize,
378    bpfl: usize,
379    decode: &[f64],
380    n_comps: usize,
381) -> Vec<ShadingPatch> {
382    let mut reader = BitReader::new(data);
383    let mut patches = Vec::new();
384
385    let x_min = decode[0];
386    let x_scale = decode_scale(bpc, decode[0], decode[1]);
387    let y_min = decode[2];
388    let y_scale = decode_scale(bpc, decode[2], decode[3]);
389
390    let mut color_params: Vec<(f64, f64)> = Vec::with_capacity(n_comps);
391    for i in 0..n_comps {
392        let idx = 4 + i * 2;
393        let c_min = decode.get(idx).copied().unwrap_or(0.0);
394        let c_max = decode.get(idx + 1).copied().unwrap_or(1.0);
395        color_params.push((c_min, decode_scale(bpco, c_min, c_max)));
396    }
397
398    let read_point = |reader: &mut BitReader| -> Option<(f64, f64)> {
399        let raw_x = reader.read(bpc)?;
400        let raw_y = reader.read(bpc)?;
401        Some((
402            decode_value(raw_x, x_scale, x_min),
403            decode_value(raw_y, y_scale, y_min),
404        ))
405    };
406
407    let read_color = |reader: &mut BitReader| -> Option<(DeviceColor, Vec<f64>)> {
408        let mut comps = vec![0.0f64; n_comps];
409        for (i, comp) in comps.iter_mut().enumerate() {
410            let raw = reader.read(bpco)?;
411            *comp = decode_value(raw, color_params[i].1, color_params[i].0);
412        }
413        Some(components_to_color(&comps))
414    };
415
416    while !reader.exhausted() {
417        let Some(flag) = reader.read(bpfl) else {
418            break;
419        };
420
421        match flag {
422            0 => {
423                // Independent patch: 12 points + 4 colors
424                let mut points = Vec::with_capacity(12);
425                let mut colors = Vec::with_capacity(4);
426                let mut raw_colors_vec = Vec::with_capacity(4);
427                let mut ok = true;
428                for _ in 0..12 {
429                    if let Some(pt) = read_point(&mut reader) {
430                        points.push(pt);
431                    } else {
432                        ok = false;
433                        break;
434                    }
435                }
436                if ok {
437                    for _ in 0..4 {
438                        if let Some((c, rc)) = read_color(&mut reader) {
439                            colors.push(c);
440                            raw_colors_vec.push(rc);
441                        } else {
442                            ok = false;
443                            break;
444                        }
445                    }
446                }
447                if ok && points.len() == 12 && colors.len() == 4 {
448                    patches.push(ShadingPatch {
449                        points,
450                        colors: [
451                            colors[0].clone(),
452                            colors[1].clone(),
453                            colors[2].clone(),
454                            colors[3].clone(),
455                        ],
456                        raw_colors: [
457                            raw_colors_vec[0].clone(),
458                            raw_colors_vec[1].clone(),
459                            raw_colors_vec[2].clone(),
460                            raw_colors_vec[3].clone(),
461                        ],
462                    });
463                }
464            }
465            1..=3 => {
466                // Inherit one side from previous patch
467                if patches.is_empty() {
468                    break;
469                }
470                let prev = patches.last().unwrap().clone();
471
472                // Inherit the full 4-point side from the previous patch.
473                // Side 1: pts[0..4], Side 2: pts[3..7], Side 3: pts[6..10],
474                // Side 4: pts[9..12]+pts[0] (wraps).
475                // PDF spec: inherit one side from the previous patch.
476                // Flag 1: prev edge 1 (P3→…→P6, C1,C2) becomes new edge 0
477                // Flag 2: prev edge 2 (P6→…→P9, C2,C3) becomes new edge 0
478                // Flag 3: prev edge 3 (P9→…→P0, C3,C0) becomes new edge 0
479                let (inherited_pts, inherited_colors, inherited_raw) = match flag {
480                    1 => (
481                        prev.points[3..7].to_vec(),
482                        [prev.colors[1].clone(), prev.colors[2].clone()],
483                        [prev.raw_colors[1].clone(), prev.raw_colors[2].clone()],
484                    ),
485                    2 => (
486                        prev.points[6..10].to_vec(),
487                        [prev.colors[2].clone(), prev.colors[3].clone()],
488                        [prev.raw_colors[2].clone(), prev.raw_colors[3].clone()],
489                    ),
490                    3 => (
491                        vec![
492                            prev.points[9],
493                            prev.points[10],
494                            prev.points[11],
495                            prev.points[0],
496                        ],
497                        [prev.colors[3].clone(), prev.colors[0].clone()],
498                        [prev.raw_colors[3].clone(), prev.raw_colors[0].clone()],
499                    ),
500                    _ => unreachable!(),
501                };
502
503                // Read remaining 8 points + 2 colors
504                let mut points = inherited_pts;
505                let mut ok = true;
506                for _ in 0..8 {
507                    if let Some(pt) = read_point(&mut reader) {
508                        points.push(pt);
509                    } else {
510                        ok = false;
511                        break;
512                    }
513                }
514                let mut colors = vec![inherited_colors[0].clone(), inherited_colors[1].clone()];
515                let mut raw_colors_vec = vec![inherited_raw[0].clone(), inherited_raw[1].clone()];
516                if ok {
517                    for _ in 0..2 {
518                        if let Some((c, rc)) = read_color(&mut reader) {
519                            colors.push(c);
520                            raw_colors_vec.push(rc);
521                        } else {
522                            ok = false;
523                            break;
524                        }
525                    }
526                }
527                if ok && points.len() == 12 && colors.len() == 4 {
528                    patches.push(ShadingPatch {
529                        points,
530                        colors: [
531                            colors[0].clone(),
532                            colors[1].clone(),
533                            colors[2].clone(),
534                            colors[3].clone(),
535                        ],
536                        raw_colors: [
537                            raw_colors_vec[0].clone(),
538                            raw_colors_vec[1].clone(),
539                            raw_colors_vec[2].clone(),
540                            raw_colors_vec[3].clone(),
541                        ],
542                    });
543                }
544            }
545            _ => break,
546        }
547    }
548
549    patches
550}
551
552/// Parse a Type 7 tensor-product patch mesh from binary data.
553pub fn parse_type7_patches(
554    data: &[u8],
555    bpc: usize,
556    bpco: usize,
557    bpfl: usize,
558    decode: &[f64],
559    n_comps: usize,
560) -> Vec<ShadingPatch> {
561    let mut reader = BitReader::new(data);
562    let mut patches = Vec::new();
563
564    let x_min = decode[0];
565    let x_scale = decode_scale(bpc, decode[0], decode[1]);
566    let y_min = decode[2];
567    let y_scale = decode_scale(bpc, decode[2], decode[3]);
568
569    let mut color_params: Vec<(f64, f64)> = Vec::with_capacity(n_comps);
570    for i in 0..n_comps {
571        let idx = 4 + i * 2;
572        let c_min = decode.get(idx).copied().unwrap_or(0.0);
573        let c_max = decode.get(idx + 1).copied().unwrap_or(1.0);
574        color_params.push((c_min, decode_scale(bpco, c_min, c_max)));
575    }
576
577    let read_point = |reader: &mut BitReader| -> Option<(f64, f64)> {
578        let raw_x = reader.read(bpc)?;
579        let raw_y = reader.read(bpc)?;
580        Some((
581            decode_value(raw_x, x_scale, x_min),
582            decode_value(raw_y, y_scale, y_min),
583        ))
584    };
585
586    let read_color = |reader: &mut BitReader| -> Option<(DeviceColor, Vec<f64>)> {
587        let mut comps = vec![0.0f64; n_comps];
588        for (i, comp) in comps.iter_mut().enumerate() {
589            let raw = reader.read(bpco)?;
590            *comp = decode_value(raw, color_params[i].1, color_params[i].0);
591        }
592        Some(components_to_color(&comps))
593    };
594
595    while !reader.exhausted() {
596        let Some(flag) = reader.read(bpfl) else {
597            break;
598        };
599
600        match flag {
601            0 => {
602                // Independent patch: 16 points + 4 colors
603                let mut points = Vec::with_capacity(16);
604                let mut colors = Vec::with_capacity(4);
605                let mut raw_colors_vec = Vec::with_capacity(4);
606                let mut ok = true;
607                for _ in 0..16 {
608                    if let Some(pt) = read_point(&mut reader) {
609                        points.push(pt);
610                    } else {
611                        ok = false;
612                        break;
613                    }
614                }
615                if ok {
616                    for _ in 0..4 {
617                        if let Some((c, rc)) = read_color(&mut reader) {
618                            colors.push(c);
619                            raw_colors_vec.push(rc);
620                        } else {
621                            ok = false;
622                            break;
623                        }
624                    }
625                }
626                if ok && points.len() == 16 && colors.len() == 4 {
627                    patches.push(ShadingPatch {
628                        points,
629                        colors: [
630                            colors[0].clone(),
631                            colors[1].clone(),
632                            colors[2].clone(),
633                            colors[3].clone(),
634                        ],
635                        raw_colors: [
636                            raw_colors_vec[0].clone(),
637                            raw_colors_vec[1].clone(),
638                            raw_colors_vec[2].clone(),
639                            raw_colors_vec[3].clone(),
640                        ],
641                    });
642                }
643            }
644            1..=3 => {
645                // Continuation: inherit one side from previous patch.
646                // Type 7 uses the same perimeter data ordering as Type 6 for
647                // the first 12 points, so flag indices are identical.
648                if patches.is_empty() {
649                    break;
650                }
651                let prev = patches.last().unwrap().clone();
652                // PDF spec: inherit one side from the previous patch.
653                // Flag 1: prev edge 1 (P3→…→P6, C1,C2) becomes new edge 0
654                // Flag 2: prev edge 2 (P6→…→P9, C2,C3) becomes new edge 0
655                // Flag 3: prev edge 3 (P9→…→P0, C3,C0) becomes new edge 0
656                let (inherited_pts, inherited_colors, inherited_raw) = match flag {
657                    1 => (
658                        prev.points[3..7].to_vec(),
659                        [prev.colors[1].clone(), prev.colors[2].clone()],
660                        [prev.raw_colors[1].clone(), prev.raw_colors[2].clone()],
661                    ),
662                    2 => (
663                        prev.points[6..10].to_vec(),
664                        [prev.colors[2].clone(), prev.colors[3].clone()],
665                        [prev.raw_colors[2].clone(), prev.raw_colors[3].clone()],
666                    ),
667                    3 => (
668                        vec![
669                            prev.points[9],
670                            prev.points[10],
671                            prev.points[11],
672                            prev.points[0],
673                        ],
674                        [prev.colors[3].clone(), prev.colors[0].clone()],
675                        [prev.raw_colors[3].clone(), prev.raw_colors[0].clone()],
676                    ),
677                    _ => unreachable!(),
678                };
679
680                // Read 12 remaining points (rows 1-3 of the 4×4 grid) + 2 colors
681                let mut points = inherited_pts; // top row (pts[0..4])
682                let mut ok = true;
683                for _ in 0..12 {
684                    if let Some(pt) = read_point(&mut reader) {
685                        points.push(pt);
686                    } else {
687                        ok = false;
688                        break;
689                    }
690                }
691                let mut colors = vec![inherited_colors[0].clone(), inherited_colors[1].clone()];
692                let mut raw_colors_vec = vec![inherited_raw[0].clone(), inherited_raw[1].clone()];
693                if ok {
694                    for _ in 0..2 {
695                        if let Some((c, rc)) = read_color(&mut reader) {
696                            colors.push(c);
697                            raw_colors_vec.push(rc);
698                        } else {
699                            ok = false;
700                            break;
701                        }
702                    }
703                }
704                if ok && points.len() == 16 && colors.len() == 4 {
705                    patches.push(ShadingPatch {
706                        points,
707                        colors: [
708                            colors[0].clone(),
709                            colors[1].clone(),
710                            colors[2].clone(),
711                            colors[3].clone(),
712                        ],
713                        raw_colors: [
714                            raw_colors_vec[0].clone(),
715                            raw_colors_vec[1].clone(),
716                            raw_colors_vec[2].clone(),
717                            raw_colors_vec[3].clone(),
718                        ],
719                    });
720                }
721            }
722            _ => break,
723        }
724    }
725
726    patches
727}
728
729/// Build triangles from an array-based DataSource (flat list of numbers).
730/// Type 4: [flag x y c0 c1 ... flag x y c0 c1 ...]
731pub fn build_type4_from_array(values: &[f64], n_comps: usize) -> Vec<ShadingTriangle> {
732    let stride = 3 + n_comps; // flag + x + y + n_comps colors
733    let mut triangles = Vec::new();
734    let mut vertices: Vec<ShadingVertex> = Vec::new();
735    let mut pos = 0;
736
737    while pos + stride <= values.len() {
738        let flag = values[pos] as u32;
739        let x = values[pos + 1];
740        let y = values[pos + 2];
741        let comps: Vec<f64> = values[pos + 3..pos + 3 + n_comps].to_vec();
742        let (color, raw_components) = components_to_color(&comps);
743        let vertex = ShadingVertex {
744            x,
745            y,
746            color,
747            raw_components,
748        };
749        pos += stride;
750
751        match flag {
752            0 => {
753                vertices.clear();
754                vertices.push(vertex);
755                for _ in 0..2 {
756                    if pos + stride > values.len() {
757                        break;
758                    }
759                    let x2 = values[pos + 1];
760                    let y2 = values[pos + 2];
761                    let comps2: Vec<f64> = values[pos + 3..pos + 3 + n_comps].to_vec();
762                    let (color2, raw2) = components_to_color(&comps2);
763                    vertices.push(ShadingVertex {
764                        x: x2,
765                        y: y2,
766                        color: color2,
767                        raw_components: raw2,
768                    });
769                    pos += stride;
770                }
771                if vertices.len() == 3 {
772                    triangles.push(ShadingTriangle {
773                        v0: vertices[0].clone(),
774                        v1: vertices[1].clone(),
775                        v2: vertices[2].clone(),
776                    });
777                }
778            }
779            1 => {
780                if vertices.len() >= 2 {
781                    let len = vertices.len();
782                    let v0 = vertices[len - 2].clone();
783                    let v1 = vertices[len - 1].clone();
784                    vertices.push(vertex);
785                    let v2 = vertices.last().unwrap().clone();
786                    triangles.push(ShadingTriangle { v0, v1, v2 });
787                }
788            }
789            2 => {
790                if vertices.len() >= 3 {
791                    let len = vertices.len();
792                    let v0 = vertices[len - 3].clone();
793                    let v1 = vertices[len - 1].clone();
794                    vertices.push(vertex);
795                    let v2 = vertices.last().unwrap().clone();
796                    triangles.push(ShadingTriangle { v0, v1, v2 });
797                }
798            }
799            _ => {}
800        }
801    }
802
803    triangles
804}
805
806/// Build triangles from an array-based Type 5 lattice mesh.
807/// Values: [x y c0 c1 ... x y c0 c1 ...] (no flags)
808pub fn build_type5_from_array(
809    values: &[f64],
810    n_comps: usize,
811    verts_per_row: usize,
812) -> Vec<ShadingTriangle> {
813    let stride = 2 + n_comps; // x + y + n_comps
814    let mut all_vertices = Vec::new();
815
816    let mut pos = 0;
817    while pos + stride <= values.len() {
818        let x = values[pos];
819        let y = values[pos + 1];
820        let comps: Vec<f64> = values[pos + 2..pos + 2 + n_comps].to_vec();
821        let (color, raw_components) = components_to_color(&comps);
822        all_vertices.push(ShadingVertex {
823            x,
824            y,
825            color,
826            raw_components,
827        });
828        pos += stride;
829    }
830
831    let mut triangles = Vec::new();
832    if verts_per_row < 2 {
833        return triangles;
834    }
835    let num_rows = all_vertices.len() / verts_per_row;
836    if num_rows < 2 {
837        return triangles;
838    }
839
840    for row in 0..num_rows - 1 {
841        for col in 0..verts_per_row - 1 {
842            let i00 = row * verts_per_row + col;
843            let i10 = i00 + 1;
844            let i01 = i00 + verts_per_row;
845            let i11 = i01 + 1;
846            if i11 >= all_vertices.len() {
847                break;
848            }
849            triangles.push(ShadingTriangle {
850                v0: all_vertices[i00].clone(),
851                v1: all_vertices[i10].clone(),
852                v2: all_vertices[i01].clone(),
853            });
854            triangles.push(ShadingTriangle {
855                v0: all_vertices[i10].clone(),
856                v1: all_vertices[i11].clone(),
857                v2: all_vertices[i01].clone(),
858            });
859        }
860    }
861
862    triangles
863}
864
865/// Build patches from an array-based Type 6 Coons patch mesh.
866/// Values: [flag x0 y0 x1 y1 ... c0_0 c0_1 ... c1_0 c1_1 ...]
867pub fn build_type6_from_array(values: &[f64], n_comps: usize) -> Vec<ShadingPatch> {
868    // flag + 12 points (24 values) + 4 colors (4 * n_comps)
869    let full_stride = 1 + 24 + 4 * n_comps;
870    // continuation: flag + 8 points (16 values) + 2 colors (2 * n_comps)
871    let cont_stride = 1 + 16 + 2 * n_comps;
872    let mut patches = Vec::new();
873    let mut pos = 0;
874
875    while pos < values.len() {
876        let flag = values[pos] as u32;
877        pos += 1;
878
879        match flag {
880            0 => {
881                if pos + 24 + 4 * n_comps > values.len() {
882                    break;
883                }
884                let mut points = Vec::with_capacity(12);
885                for i in 0..12 {
886                    points.push((values[pos + i * 2], values[pos + i * 2 + 1]));
887                }
888                pos += 24;
889                let mut colors = Vec::with_capacity(4);
890                let mut raw_colors_vec = Vec::with_capacity(4);
891                for _ in 0..4 {
892                    let comps: Vec<f64> = values[pos..pos + n_comps].to_vec();
893                    let (c, rc) = components_to_color(&comps);
894                    colors.push(c);
895                    raw_colors_vec.push(rc);
896                    pos += n_comps;
897                }
898                let _ = full_stride; // used for documentation
899                patches.push(ShadingPatch {
900                    points,
901                    colors: [
902                        colors[0].clone(),
903                        colors[1].clone(),
904                        colors[2].clone(),
905                        colors[3].clone(),
906                    ],
907                    raw_colors: [
908                        raw_colors_vec[0].clone(),
909                        raw_colors_vec[1].clone(),
910                        raw_colors_vec[2].clone(),
911                        raw_colors_vec[3].clone(),
912                    ],
913                });
914            }
915            1..=3 => {
916                if patches.is_empty() {
917                    break;
918                }
919                let prev = patches.last().unwrap().clone();
920                // Inherit the full 4-point side from the previous patch.
921                // PDF spec: inherit one side from the previous patch.
922                // Flag 1: prev edge 1 (P3→…→P6, C1,C2) becomes new edge 0
923                // Flag 2: prev edge 2 (P6→…→P9, C2,C3) becomes new edge 0
924                // Flag 3: prev edge 3 (P9→…→P0, C3,C0) becomes new edge 0
925                let (inherited_pts, inherited_colors, inherited_raw) = match flag {
926                    1 => (
927                        prev.points[3..7].to_vec(),
928                        [prev.colors[1].clone(), prev.colors[2].clone()],
929                        [prev.raw_colors[1].clone(), prev.raw_colors[2].clone()],
930                    ),
931                    2 => (
932                        prev.points[6..10].to_vec(),
933                        [prev.colors[2].clone(), prev.colors[3].clone()],
934                        [prev.raw_colors[2].clone(), prev.raw_colors[3].clone()],
935                    ),
936                    3 => (
937                        vec![
938                            prev.points[9],
939                            prev.points[10],
940                            prev.points[11],
941                            prev.points[0],
942                        ],
943                        [prev.colors[3].clone(), prev.colors[0].clone()],
944                        [prev.raw_colors[3].clone(), prev.raw_colors[0].clone()],
945                    ),
946                    _ => unreachable!(),
947                };
948
949                if pos + 16 + 2 * n_comps > values.len() {
950                    break;
951                }
952                let mut points = inherited_pts;
953                for i in 0..8 {
954                    points.push((values[pos + i * 2], values[pos + i * 2 + 1]));
955                }
956                pos += 16;
957                let mut colors = vec![inherited_colors[0].clone(), inherited_colors[1].clone()];
958                let mut raw_colors_vec = vec![inherited_raw[0].clone(), inherited_raw[1].clone()];
959                for _ in 0..2 {
960                    let comps: Vec<f64> = values[pos..pos + n_comps].to_vec();
961                    let (c, rc) = components_to_color(&comps);
962                    colors.push(c);
963                    raw_colors_vec.push(rc);
964                    pos += n_comps;
965                }
966                let _ = cont_stride;
967                patches.push(ShadingPatch {
968                    points,
969                    colors: [
970                        colors[0].clone(),
971                        colors[1].clone(),
972                        colors[2].clone(),
973                        colors[3].clone(),
974                    ],
975                    raw_colors: [
976                        raw_colors_vec[0].clone(),
977                        raw_colors_vec[1].clone(),
978                        raw_colors_vec[2].clone(),
979                        raw_colors_vec[3].clone(),
980                    ],
981                });
982            }
983            _ => break,
984        }
985    }
986
987    patches
988}
989
990/// Build patches from an array-based Type 7 tensor-product patch mesh.
991pub fn build_type7_from_array(values: &[f64], n_comps: usize) -> Vec<ShadingPatch> {
992    // flag + 16 points (32 values) + 4 colors (4 * n_comps)
993    let full_stride = 1 + 32 + 4 * n_comps;
994    // continuation: flag + 12 points (24 values) + 2 colors (2 * n_comps)
995    let _cont_stride = 1 + 24 + 2 * n_comps;
996    let mut patches = Vec::new();
997    let mut pos = 0;
998
999    while pos < values.len() {
1000        let flag = values[pos] as u32;
1001        pos += 1;
1002
1003        match flag {
1004            0 => {
1005                if pos + 32 + 4 * n_comps > values.len() {
1006                    break;
1007                }
1008                let mut points = Vec::with_capacity(16);
1009                for i in 0..16 {
1010                    points.push((values[pos + i * 2], values[pos + i * 2 + 1]));
1011                }
1012                pos += 32;
1013                let mut colors = Vec::with_capacity(4);
1014                let mut raw_colors_vec = Vec::with_capacity(4);
1015                for _ in 0..4 {
1016                    let comps: Vec<f64> = values[pos..pos + n_comps].to_vec();
1017                    let (c, rc) = components_to_color(&comps);
1018                    colors.push(c);
1019                    raw_colors_vec.push(rc);
1020                    pos += n_comps;
1021                }
1022                let _ = full_stride;
1023                patches.push(ShadingPatch {
1024                    points,
1025                    colors: [
1026                        colors[0].clone(),
1027                        colors[1].clone(),
1028                        colors[2].clone(),
1029                        colors[3].clone(),
1030                    ],
1031                    raw_colors: [
1032                        raw_colors_vec[0].clone(),
1033                        raw_colors_vec[1].clone(),
1034                        raw_colors_vec[2].clone(),
1035                        raw_colors_vec[3].clone(),
1036                    ],
1037                });
1038            }
1039            1..=3 => {
1040                if patches.is_empty() {
1041                    break;
1042                }
1043                let prev = patches.last().unwrap().clone();
1044                // 4×4 grid sides: 1=top[0..4], 2=right[3,7,11,15],
1045                // 3=bottom[15,14,13,12], 4=left[12,8,4,0].
1046                let (inherited_pts, inherited_colors, inherited_raw) = match flag {
1047                    1 => (
1048                        vec![
1049                            prev.points[3],
1050                            prev.points[7],
1051                            prev.points[11],
1052                            prev.points[15],
1053                        ],
1054                        [prev.colors[1].clone(), prev.colors[2].clone()],
1055                        [prev.raw_colors[1].clone(), prev.raw_colors[2].clone()],
1056                    ),
1057                    2 => (
1058                        vec![
1059                            prev.points[12],
1060                            prev.points[8],
1061                            prev.points[4],
1062                            prev.points[0],
1063                        ],
1064                        [prev.colors[3].clone(), prev.colors[0].clone()],
1065                        [prev.raw_colors[3].clone(), prev.raw_colors[0].clone()],
1066                    ),
1067                    3 => (
1068                        vec![
1069                            prev.points[15],
1070                            prev.points[14],
1071                            prev.points[13],
1072                            prev.points[12],
1073                        ],
1074                        [prev.colors[2].clone(), prev.colors[3].clone()],
1075                        [prev.raw_colors[2].clone(), prev.raw_colors[3].clone()],
1076                    ),
1077                    _ => unreachable!(),
1078                };
1079
1080                if pos + 24 + 2 * n_comps > values.len() {
1081                    break;
1082                }
1083                let mut points = inherited_pts; // top row (pts[0..4])
1084                for i in 0..12 {
1085                    points.push((values[pos + i * 2], values[pos + i * 2 + 1]));
1086                }
1087                pos += 24;
1088                let mut colors = vec![inherited_colors[0].clone(), inherited_colors[1].clone()];
1089                let mut raw_colors_vec = vec![inherited_raw[0].clone(), inherited_raw[1].clone()];
1090                for _ in 0..2 {
1091                    let comps: Vec<f64> = values[pos..pos + n_comps].to_vec();
1092                    let (c, rc) = components_to_color(&comps);
1093                    colors.push(c);
1094                    raw_colors_vec.push(rc);
1095                    pos += n_comps;
1096                }
1097                patches.push(ShadingPatch {
1098                    points,
1099                    colors: [
1100                        colors[0].clone(),
1101                        colors[1].clone(),
1102                        colors[2].clone(),
1103                        colors[3].clone(),
1104                    ],
1105                    raw_colors: [
1106                        raw_colors_vec[0].clone(),
1107                        raw_colors_vec[1].clone(),
1108                        raw_colors_vec[2].clone(),
1109                        raw_colors_vec[3].clone(),
1110                    ],
1111                });
1112            }
1113            _ => break,
1114        }
1115    }
1116
1117    patches
1118}
1119
1120#[cfg(test)]
1121mod tests {
1122    use super::*;
1123
1124    #[test]
1125    fn test_bit_reader_aligned() {
1126        let data = [0xAB, 0xCD, 0xEF, 0x01];
1127        let mut reader = BitReader::new(&data);
1128        assert_eq!(reader.read(8), Some(0xAB));
1129        assert_eq!(reader.read(8), Some(0xCD));
1130        assert_eq!(reader.read(16), Some(0xEF01));
1131        assert!(reader.exhausted());
1132    }
1133
1134    #[test]
1135    fn test_bit_reader_unaligned() {
1136        let data = [0b10110100, 0b11010010];
1137        let mut reader = BitReader::new(&data);
1138        assert_eq!(reader.read(4), Some(0b1011));
1139        assert_eq!(reader.read(4), Some(0b0100));
1140        assert_eq!(reader.read(4), Some(0b1101));
1141        assert_eq!(reader.read(4), Some(0b0010));
1142        assert!(reader.exhausted());
1143    }
1144
1145    #[test]
1146    fn test_bit_reader_cross_byte() {
1147        let data = [0b11110000, 0b10101010];
1148        let mut reader = BitReader::new(&data);
1149        assert_eq!(reader.read(6), Some(0b111100));
1150        assert_eq!(reader.read(6), Some(0b001010));
1151        assert_eq!(reader.read(4), Some(0b1010));
1152        assert!(reader.exhausted());
1153    }
1154
1155    #[test]
1156    fn test_decode_scale_8bit() {
1157        let scale = decode_scale(8, 0.0, 1.0);
1158        assert!((scale - 1.0 / 255.0).abs() < 1e-10);
1159        let val = decode_value(128, scale, 0.0);
1160        assert!((val - 128.0 / 255.0).abs() < 1e-10);
1161    }
1162
1163    #[test]
1164    fn test_type5_lattice_simple() {
1165        // 2x2 lattice with 1-component (gray) colors
1166        // 8 bits/coord, 8 bits/component
1167        // Decode: [0, 255, 0, 255, 0, 1]
1168        #[rustfmt::skip]
1169        let data = [
1170            0, 0, 0,      // (0,0) gray=0.0
1171            255, 0, 128,   // (255,0) gray≈0.5
1172            0, 255, 0,     // (0,255) gray=0.0
1173            255, 255, 255, // (255,255) gray=1.0
1174        ];
1175        let decode = [0.0, 255.0, 0.0, 255.0, 0.0, 1.0];
1176        let triangles = parse_type5_mesh(&data, 8, 8, &decode, 1, 2);
1177        assert_eq!(triangles.len(), 2);
1178    }
1179}