hayro_interpret/
soft_mask.rs

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