1use justpdf_core::function::PdfFunction;
2use justpdf_core::object::{PdfDict, PdfObject};
3use tiny_skia::{
4 Color, FillRule, GradientStop, LinearGradient, Mask, Paint, PathBuilder, Pixmap,
5 RadialGradient, SpreadMode, Transform,
6};
7
8use crate::graphics_state::Matrix;
9
10pub fn render_shading(
13 pixmap: &mut Pixmap,
14 shading_dict: &PdfDict,
15 ctm: &Matrix,
16 page_transform: &Matrix,
17 clip_mask: Option<&Mask>,
18 stream_data: Option<&[u8]>,
19) {
20 let shading_type = shading_dict.get_i64(b"ShadingType").unwrap_or(0);
21
22 match shading_type {
23 1 => render_function_based(pixmap, shading_dict, ctm, page_transform, clip_mask),
24 2 => render_axial(pixmap, shading_dict, ctm, page_transform, clip_mask),
25 3 => render_radial(pixmap, shading_dict, ctm, page_transform, clip_mask),
26 4 | 5 => render_gouraud_mesh(pixmap, shading_dict, ctm, page_transform, clip_mask, stream_data),
27 6 | 7 => render_patch_mesh(pixmap, shading_dict, ctm, page_transform, clip_mask, stream_data),
28 _ => {} }
30}
31
32fn render_function_based(
37 pixmap: &mut Pixmap,
38 dict: &PdfDict,
39 ctm: &Matrix,
40 page_transform: &Matrix,
41 clip: Option<&Mask>,
42) {
43 let domain = dict.get_array(b"Domain");
45 let x0 = domain
46 .and_then(|a| a.first())
47 .and_then(|o| o.as_f64())
48 .unwrap_or(0.0);
49 let x1 = domain
50 .and_then(|a| a.get(1))
51 .and_then(|o| o.as_f64())
52 .unwrap_or(1.0);
53 let y0 = domain
54 .and_then(|a| a.get(2))
55 .and_then(|o| o.as_f64())
56 .unwrap_or(0.0);
57 let y1 = domain
58 .and_then(|a| a.get(3))
59 .and_then(|o| o.as_f64())
60 .unwrap_or(1.0);
61
62 let cs_name = dict
63 .get(b"ColorSpace")
64 .and_then(|o| o.as_name())
65 .unwrap_or(b"DeviceRGB");
66
67 let shading_matrix = if let Some(matrix_arr) = dict.get_array(b"Matrix") {
69 if matrix_arr.len() >= 6 {
70 Matrix {
71 a: matrix_arr[0].as_f64().unwrap_or(1.0),
72 b: matrix_arr[1].as_f64().unwrap_or(0.0),
73 c: matrix_arr[2].as_f64().unwrap_or(0.0),
74 d: matrix_arr[3].as_f64().unwrap_or(1.0),
75 e: matrix_arr[4].as_f64().unwrap_or(0.0),
76 f: matrix_arr[5].as_f64().unwrap_or(0.0),
77 }
78 } else {
79 Matrix::identity()
80 }
81 } else {
82 Matrix::identity()
83 };
84
85 let func = dict.get(b"Function").and_then(PdfFunction::parse);
87
88 let transform = shading_matrix.concat(ctm).concat(page_transform).to_skia();
89
90 let samples = 64; let dx = (x1 - x0) / samples as f64;
93 let dy = (y1 - y0) / samples as f64;
94
95 for iy in 0..samples {
96 for ix in 0..samples {
97 let sx = x0 + (ix as f64 + 0.5) * dx;
98 let sy = y0 + (iy as f64 + 0.5) * dy;
99
100 let color = if let Some(ref f) = func {
101 let result = f.evaluate(&[sx, sy]);
102 components_to_color(&result, cs_name)
103 } else {
104 components_to_color(&[0.5, 0.5, 0.5], cs_name)
106 };
107
108 let mut paint = Paint::default();
109 paint.set_color(color);
110
111 let rx = x0 + ix as f64 * dx;
112 let ry = y0 + iy as f64 * dy;
113
114 let mut pb = PathBuilder::new();
115 pb.move_to(rx as f32, ry as f32);
116 pb.line_to((rx + dx) as f32, ry as f32);
117 pb.line_to((rx + dx) as f32, (ry + dy) as f32);
118 pb.line_to(rx as f32, (ry + dy) as f32);
119 pb.close();
120
121 if let Some(path) = pb.finish() {
122 pixmap.fill_path(&path, &paint, FillRule::Winding, transform, clip);
123 }
124 }
125 }
126}
127
128fn render_axial(
133 pixmap: &mut Pixmap,
134 dict: &PdfDict,
135 ctm: &Matrix,
136 page_transform: &Matrix,
137 clip: Option<&Mask>,
138) {
139 let coords = match dict.get_array(b"Coords") {
140 Some(arr) if arr.len() >= 4 => arr,
141 _ => return,
142 };
143
144 let x0 = coords[0].as_f64().unwrap_or(0.0) as f32;
145 let y0 = coords[1].as_f64().unwrap_or(0.0) as f32;
146 let x1 = coords[2].as_f64().unwrap_or(0.0) as f32;
147 let y1 = coords[3].as_f64().unwrap_or(0.0) as f32;
148
149 let stops = extract_color_stops(dict);
150 if stops.is_empty() {
151 return;
152 }
153
154 let gradient = match LinearGradient::new(
155 tiny_skia::Point::from_xy(x0, y0),
156 tiny_skia::Point::from_xy(x1, y1),
157 stops,
158 SpreadMode::Pad,
159 Transform::identity(),
160 ) {
161 Some(g) => g,
162 None => return,
163 };
164
165 let mut paint = Paint::default();
166 paint.shader = gradient;
167 paint.anti_alias = true;
168
169 let transform = ctm.concat(page_transform).to_skia();
170
171 let mut pb = PathBuilder::new();
172 pb.move_to(-10000.0, -10000.0);
173 pb.line_to(20000.0, -10000.0);
174 pb.line_to(20000.0, 20000.0);
175 pb.line_to(-10000.0, 20000.0);
176 pb.close();
177
178 if let Some(path) = pb.finish() {
179 pixmap.fill_path(&path, &paint, FillRule::Winding, transform, clip);
180 }
181}
182
183fn render_radial(
188 pixmap: &mut Pixmap,
189 dict: &PdfDict,
190 ctm: &Matrix,
191 page_transform: &Matrix,
192 clip: Option<&Mask>,
193) {
194 let coords = match dict.get_array(b"Coords") {
195 Some(arr) if arr.len() >= 6 => arr,
196 _ => return,
197 };
198
199 let x0 = coords[0].as_f64().unwrap_or(0.0) as f32;
200 let y0 = coords[1].as_f64().unwrap_or(0.0) as f32;
201 let _r0 = coords[2].as_f64().unwrap_or(0.0) as f32;
202 let x1 = coords[3].as_f64().unwrap_or(0.0) as f32;
203 let y1 = coords[4].as_f64().unwrap_or(0.0) as f32;
204 let r1 = coords[5].as_f64().unwrap_or(0.0) as f32;
205
206 let stops = extract_color_stops(dict);
207 if stops.is_empty() {
208 return;
209 }
210
211 let gradient = match RadialGradient::new(
212 tiny_skia::Point::from_xy(x0, y0),
213 tiny_skia::Point::from_xy(x1, y1),
214 r1,
215 stops,
216 SpreadMode::Pad,
217 Transform::identity(),
218 ) {
219 Some(g) => g,
220 None => return,
221 };
222
223 let mut paint = Paint::default();
224 paint.shader = gradient;
225 paint.anti_alias = true;
226
227 let transform = ctm.concat(page_transform).to_skia();
228
229 let mut pb = PathBuilder::new();
230 pb.move_to(-10000.0, -10000.0);
231 pb.line_to(20000.0, -10000.0);
232 pb.line_to(20000.0, 20000.0);
233 pb.line_to(-10000.0, 20000.0);
234 pb.close();
235
236 if let Some(path) = pb.finish() {
237 pixmap.fill_path(&path, &paint, FillRule::Winding, transform, clip);
238 }
239}
240
241#[derive(Debug, Clone, Copy)]
247#[allow(dead_code)]
248pub struct Vertex {
249 x: f64,
250 y: f64,
251 r: u8,
252 g: u8,
253 b: u8,
254}
255
256fn render_gouraud_mesh(
257 pixmap: &mut Pixmap,
258 dict: &PdfDict,
259 ctm: &Matrix,
260 page_transform: &Matrix,
261 clip: Option<&Mask>,
262 stream_data: Option<&[u8]>,
263) {
264 let shading_type = dict.get_i64(b"ShadingType").unwrap_or(4);
265
266 let cs_name = dict
267 .get(b"ColorSpace")
268 .and_then(|o| o.as_name())
269 .unwrap_or(b"DeviceRGB");
270 let n_comps = color_space_components(cs_name);
271
272 let bits_per_coordinate = dict.get_i64(b"BitsPerCoordinate").unwrap_or(8) as u32;
273 let bits_per_component = dict.get_i64(b"BitsPerComponent").unwrap_or(8) as u32;
274 let bits_per_flag = if shading_type == 4 {
275 dict.get_i64(b"BitsPerFlag").unwrap_or(8) as u32
276 } else {
277 0
278 };
279
280 let decode = dict
281 .get_array(b"Decode")
282 .map(|a| a.iter().filter_map(|o| o.as_f64()).collect::<Vec<_>>())
283 .unwrap_or_default();
284
285 let data = match stream_data {
286 Some(d) if !d.is_empty() => d,
287 _ => {
288 render_mesh_fallback(pixmap, ctm, page_transform, clip, cs_name, &decode);
290 return;
291 }
292 };
293
294 let transform = ctm.concat(page_transform).to_skia();
295
296 if shading_type == 4 {
297 let triangles = parse_gouraud_triangles(
299 data,
300 bits_per_flag,
301 bits_per_coordinate,
302 bits_per_component,
303 n_comps,
304 &decode,
305 cs_name,
306 );
307
308 for tri in &triangles {
309 rasterize_triangle(
310 pixmap,
311 (tri[0].x as f32, tri[0].y as f32, [tri[0].r, tri[0].g, tri[0].b, 255]),
312 (tri[1].x as f32, tri[1].y as f32, [tri[1].r, tri[1].g, tri[1].b, 255]),
313 (tri[2].x as f32, tri[2].y as f32, [tri[2].r, tri[2].g, tri[2].b, 255]),
314 transform,
315 clip,
316 );
317 }
318 } else {
319 let vertices_per_row = dict.get_i64(b"VerticesPerRow").unwrap_or(2) as usize;
321 if vertices_per_row < 2 {
322 return;
323 }
324
325 let vertices = parse_lattice_vertices(
326 data,
327 bits_per_coordinate,
328 bits_per_component,
329 n_comps,
330 &decode,
331 cs_name,
332 );
333
334 let n_rows = vertices.len() / vertices_per_row;
336 for row in 0..n_rows.saturating_sub(1) {
337 for col in 0..vertices_per_row - 1 {
338 let i0 = row * vertices_per_row + col;
339 let i1 = i0 + 1;
340 let i2 = i0 + vertices_per_row;
341 let i3 = i2 + 1;
342
343 if i3 < vertices.len() {
344 let v0 = &vertices[i0];
345 let v1 = &vertices[i1];
346 let v2 = &vertices[i2];
347 let v3 = &vertices[i3];
348
349 rasterize_triangle(
351 pixmap,
352 (v0.x as f32, v0.y as f32, [v0.r, v0.g, v0.b, 255]),
353 (v1.x as f32, v1.y as f32, [v1.r, v1.g, v1.b, 255]),
354 (v2.x as f32, v2.y as f32, [v2.r, v2.g, v2.b, 255]),
355 transform,
356 clip,
357 );
358 rasterize_triangle(
359 pixmap,
360 (v1.x as f32, v1.y as f32, [v1.r, v1.g, v1.b, 255]),
361 (v3.x as f32, v3.y as f32, [v3.r, v3.g, v3.b, 255]),
362 (v2.x as f32, v2.y as f32, [v2.r, v2.g, v2.b, 255]),
363 transform,
364 clip,
365 );
366 }
367 }
368 }
369 }
370}
371
372fn render_patch_mesh(
377 pixmap: &mut Pixmap,
378 dict: &PdfDict,
379 ctm: &Matrix,
380 page_transform: &Matrix,
381 clip: Option<&Mask>,
382 stream_data: Option<&[u8]>,
383) {
384 let shading_type = dict.get_i64(b"ShadingType").unwrap_or(6);
385
386 let cs_name = dict
387 .get(b"ColorSpace")
388 .and_then(|o| o.as_name())
389 .unwrap_or(b"DeviceRGB");
390 let n_comps = color_space_components(cs_name);
391
392 let bits_per_coordinate = dict.get_i64(b"BitsPerCoordinate").unwrap_or(8) as u32;
393 let bits_per_component = dict.get_i64(b"BitsPerComponent").unwrap_or(8) as u32;
394 let bits_per_flag = dict.get_i64(b"BitsPerFlag").unwrap_or(8) as u32;
395
396 let decode = dict
397 .get_array(b"Decode")
398 .map(|a| a.iter().filter_map(|o| o.as_f64()).collect::<Vec<_>>())
399 .unwrap_or_default();
400
401 let data = match stream_data {
402 Some(d) if !d.is_empty() => d,
403 _ => {
404 render_mesh_fallback(pixmap, ctm, page_transform, clip, cs_name, &decode);
405 return;
406 }
407 };
408
409 let transform = ctm.concat(page_transform).to_skia();
410
411 let points_per_patch: usize = if shading_type == 7 { 16 } else { 12 };
413
414 let patches = parse_patch_mesh(
415 data,
416 bits_per_flag,
417 bits_per_coordinate,
418 bits_per_component,
419 n_comps,
420 points_per_patch,
421 &decode,
422 cs_name,
423 );
424
425 for patch in &patches {
427 subdivide_and_rasterize_patch(pixmap, patch, transform, clip, 3);
428 }
429}
430
431fn render_mesh_fallback(
434 pixmap: &mut Pixmap,
435 ctm: &Matrix,
436 page_transform: &Matrix,
437 clip: Option<&Mask>,
438 cs_name: &[u8],
439 decode: &[f64],
440) {
441 let x_min = decode.first().copied().unwrap_or(0.0) as f32;
442 let x_max = decode.get(1).copied().unwrap_or(1.0) as f32;
443 let y_min = decode.get(2).copied().unwrap_or(0.0) as f32;
444 let y_max = decode.get(3).copied().unwrap_or(1.0) as f32;
445
446 let n_comps = color_space_components(cs_name);
448 let mut mid_comps = Vec::new();
449 for i in 0..n_comps {
450 let idx = 4 + i * 2;
451 let cmin = decode.get(idx).copied().unwrap_or(0.0);
452 let cmax = decode.get(idx + 1).copied().unwrap_or(1.0);
453 mid_comps.push((cmin + cmax) / 2.0);
454 }
455
456 let color = components_to_color(&mid_comps, cs_name);
457 let transform = ctm.concat(page_transform).to_skia();
458
459 let mut paint = Paint::default();
460 paint.set_color(color);
461 paint.anti_alias = true;
462
463 let mut pb = PathBuilder::new();
464 pb.move_to(x_min, y_min);
465 pb.line_to(x_max, y_min);
466 pb.line_to(x_max, y_max);
467 pb.line_to(x_min, y_max);
468 pb.close();
469
470 if let Some(path) = pb.finish() {
471 pixmap.fill_path(&path, &paint, FillRule::Winding, transform, clip);
472 }
473}
474
475pub fn rasterize_triangle(
478 pixmap: &mut Pixmap,
479 v0: (f32, f32, [u8; 4]),
480 v1: (f32, f32, [u8; 4]),
481 v2: (f32, f32, [u8; 4]),
482 transform: Transform,
483 clip: Option<&Mask>,
484) {
485 let p0 = transform_point(transform, v0.0, v0.1);
487 let p1 = transform_point(transform, v1.0, v1.1);
488 let p2 = transform_point(transform, v2.0, v2.1);
489
490 let min_x = p0.0.min(p1.0).min(p2.0).floor().max(0.0) as i32;
492 let max_x = p0.0.max(p1.0).max(p2.0).ceil().min(pixmap.width() as f32) as i32;
493 let min_y = p0.1.min(p1.1).min(p2.1).floor().max(0.0) as i32;
494 let max_y = p0.1.max(p1.1).max(p2.1).ceil().min(pixmap.height() as f32) as i32;
495
496 let area = edge_function(p0, p1, p2);
497 if area.abs() < 0.001 {
498 return; }
500 let inv_area = 1.0 / area;
501
502 let width = pixmap.width() as i32;
503 let pixels = pixmap.pixels_mut();
504
505 for y in min_y..max_y {
506 for x in min_x..max_x {
507 let px = x as f32 + 0.5;
508 let py = y as f32 + 0.5;
509 let p = (px, py);
510
511 let w0 = edge_function(p1, p2, p) * inv_area;
512 let w1 = edge_function(p2, p0, p) * inv_area;
513 let w2 = edge_function(p0, p1, p) * inv_area;
514
515 if w0 >= 0.0 && w1 >= 0.0 && w2 >= 0.0 {
516 let _ = clip;
519
520 let r = (w0 * v0.2[0] as f32 + w1 * v1.2[0] as f32 + w2 * v2.2[0] as f32) as u8;
521 let g = (w0 * v0.2[1] as f32 + w1 * v1.2[1] as f32 + w2 * v2.2[1] as f32) as u8;
522 let b = (w0 * v0.2[2] as f32 + w1 * v1.2[2] as f32 + w2 * v2.2[2] as f32) as u8;
523 let a = (w0 * v0.2[3] as f32 + w1 * v1.2[3] as f32 + w2 * v2.2[3] as f32) as u8;
524
525 let idx = (y * width + x) as usize;
526 if idx < pixels.len() {
527 if let Some(color) =
529 tiny_skia::PremultipliedColorU8::from_rgba(r, g, b, a)
530 {
531 pixels[idx] = color;
532 }
533 }
534 }
535 }
536 }
537}
538
539fn edge_function(a: (f32, f32), b: (f32, f32), c: (f32, f32)) -> f32 {
540 (c.0 - a.0) * (b.1 - a.1) - (c.1 - a.1) * (b.0 - a.0)
541}
542
543fn transform_point(t: Transform, x: f32, y: f32) -> (f32, f32) {
544 (
545 t.sx * x + t.kx * y + t.tx,
546 t.ky * x + t.sy * y + t.ty,
547 )
548}
549
550pub fn parse_gouraud_triangles(
556 data: &[u8],
557 bits_per_flag: u32,
558 bits_per_coordinate: u32,
559 bits_per_component: u32,
560 n_components: usize,
561 decode: &[f64],
562 cs_name: &[u8],
563) -> Vec<[Vertex; 3]> {
564 let mut reader = BitReader::new(data);
565 let mut vertices: Vec<Vertex> = Vec::new();
566 let mut triangles: Vec<[Vertex; 3]> = Vec::new();
567
568 let (x_min, x_max) = (
569 decode.first().copied().unwrap_or(0.0),
570 decode.get(1).copied().unwrap_or(1.0),
571 );
572 let (y_min, y_max) = (
573 decode.get(2).copied().unwrap_or(0.0),
574 decode.get(3).copied().unwrap_or(1.0),
575 );
576
577 let coord_max = ((1u64 << bits_per_coordinate) - 1) as f64;
578 let comp_max = ((1u64 << bits_per_component) - 1) as f64;
579
580 loop {
581 let flag = if bits_per_flag > 0 {
583 match reader.read_bits(bits_per_flag) {
584 Some(f) => f as u8,
585 None => break,
586 }
587 } else {
588 0
589 };
590
591 let raw_x = match reader.read_bits(bits_per_coordinate) {
593 Some(v) => v,
594 None => break,
595 };
596 let raw_y = match reader.read_bits(bits_per_coordinate) {
597 Some(v) => v,
598 None => break,
599 };
600
601 let x = x_min + (raw_x as f64 / coord_max) * (x_max - x_min);
602 let y = y_min + (raw_y as f64 / coord_max) * (y_max - y_min);
603
604 let mut comps = Vec::with_capacity(n_components);
606 for i in 0..n_components {
607 let raw_c = match reader.read_bits(bits_per_component) {
608 Some(v) => v,
609 None => break,
610 };
611 let c_min = decode.get(4 + i * 2).copied().unwrap_or(0.0);
612 let c_max = decode.get(4 + i * 2 + 1).copied().unwrap_or(1.0);
613 comps.push(c_min + (raw_c as f64 / comp_max) * (c_max - c_min));
614 }
615 if comps.len() != n_components {
616 break;
617 }
618
619 let color = components_to_color(&comps, cs_name);
620 let [r, g, b, _] = color_to_rgba8(color);
621
622 let vertex = Vertex { x, y, r, g, b };
623
624 match flag {
625 0 => {
626 vertices.clear();
628 vertices.push(vertex);
629 }
630 1 => {
631 if vertices.len() >= 2 {
633 let v_prev1 = vertices[vertices.len() - 2];
634 let v_prev2 = vertices[vertices.len() - 1];
635 vertices.push(vertex);
636 triangles.push([v_prev2, vertices.last().copied().unwrap(), v_prev1]);
637 } else {
638 vertices.push(vertex);
639 }
640 }
641 2 => {
642 if vertices.len() >= 2 {
644 let v_first = vertices[0];
645 let v_last = vertices[vertices.len() - 1];
646 vertices.push(vertex);
647 triangles.push([v_last, vertices.last().copied().unwrap(), v_first]);
648 } else {
649 vertices.push(vertex);
650 }
651 }
652 _ => {
653 vertices.push(vertex);
654 }
655 }
656
657 if flag == 0 && vertices.len() == 3 {
659 triangles.push([vertices[0], vertices[1], vertices[2]]);
660 } else if flag == 0 && vertices.len() < 3 {
661 continue; }
663 }
664
665 triangles
666}
667
668pub fn parse_lattice_vertices(
671 data: &[u8],
672 bits_per_coordinate: u32,
673 bits_per_component: u32,
674 n_components: usize,
675 decode: &[f64],
676 cs_name: &[u8],
677) -> Vec<Vertex> {
678 let mut reader = BitReader::new(data);
679 let mut vertices: Vec<Vertex> = Vec::new();
680
681 let (x_min, x_max) = (
682 decode.first().copied().unwrap_or(0.0),
683 decode.get(1).copied().unwrap_or(1.0),
684 );
685 let (y_min, y_max) = (
686 decode.get(2).copied().unwrap_or(0.0),
687 decode.get(3).copied().unwrap_or(1.0),
688 );
689
690 let coord_max = ((1u64 << bits_per_coordinate) - 1) as f64;
691 let comp_max = ((1u64 << bits_per_component) - 1) as f64;
692
693 loop {
694 let raw_x = match reader.read_bits(bits_per_coordinate) {
695 Some(v) => v,
696 None => break,
697 };
698 let raw_y = match reader.read_bits(bits_per_coordinate) {
699 Some(v) => v,
700 None => break,
701 };
702
703 let x = x_min + (raw_x as f64 / coord_max) * (x_max - x_min);
704 let y = y_min + (raw_y as f64 / coord_max) * (y_max - y_min);
705
706 let mut comps = Vec::with_capacity(n_components);
707 let mut ok = true;
708 for i in 0..n_components {
709 match reader.read_bits(bits_per_component) {
710 Some(raw_c) => {
711 let c_min = decode.get(4 + i * 2).copied().unwrap_or(0.0);
712 let c_max = decode.get(4 + i * 2 + 1).copied().unwrap_or(1.0);
713 comps.push(c_min + (raw_c as f64 / comp_max) * (c_max - c_min));
714 }
715 None => {
716 ok = false;
717 break;
718 }
719 }
720 }
721 if !ok {
722 break;
723 }
724
725 let color = components_to_color(&comps, cs_name);
726 let [r, g, b, _] = color_to_rgba8(color);
727 vertices.push(Vertex { x, y, r, g, b });
728 }
729
730 vertices
731}
732
733#[derive(Debug, Clone)]
735pub struct Patch {
736 pub corners: [(f64, f64); 4],
738 pub colors: [[u8; 4]; 4],
740}
741
742pub fn parse_patch_mesh(
744 data: &[u8],
745 bits_per_flag: u32,
746 bits_per_coordinate: u32,
747 bits_per_component: u32,
748 n_components: usize,
749 points_per_patch: usize,
750 decode: &[f64],
751 cs_name: &[u8],
752) -> Vec<Patch> {
753 let mut reader = BitReader::new(data);
754 let mut patches: Vec<Patch> = Vec::new();
755
756 let (x_min, x_max) = (
757 decode.first().copied().unwrap_or(0.0),
758 decode.get(1).copied().unwrap_or(1.0),
759 );
760 let (y_min, y_max) = (
761 decode.get(2).copied().unwrap_or(0.0),
762 decode.get(3).copied().unwrap_or(1.0),
763 );
764
765 let coord_max = ((1u64 << bits_per_coordinate) - 1).max(1) as f64;
766 let comp_max = ((1u64 << bits_per_component) - 1).max(1) as f64;
767
768 let mut prev_points: Vec<(f64, f64)> = Vec::new();
769 let mut prev_colors: Vec<[u8; 4]> = Vec::new();
770
771 loop {
772 let flag = match reader.read_bits(bits_per_flag) {
773 Some(f) => f as u8,
774 None => break,
775 };
776
777 let (n_points, n_colors) = if flag == 0 {
779 (points_per_patch, 4) } else {
781 if points_per_patch == 16 {
785 (12, 2)
786 } else {
787 (8, 2)
788 }
789 };
790
791 let mut points: Vec<(f64, f64)> = Vec::with_capacity(n_points);
793 let mut ok = true;
794 for _ in 0..n_points {
795 let raw_x = match reader.read_bits(bits_per_coordinate) {
796 Some(v) => v,
797 None => { ok = false; break; }
798 };
799 let raw_y = match reader.read_bits(bits_per_coordinate) {
800 Some(v) => v,
801 None => { ok = false; break; }
802 };
803 let x = x_min + (raw_x as f64 / coord_max) * (x_max - x_min);
804 let y = y_min + (raw_y as f64 / coord_max) * (y_max - y_min);
805 points.push((x, y));
806 }
807 if !ok { break; }
808
809 let mut colors: Vec<[u8; 4]> = Vec::with_capacity(n_colors);
811 for _ in 0..n_colors {
812 let mut comps = Vec::with_capacity(n_components);
813 for i in 0..n_components {
814 match reader.read_bits(bits_per_component) {
815 Some(raw_c) => {
816 let c_min = decode.get(4 + i * 2).copied().unwrap_or(0.0);
817 let c_max = decode.get(4 + i * 2 + 1).copied().unwrap_or(1.0);
818 comps.push(c_min + (raw_c as f64 / comp_max) * (c_max - c_min));
819 }
820 None => { ok = false; break; }
821 }
822 }
823 if !ok { break; }
824 let color = components_to_color(&comps, cs_name);
825 let [r, g, b, a] = color_to_rgba8(color);
826 colors.push([r, g, b, a]);
827 }
828 if !ok { break; }
829
830 let (corners, corner_colors) = if flag == 0 && points.len() >= 4 && colors.len() >= 4 {
833 let c0 = points[0];
836 let c1 = if points_per_patch == 16 { points[3] } else { points[3] };
837 let c2 = if points_per_patch == 16 { points[12] } else { points[9] };
838 let c3 = if points_per_patch == 16 {
839 points.get(15).copied().unwrap_or(c2)
840 } else {
841 points.get(6).copied().unwrap_or(c0)
842 };
843 (
844 [c0, c1, c2, c3],
845 [colors[0], colors[1], colors[2], colors[3]],
846 )
847 } else if !prev_points.is_empty() && points.len() >= 4 && colors.len() >= 2 {
848 let c0 = points[0];
850 let c1 = points.get(3).copied().unwrap_or(c0);
851 let c2 = points.last().copied().unwrap_or(c0);
852 let c3 = points.get(points.len() / 2).copied().unwrap_or(c0);
853 let pc = if prev_colors.len() >= 4 {
854 [prev_colors[1], prev_colors[2]]
855 } else {
856 [[128, 128, 128, 255]; 2]
857 };
858 (
859 [c0, c1, c2, c3],
860 [pc[0], colors[0], pc[1], colors[1]],
861 )
862 } else {
863 continue;
864 };
865
866 prev_points = points;
867 prev_colors = Vec::from(corner_colors);
868
869 patches.push(Patch {
870 corners,
871 colors: corner_colors,
872 });
873 }
874
875 patches
876}
877
878fn subdivide_and_rasterize_patch(
881 pixmap: &mut Pixmap,
882 patch: &Patch,
883 transform: Transform,
884 clip: Option<&Mask>,
885 subdivisions: usize,
886) {
887 let n = subdivisions.max(1);
888 let step = 1.0 / n as f64;
889
890 for i in 0..n {
891 for j in 0..n {
892 let u0 = i as f64 * step;
893 let v0 = j as f64 * step;
894 let u1 = u0 + step;
895 let v1 = v0 + step;
896
897 let p00 = bilinear_point(&patch.corners, u0, v0);
898 let p10 = bilinear_point(&patch.corners, u1, v0);
899 let p01 = bilinear_point(&patch.corners, u0, v1);
900 let p11 = bilinear_point(&patch.corners, u1, v1);
901
902 let c00 = bilinear_color(&patch.colors, u0, v0);
903 let c10 = bilinear_color(&patch.colors, u1, v0);
904 let c01 = bilinear_color(&patch.colors, u0, v1);
905 let c11 = bilinear_color(&patch.colors, u1, v1);
906
907 rasterize_triangle(
909 pixmap,
910 (p00.0 as f32, p00.1 as f32, c00),
911 (p10.0 as f32, p10.1 as f32, c10),
912 (p01.0 as f32, p01.1 as f32, c01),
913 transform,
914 clip,
915 );
916 rasterize_triangle(
917 pixmap,
918 (p10.0 as f32, p10.1 as f32, c10),
919 (p11.0 as f32, p11.1 as f32, c11),
920 (p01.0 as f32, p01.1 as f32, c01),
921 transform,
922 clip,
923 );
924 }
925 }
926}
927
928fn bilinear_point(corners: &[(f64, f64); 4], u: f64, v: f64) -> (f64, f64) {
930 let x = corners[0].0 * (1.0 - u) * (1.0 - v)
931 + corners[1].0 * u * (1.0 - v)
932 + corners[2].0 * (1.0 - u) * v
933 + corners[3].0 * u * v;
934 let y = corners[0].1 * (1.0 - u) * (1.0 - v)
935 + corners[1].1 * u * (1.0 - v)
936 + corners[2].1 * (1.0 - u) * v
937 + corners[3].1 * u * v;
938 (x, y)
939}
940
941fn bilinear_color(colors: &[[u8; 4]; 4], u: f64, v: f64) -> [u8; 4] {
943 let mut result = [0u8; 4];
944 for ch in 0..4 {
945 let c = colors[0][ch] as f64 * (1.0 - u) * (1.0 - v)
946 + colors[1][ch] as f64 * u * (1.0 - v)
947 + colors[2][ch] as f64 * (1.0 - u) * v
948 + colors[3][ch] as f64 * u * v;
949 result[ch] = c.clamp(0.0, 255.0) as u8;
950 }
951 result
952}
953
954fn color_to_rgba8(color: Color) -> [u8; 4] {
955 [
956 (color.red() * 255.0) as u8,
957 (color.green() * 255.0) as u8,
958 (color.blue() * 255.0) as u8,
959 (color.alpha() * 255.0) as u8,
960 ]
961}
962
963struct BitReader<'a> {
965 data: &'a [u8],
966 byte_pos: usize,
967 bit_pos: u8, }
969
970impl<'a> BitReader<'a> {
971 fn new(data: &'a [u8]) -> Self {
972 Self {
973 data,
974 byte_pos: 0,
975 bit_pos: 0,
976 }
977 }
978
979 fn read_bits(&mut self, n: u32) -> Option<u64> {
980 if n == 0 || n > 64 {
981 return Some(0);
982 }
983
984 let mut result: u64 = 0;
985 let mut remaining = n;
986
987 while remaining > 0 {
988 if self.byte_pos >= self.data.len() {
989 return None;
990 }
991
992 let available = 8 - self.bit_pos as u32;
993 let to_read = remaining.min(available);
994
995 let byte = self.data[self.byte_pos];
996 let shift = available - to_read;
997 let mask = ((1u16 << to_read) - 1) as u8;
998 let bits = (byte >> shift) & mask;
999
1000 result = (result << to_read) | bits as u64;
1001 remaining -= to_read;
1002
1003 self.bit_pos += to_read as u8;
1004 if self.bit_pos >= 8 {
1005 self.bit_pos = 0;
1006 self.byte_pos += 1;
1007 }
1008 }
1009
1010 Some(result)
1011 }
1012}
1013
1014fn extract_color_stops(dict: &PdfDict) -> Vec<GradientStop> {
1019 let cs_name = dict
1020 .get(b"ColorSpace")
1021 .and_then(|o| o.as_name())
1022 .unwrap_or(b"DeviceRGB");
1023
1024 if let Some(func_obj) = dict.get(b"Function") {
1025 if let PdfObject::Dict(func) = func_obj {
1026 return extract_stops_from_function(func, cs_name);
1027 }
1028 if let PdfObject::Array(funcs) = func_obj {
1029 if let Some(PdfObject::Dict(func)) = funcs.first() {
1030 return extract_stops_from_function(func, cs_name);
1031 }
1032 }
1033 }
1034
1035 vec![
1036 GradientStop::new(0.0, Color::BLACK),
1037 GradientStop::new(1.0, Color::WHITE),
1038 ]
1039}
1040
1041fn extract_stops_from_function(func: &PdfDict, cs_name: &[u8]) -> Vec<GradientStop> {
1042 let func_type = func.get_i64(b"FunctionType").unwrap_or(0);
1043
1044 if func_type == 2 {
1045 let c0 = func
1046 .get_array(b"C0")
1047 .map(|a| a.iter().map(|o| o.as_f64().unwrap_or(0.0)).collect::<Vec<_>>())
1048 .unwrap_or_else(|| vec![0.0]);
1049 let c1 = func
1050 .get_array(b"C1")
1051 .map(|a| a.iter().map(|o| o.as_f64().unwrap_or(0.0)).collect::<Vec<_>>())
1052 .unwrap_or_else(|| vec![1.0]);
1053
1054 vec![
1055 GradientStop::new(0.0, components_to_color(&c0, cs_name)),
1056 GradientStop::new(1.0, components_to_color(&c1, cs_name)),
1057 ]
1058 } else if func_type == 3 {
1059 let bounds = func
1060 .get_array(b"Bounds")
1061 .map(|a| a.iter().filter_map(|o| o.as_f64()).collect::<Vec<_>>())
1062 .unwrap_or_default();
1063 let functions = func.get_array(b"Functions").unwrap_or(&[]);
1064
1065 let mut color_stops: Vec<(f32, Color)> = Vec::new();
1066
1067 for (i, sub_func) in functions.iter().enumerate() {
1068 if let PdfObject::Dict(sub) = sub_func {
1069 let sub_colors = extract_colors_from_function(sub, cs_name);
1070 let t_start = if i == 0 {
1071 0.0
1072 } else {
1073 bounds.get(i - 1).copied().unwrap_or(0.0) as f32
1074 };
1075 let t_end = if i < bounds.len() {
1076 bounds[i] as f32
1077 } else {
1078 1.0
1079 };
1080
1081 if let Some(c) = sub_colors.first() {
1082 color_stops.push((t_start.clamp(0.0, 1.0), *c));
1083 }
1084 if let Some(c) = sub_colors.last() {
1085 color_stops.push((t_end.clamp(0.0, 1.0), *c));
1086 }
1087 }
1088 }
1089
1090 color_stops.dedup_by(|a, b| (a.0 - b.0).abs() < 0.001);
1091
1092 if color_stops.len() < 2 {
1093 color_stops = vec![(0.0, Color::BLACK), (1.0, Color::WHITE)];
1094 }
1095
1096 color_stops
1097 .into_iter()
1098 .map(|(pos, color)| GradientStop::new(pos, color))
1099 .collect()
1100 } else {
1101 vec![
1102 GradientStop::new(0.0, Color::BLACK),
1103 GradientStop::new(1.0, Color::WHITE),
1104 ]
1105 }
1106}
1107
1108fn extract_colors_from_function(func: &PdfDict, cs_name: &[u8]) -> Vec<Color> {
1109 let c0 = func
1110 .get_array(b"C0")
1111 .map(|a| a.iter().map(|o| o.as_f64().unwrap_or(0.0)).collect::<Vec<_>>())
1112 .unwrap_or_else(|| vec![0.0]);
1113 let c1 = func
1114 .get_array(b"C1")
1115 .map(|a| a.iter().map(|o| o.as_f64().unwrap_or(0.0)).collect::<Vec<_>>())
1116 .unwrap_or_else(|| vec![1.0]);
1117
1118 vec![
1119 components_to_color(&c0, cs_name),
1120 components_to_color(&c1, cs_name),
1121 ]
1122}
1123
1124fn components_to_color(comps: &[f64], cs_name: &[u8]) -> Color {
1129 match cs_name {
1130 b"DeviceGray" | b"CalGray" | b"G" => {
1131 let g = (comps.first().copied().unwrap_or(0.0).clamp(0.0, 1.0) * 255.0) as u8;
1132 Color::from_rgba8(g, g, g, 255)
1133 }
1134 b"DeviceCMYK" | b"CMYK" => {
1135 let c = comps.first().copied().unwrap_or(0.0);
1136 let m = comps.get(1).copied().unwrap_or(0.0);
1137 let y = comps.get(2).copied().unwrap_or(0.0);
1138 let k = comps.get(3).copied().unwrap_or(0.0);
1139 let r = ((1.0 - c) * (1.0 - k) * 255.0) as u8;
1140 let g = ((1.0 - m) * (1.0 - k) * 255.0) as u8;
1141 let b = ((1.0 - y) * (1.0 - k) * 255.0) as u8;
1142 Color::from_rgba8(r, g, b, 255)
1143 }
1144 _ => {
1145 let r = (comps.first().copied().unwrap_or(0.0).clamp(0.0, 1.0) * 255.0) as u8;
1146 let g = (comps.get(1).copied().unwrap_or(0.0).clamp(0.0, 1.0) * 255.0) as u8;
1147 let b = (comps.get(2).copied().unwrap_or(0.0).clamp(0.0, 1.0) * 255.0) as u8;
1148 Color::from_rgba8(r, g, b, 255)
1149 }
1150 }
1151}
1152
1153fn color_space_components(cs_name: &[u8]) -> usize {
1154 match cs_name {
1155 b"DeviceGray" | b"CalGray" | b"G" => 1,
1156 b"DeviceRGB" | b"CalRGB" | b"RGB" => 3,
1157 b"DeviceCMYK" | b"CMYK" => 4,
1158 _ => 3,
1159 }
1160}
1161
1162#[cfg(test)]
1163mod tests {
1164 use super::*;
1165
1166 #[test]
1167 fn test_bit_reader_8bit() {
1168 let data = [0xAB, 0xCD];
1169 let mut r = BitReader::new(&data);
1170 assert_eq!(r.read_bits(8), Some(0xAB));
1171 assert_eq!(r.read_bits(8), Some(0xCD));
1172 assert_eq!(r.read_bits(8), None);
1173 }
1174
1175 #[test]
1176 fn test_bit_reader_4bit() {
1177 let data = [0xAB]; let mut r = BitReader::new(&data);
1179 assert_eq!(r.read_bits(4), Some(0xA)); assert_eq!(r.read_bits(4), Some(0xB)); }
1182
1183 #[test]
1184 fn test_bit_reader_mixed() {
1185 let data = [0b11001010, 0b01010101];
1186 let mut r = BitReader::new(&data);
1187 assert_eq!(r.read_bits(2), Some(0b11));
1188 assert_eq!(r.read_bits(3), Some(0b001));
1189 assert_eq!(r.read_bits(3), Some(0b010));
1190 assert_eq!(r.read_bits(8), Some(0b01010101));
1192 }
1193
1194 #[test]
1195 fn test_bit_reader_16bit() {
1196 let data = [0x12, 0x34];
1197 let mut r = BitReader::new(&data);
1198 assert_eq!(r.read_bits(16), Some(0x1234));
1199 }
1200
1201 #[test]
1202 fn test_components_to_color_rgb() {
1203 let c = components_to_color(&[1.0, 0.0, 0.5], b"DeviceRGB");
1204 assert_eq!(c.red(), 1.0);
1205 assert_eq!(c.green(), 0.0);
1206 assert!((c.blue() - 0.5).abs() < 0.01);
1207 }
1208
1209 #[test]
1210 fn test_components_to_color_gray() {
1211 let c = components_to_color(&[0.5], b"DeviceGray");
1212 assert!((c.red() - c.green()).abs() < 0.01);
1213 assert!((c.green() - c.blue()).abs() < 0.01);
1214 }
1215
1216 #[test]
1217 fn test_rasterize_triangle_basic() {
1218 let mut pixmap = Pixmap::new(10, 10).unwrap();
1219 rasterize_triangle(
1220 &mut pixmap,
1221 (1.0, 1.0, [255, 0, 0, 255]),
1222 (9.0, 1.0, [0, 255, 0, 255]),
1223 (5.0, 9.0, [0, 0, 255, 255]),
1224 Transform::identity(),
1225 None,
1226 );
1227
1228 let has_colored = pixmap.pixels().iter().any(|p| p.alpha() > 0);
1230 assert!(has_colored, "triangle should have colored some pixels");
1231 }
1232
1233 #[test]
1234 fn test_edge_function() {
1235 let a = (0.0, 0.0);
1236 let b = (10.0, 0.0);
1237 let c = (5.0, 5.0);
1238 let area = edge_function(a, b, c);
1239 assert!(area.abs() > 0.0, "degenerate triangle should not have zero area");
1241 }
1242}