hayro_interpret/
soft_mask.rs

1use crate::InterpreterSettings;
2use crate::cache::Cache;
3use crate::context::Context;
4use crate::device::Device;
5use crate::interpret::state::State;
6use crate::x_object::{XObject, draw_xobject};
7use hayro_syntax::object::Dict;
8use hayro_syntax::object::Name;
9use hayro_syntax::object::ObjectIdentifier;
10use hayro_syntax::object::Stream;
11use hayro_syntax::object::dict::keys::*;
12use hayro_syntax::page::Resources;
13use hayro_syntax::xref::XRef;
14use kurbo::Affine;
15use std::fmt::Debug;
16use std::hash::{Hash, Hasher};
17use std::ops::Deref;
18use std::sync::Arc;
19
20/// Type type of mask.
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub enum MaskType {
23    /// A luminosity mask.
24    Luminosity,
25    /// An alpha mask.
26    Alpha,
27}
28
29struct Repr<'a> {
30    obj_id: ObjectIdentifier,
31    group: XObject<'a>,
32    mask_type: MaskType,
33    parent_resources: Resources<'a>,
34    root_transform: Affine,
35    bbox: kurbo::Rect,
36    object_cache: Cache,
37    settings: InterpreterSettings,
38    xref: &'a XRef,
39}
40
41/// A soft mask.
42#[derive(Clone)]
43pub struct SoftMask<'a>(Arc<Repr<'a>>);
44
45impl Debug for SoftMask<'_> {
46    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47        write!(f, "SoftMask({:?})", self.0.obj_id)
48    }
49}
50
51impl Hash for SoftMask<'_> {
52    fn hash<H: Hasher>(&self, state: &mut H) {
53        // Soft masks are uniquely identified by their object
54        self.0.obj_id.hash(state);
55    }
56}
57
58impl PartialEq for SoftMask<'_> {
59    fn eq(&self, other: &Self) -> bool {
60        self.0.obj_id == other.0.obj_id
61    }
62}
63
64impl Eq for SoftMask<'_> {}
65
66impl<'a> SoftMask<'a> {
67    pub(crate) fn new(
68        dict: &Dict<'a>,
69        context: &Context<'a>,
70        parent_resources: Resources<'a>,
71    ) -> Option<SoftMask<'a>> {
72        // TODO: With this setup, if there is a luminosity mask and alpha mask pointing to the
73        // same xobject, the ID will be the same.
74        let obj_id = dict.get_ref(G)?.into();
75        let group_stream = dict.get::<Stream>(G)?;
76        let group = XObject::new(
77            &group_stream,
78            &context.settings.warning_sink,
79            &context.object_cache,
80        )?;
81        let mask_type = match dict.get::<Name>(S)?.deref() {
82            LUMINOSITY => MaskType::Luminosity,
83            ALPHA => MaskType::Alpha,
84            _ => return None,
85        };
86
87        Some(Self(Arc::new(Repr {
88            obj_id,
89            group,
90            mask_type,
91            root_transform: context.get().ctm,
92            bbox: context.bbox(),
93            object_cache: context.object_cache.clone(),
94            settings: context.settings.clone(),
95            xref: context.xref,
96            parent_resources,
97        })))
98    }
99
100    /// Interpret the contents of the mask into the given device.
101    pub fn interpret(&self, device: &mut impl Device<'a>) {
102        let state = State::new(self.0.root_transform);
103        let mut ctx = Context::new_with(
104            self.0.root_transform,
105            self.0.bbox,
106            self.0.object_cache.clone(),
107            self.0.xref,
108            self.0.settings.clone(),
109            state,
110        );
111        draw_xobject(&self.0.group, &self.0.parent_resources, &mut ctx, device);
112    }
113
114    /// Return the object identifier of the mask.
115    ///
116    /// This can be used as a unique identifier for caching purposes.
117    pub fn id(&self) -> ObjectIdentifier {
118        self.0.obj_id
119    }
120
121    /// Return the underlying mask type.
122    pub fn mask_type(&self) -> MaskType {
123        self.0.mask_type
124    }
125}