hayro_interpret/x_object/
soft_mask.rs1use super::form::FormXObject;
2use crate::color::{Color, ColorComponents, ColorSpace};
3use crate::context::{Context, InterpreterCache};
4use crate::device::Device;
5use crate::function::{Function, TransferFunction};
6use crate::interpret::state::State;
7use crate::util::hash128;
8use crate::{CacheKey, InterpreterSettings};
9use hayro_syntax::object::Name;
10use hayro_syntax::object::ObjectIdentifier;
11use hayro_syntax::object::Stream;
12use hayro_syntax::object::dict::keys::*;
13use hayro_syntax::object::{Dict, Object};
14use hayro_syntax::page::Resources;
15use hayro_syntax::xref::XRef;
16use kurbo::Affine;
17use smallvec::smallvec;
18use std::fmt::Debug;
19use std::hash::{Hash, Hasher};
20use std::ops::Deref;
21use std::rc::Rc;
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25pub enum MaskType {
26 Luminosity,
28 Alpha,
30}
31
32struct Repr<'a> {
33 obj_id: ObjectIdentifier,
34 group: FormXObject<'a>,
35 mask_type: MaskType,
36 parent_resources: Resources<'a>,
37 root_transform: Affine,
38 bbox: kurbo::Rect,
39 interpreter_cache: InterpreterCache<'a>,
40 transfer_function: Option<TransferFunction>,
41 settings: InterpreterSettings,
42 background: Color,
43 xref: &'a XRef,
44 nesting_depth: u32,
45}
46
47impl Hash for Repr<'_> {
48 fn hash<H: Hasher>(&self, state: &mut H) {
49 self.obj_id.hash(state);
50 self.root_transform.cache_key().hash(state);
51 }
52}
53
54#[derive(Clone, Hash)]
56pub struct SoftMask<'a>(Rc<Repr<'a>>);
57
58impl Debug for SoftMask<'_> {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 write!(f, "SoftMask({:?})", self.0.obj_id)
61 }
62}
63
64impl PartialEq for SoftMask<'_> {
65 fn eq(&self, other: &Self) -> bool {
66 self.0.obj_id == other.0.obj_id
67 }
68}
69
70impl Eq for SoftMask<'_> {}
71
72impl CacheKey for SoftMask<'_> {
73 fn cache_key(&self) -> u128 {
74 hash128(self)
75 }
76}
77
78impl<'a> SoftMask<'a> {
79 pub(crate) fn new(
80 dict: &Dict<'a>,
81 context: &Context<'a>,
82 parent_resources: Resources<'a>,
83 ) -> Option<Self> {
84 let obj_id = dict.get_ref(G)?.into();
87 let group_stream = dict.get::<Stream<'_>>(G)?;
88 let group = FormXObject::new(&group_stream)?;
89 let cs = ColorSpace::new(
90 group.dict.get::<Dict<'_>>(GROUP)?.get::<Object<'_>>(CS)?,
91 &context.interpreter_cache.object_cache,
92 )?;
93 let transfer_function = dict
94 .get::<Object<'_>>(TR)
95 .and_then(|o| Function::new(&o))
96 .map(TransferFunction::new);
97 let (mask_type, background) = match dict.get::<Name<'_>>(S)?.deref() {
98 LUMINOSITY => {
99 let color = dict
100 .get::<ColorComponents>(BC)
101 .map(|c| Color::new(cs, c, 1.0))
102 .unwrap_or(Color::new(ColorSpace::device_gray(), smallvec![0.0], 1.0));
103
104 (MaskType::Luminosity, color)
105 }
106 ALPHA => (
107 MaskType::Alpha,
108 Color::new(ColorSpace::device_gray(), smallvec![0.0], 1.0),
110 ),
111 _ => return None,
112 };
113 let nesting_depth = context.nesting_depth() + 1;
114
115 Some(Self(Rc::new(Repr {
116 obj_id,
117 group,
118 mask_type,
119 root_transform: context.get().ctm,
120 transfer_function,
121 bbox: context.bbox(),
122 interpreter_cache: context.interpreter_cache.clone(),
123 settings: context.settings.clone(),
124 xref: context.xref,
125 background,
126 parent_resources,
127 nesting_depth,
128 })))
129 }
130
131 pub fn interpret(&self, device: &mut impl Device<'a>) {
133 let state = State::new(self.0.root_transform);
134 let mut ctx = Context::new_with(
135 self.0.root_transform,
136 self.0.bbox,
137 &self.0.interpreter_cache,
138 self.0.xref,
139 self.0.settings.clone(),
140 state,
141 self.0.nesting_depth,
142 );
143 self.0
144 .group
145 .draw(&self.0.parent_resources, &mut ctx, device);
146 }
147
148 pub fn id(&self) -> ObjectIdentifier {
152 self.0.obj_id
153 }
154
155 pub fn mask_type(&self) -> MaskType {
157 self.0.mask_type
158 }
159
160 pub fn background_color(&self) -> Color {
162 self.0.background.clone()
163 }
164
165 pub fn transfer_function(&self) -> Option<&TransferFunction> {
167 self.0.transfer_function.as_ref()
168 }
169}