zeno/
mask.rs

1//! Mask generator.
2
3use super::geometry::{Origin, Placement, Transform, Vector};
4use super::path_data::{apply, PathData};
5use super::scratch::Scratch;
6use super::style::{Fill, Style};
7#[allow(unused)]
8use super::F32Ext;
9
10use crate::lib::Vec;
11use core::cell::RefCell;
12
13/// The desired output image format for rendering.
14#[derive(Copy, Clone, PartialEq, Debug)]
15pub enum Format {
16    /// 8-bit alpha mask.
17    Alpha,
18    /// 32-bit RGBA subpixel mask with 1/3 pixel offsets for the red and
19    /// blue channels.
20    Subpixel,
21    /// 32-bit RGBA subpixel mask with custom offsets.
22    CustomSubpixel([f32; 3]),
23}
24
25impl Format {
26    /// Creates a format for BGRA subpixel rendering.
27    pub fn subpixel_bgra() -> Self {
28        Self::CustomSubpixel([0.3, 0., -0.3])
29    }
30
31    /// Returns the necessary buffer size to hold an image of the specified
32    /// width and height with this format.
33    pub fn buffer_size(self, width: u32, height: u32) -> usize {
34        (width
35            * height
36            * match self {
37                Self::Alpha => 1,
38                _ => 4,
39            }) as usize
40    }
41}
42
43impl Default for Format {
44    fn default() -> Self {
45        Self::Alpha
46    }
47}
48
49/// Builder for configuring and rendering a mask.
50pub struct Mask<'a, 's, D> {
51    data: D,
52    style: Style<'a>,
53    transform: Option<Transform>,
54    format: Format,
55    origin: Origin,
56    offset: Vector,
57    render_offset: Vector,
58    width: u32,
59    height: u32,
60    explicit_size: bool,
61    has_size: bool,
62    bounds_offset: Vector,
63    scratch: RefCell<Option<&'s mut Scratch>>,
64}
65
66impl<'a, 's, D> Mask<'a, 's, D>
67where
68    D: PathData,
69{
70    /// Creates a new mask builder for the specified path data.
71    pub fn new(data: D) -> Self {
72        Self {
73            data,
74            style: Style::Fill(Fill::NonZero),
75            transform: None,
76            format: Format::Alpha,
77            origin: Origin::TopLeft,
78            offset: Vector::ZERO,
79            render_offset: Vector::ZERO,
80            width: 0,
81            height: 0,
82            explicit_size: false,
83            has_size: false,
84            bounds_offset: Vector::ZERO,
85            scratch: RefCell::new(None),
86        }
87    }
88
89    /// Creates a new mask builder for the specified path data and scratch memory.
90    pub fn with_scratch(data: D, scratch: &'s mut Scratch) -> Self {
91        Self {
92            data,
93            style: Style::Fill(Fill::NonZero),
94            transform: None,
95            format: Format::Alpha,
96            origin: Origin::TopLeft,
97            offset: Vector::ZERO,
98            render_offset: Vector::ZERO,
99            width: 0,
100            height: 0,
101            explicit_size: false,
102            has_size: false,
103            bounds_offset: Vector::ZERO,
104            scratch: RefCell::new(Some(scratch)),
105        }
106    }
107
108    /// Sets the style of the path. The default is a non-zero fill.
109    pub fn style(&mut self, style: impl Into<Style<'a>>) -> &mut Self {
110        self.style = style.into();
111        self
112    }
113
114    /// Sets the transformation matrix of the path.
115    pub fn transform(&mut self, transform: Option<Transform>) -> &mut Self {
116        self.transform = transform;
117        self
118    }
119
120    /// Sets the desired format of the mask. The default value is an 8-bit
121    /// alpha format.
122    pub fn format(&mut self, format: Format) -> &mut Self {
123        self.format = format;
124        self
125    }
126
127    /// Sets the origin that defines the coordinate system for the mask.
128    pub fn origin(&mut self, origin: Origin) -> &mut Self {
129        self.origin = origin;
130        self
131    }
132
133    /// Sets the offset for the path's rendered bounds. To translate both the
134    /// path and its rendered bounding box, set both [`Self::offset`] and
135    /// [`Self::render_offset`].
136    pub fn offset(&mut self, offset: impl Into<Vector>) -> &mut Self {
137        self.offset = offset.into();
138        self
139    }
140
141    /// Sets an explicit size for the mask. If left unspecified, the size will
142    /// be computed from the bounding box of the path after applying any
143    /// relevant style, offset and transform.
144    pub fn size(&mut self, width: u32, height: u32) -> &mut Self {
145        self.width = width;
146        self.height = height;
147        self.explicit_size = true;
148        self.has_size = true;
149        self
150    }
151
152    /// Sets an additional rendering offset for the mask. This offset does not
153    /// affect bounds or size computations and is only applied during
154    /// rendering.
155    pub fn render_offset(&mut self, offset: impl Into<Vector>) -> &mut Self {
156        self.render_offset = offset.into();
157        self
158    }
159
160    /// Invokes a closure with the format, width and height of the mask provided
161    /// as arguments. This is primarily useful for preparing a target buffer without
162    /// interrupting the call chain.
163    pub fn inspect(&mut self, mut f: impl FnMut(Format, u32, u32)) -> &mut Self {
164        self.ensure_size();
165        f(self.format, self.width, self.height);
166        self
167    }
168
169    /// Renders the mask into a byte buffer. If specified, the pitch describes
170    /// the number of bytes between subsequent rows of the target buffer. This
171    /// is primarily useful for rendering into tiled images such as texture
172    /// atlases. If left unspecified, the buffer is assumed to be linear and
173    /// tightly packed.
174    pub fn render_into(&self, buffer: &mut [u8], pitch: Option<usize>) -> Placement {
175        let (offset, placement) = self.placement();
176        let pitch = match pitch {
177            Some(pitch) => pitch,
178            _ => {
179                placement.width as usize
180                    * match self.format {
181                        Format::Alpha => 1,
182                        _ => 4,
183                    }
184            }
185        };
186        render(self, offset, &placement, buffer, pitch);
187        placement
188    }
189
190    /// Renders the mask to a newly allocated buffer.
191    pub fn render(&self) -> (Vec<u8>, Placement) {
192        let mut buf = Vec::new();
193        let (offset, placement) = self.placement();
194        buf.resize(
195            self.format.buffer_size(placement.width, placement.height),
196            0,
197        );
198        let pitch = placement.width as usize
199            * match self.format {
200                Format::Alpha => 1,
201                _ => 4,
202            };
203        render(self, offset, &placement, &mut buf, pitch);
204        (buf, placement)
205    }
206
207    fn ensure_size(&mut self) {
208        if self.has_size {
209            return;
210        }
211        let (offset, placement) = self.placement();
212        self.bounds_offset = offset;
213        self.width = placement.width;
214        self.height = placement.height;
215        self.explicit_size = false;
216        self.has_size = true;
217    }
218
219    fn placement(&self) -> (Vector, Placement) {
220        let mut placement = Placement {
221            left: 0,
222            top: 0,
223            width: self.width,
224            height: self.height,
225        };
226        let mut offset = self.offset;
227        if self.explicit_size {
228            return (offset, placement);
229        } else if !self.has_size {
230            let mut scratch = self.scratch.borrow_mut();
231            let mut bounds = if let Some(scratch) = scratch.as_mut() {
232                scratch.bounds(&self.data, self.style, self.transform)
233            } else {
234                super::bounds(&self.data, self.style, self.transform)
235            };
236            bounds.min = (bounds.min + self.offset).floor();
237            bounds.max = (bounds.max + self.offset).ceil();
238            offset = Vector::new(-bounds.min.x, -bounds.min.y);
239            placement.width = bounds.width() as u32;
240            placement.height = bounds.height() as u32;
241        } else {
242            offset = self.bounds_offset;
243        }
244        placement.left = -offset.x as i32;
245        placement.top = if self.origin == Origin::BottomLeft {
246            (-offset.y).floor() + self.height as f32
247        } else {
248            -offset.y
249        } as i32;
250        (offset, placement)
251    }
252}
253
254#[allow(clippy::needless_lifetimes)]
255pub fn render<'a, 'c, D>(
256    mask: &'a Mask<'a, 'c, D>,
257    offset: Vector,
258    placement: &Placement,
259    buf: &mut [u8],
260    pitch: usize,
261) where
262    D: PathData,
263{
264    let y_up = mask.origin == Origin::BottomLeft;
265    let (is_subpx, subpx) = match mask.format {
266        Format::Alpha => (false, [Vector::ZERO; 3]),
267        Format::Subpixel => (
268            true,
269            [Vector::new(-0.3, 0.), Vector::ZERO, Vector::new(0.3, 0.)],
270        ),
271        Format::CustomSubpixel(subpx) => (
272            true,
273            [
274                Vector::new(subpx[0], 0.),
275                Vector::new(subpx[1], 0.),
276                Vector::new(subpx[2], 0.),
277            ],
278        ),
279    };
280    let fill = match mask.style {
281        Style::Fill(fill) => fill,
282        _ => Fill::NonZero,
283    };
284    let w = placement.width;
285    let h = placement.height;
286    let shift = offset + mask.render_offset;
287    let data = &mask.data;
288    let style = mask.style;
289    let transform = mask.transform;
290    let mut scratch = mask.scratch.borrow_mut();
291    use super::raster::{AdaptiveStorage, Rasterizer};
292    if let Some(scratch) = scratch.as_mut() {
293        let mut ras = Rasterizer::new(&mut scratch.render);
294        let inner = &mut scratch.inner;
295        if is_subpx {
296            ras.rasterize_write(
297                shift + subpx[0],
298                w,
299                h,
300                &mut |r| {
301                    inner.apply(data, &style, transform, r);
302                },
303                fill,
304                pitch,
305                y_up,
306                &mut |row_offset, x, count, coverage| {
307                    let buf = &mut buf[row_offset..];
308                    let mut i = 0;
309                    let mut j = x * 4;
310                    while i < count {
311                        buf[j] = coverage;
312                        i += 1;
313                        j += 4;
314                    }
315                },
316            );
317            ras.rasterize_write(
318                shift + subpx[1],
319                w,
320                h,
321                &mut |r| {
322                    inner.apply(data, &style, transform, r);
323                },
324                fill,
325                pitch,
326                y_up,
327                &mut |row_offset, x, count, coverage| {
328                    let buf = &mut buf[row_offset..];
329                    let mut i = 0;
330                    let mut j = x * 4 + 1;
331                    while i < count {
332                        buf[j] = coverage;
333                        i += 1;
334                        j += 4;
335                    }
336                },
337            );
338            ras.rasterize_write(
339                shift + subpx[2],
340                w,
341                h,
342                &mut |r| {
343                    inner.apply(data, &style, transform, r);
344                },
345                fill,
346                pitch,
347                y_up,
348                &mut |row_offset, x, count, coverage| {
349                    let buf = &mut buf[row_offset..];
350                    let mut i = 0;
351                    let mut j = x * 4 + 2;
352                    while i < count {
353                        buf[j] = coverage;
354                        i += 1;
355                        j += 4;
356                    }
357                },
358            );
359        } else {
360            ras.rasterize(
361                shift,
362                w,
363                h,
364                &mut |r| {
365                    inner.apply(data, &style, transform, r);
366                },
367                fill,
368                buf,
369                pitch,
370                y_up,
371            );
372        }
373    } else {
374        let mut storage = AdaptiveStorage::new();
375        let mut ras = Rasterizer::new(&mut storage);
376        if is_subpx {
377            ras.rasterize_write(
378                shift + subpx[0],
379                w,
380                h,
381                &mut |r| {
382                    apply(data, style, transform, r);
383                },
384                fill,
385                pitch,
386                y_up,
387                &mut |row_offset, x, count, coverage| {
388                    let buf = &mut buf[row_offset..];
389                    let mut i = 0;
390                    let mut j = x * 4;
391                    while i < count {
392                        buf[j] = coverage;
393                        i += 1;
394                        j += 4;
395                    }
396                },
397            );
398            ras.rasterize_write(
399                shift + subpx[1],
400                w,
401                h,
402                &mut |r| {
403                    apply(data, style, transform, r);
404                },
405                fill,
406                pitch,
407                y_up,
408                &mut |row_offset, x, count, coverage| {
409                    let buf = &mut buf[row_offset..];
410                    let mut i = 0;
411                    let mut j = x * 4 + 1;
412                    while i < count {
413                        buf[j] = coverage;
414                        i += 1;
415                        j += 4;
416                    }
417                },
418            );
419            ras.rasterize_write(
420                shift + subpx[2],
421                w,
422                h,
423                &mut |r| {
424                    apply(data, style, transform, r);
425                },
426                fill,
427                pitch,
428                y_up,
429                &mut |row_offset, x, count, coverage| {
430                    let buf = &mut buf[row_offset..];
431                    let mut i = 0;
432                    let mut j = x * 4 + 2;
433                    while i < count {
434                        buf[j] = coverage;
435                        i += 1;
436                        j += 4;
437                    }
438                },
439            );
440        } else {
441            ras.rasterize(
442                shift,
443                w,
444                h,
445                &mut |r| {
446                    apply(data, style, transform, r);
447                },
448                fill,
449                buf,
450                pitch,
451                y_up,
452            );
453        }
454    }
455}