1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
// Copyright 2020 Yevhenii Reizner
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
use tiny_skia_path::NoStdFloat;

use alloc::vec;
use alloc::vec::Vec;

use tiny_skia_path::{IntRect, IntSize, Path, Scalar, Transform};

use crate::geom::IntSizeExt;
use crate::painter::DrawTiler;
use crate::pipeline::RasterPipelineBlitter;
use crate::pixmap::SubPixmapMut;
use crate::scan;
use crate::{FillRule, PixmapRef};

/// A mask type.
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum MaskType {
    /// Transfers only the Alpha channel from `Pixmap` to `Mask`.
    Alpha,
    /// Transfers RGB channels as luminance from `Pixmap` to `Mask`.
    ///
    /// Formula: `Y = 0.2126 * R + 0.7152 * G + 0.0722 * B`
    Luminance,
}

/// A mask.
///
/// During drawing over `Pixmap`, mask's black (0) "pixels" would block rendering
/// and white (255) will allow it.
/// Anything in between is used for gradual masking and anti-aliasing.
///
/// Unlike Skia, we're using just a simple 8bit alpha mask.
/// It's way slower, but easier to implement.
#[derive(Clone, PartialEq)]
pub struct Mask {
    data: Vec<u8>,
    size: IntSize,
}

impl Mask {
    /// Creates a new mask by taking ownership over a mask buffer.
    ///
    /// The size needs to match the data provided.
    pub fn new(width: u32, height: u32) -> Option<Self> {
        let size = IntSize::from_wh(width, height)?;
        Some(Mask {
            data: vec![0; width as usize * height as usize],
            size,
        })
    }

    /// Creates a new mask from a `PixmapRef`.
    pub fn from_pixmap(pixmap: PixmapRef, mask_type: MaskType) -> Self {
        let data_len = pixmap.width() as usize * pixmap.height() as usize;
        let mut mask = Mask {
            data: vec![0; data_len],
            size: pixmap.size(),
        };

        // TODO: optimize
        match mask_type {
            MaskType::Alpha => {
                for (p, a) in pixmap.pixels().iter().zip(mask.data.as_mut_slice()) {
                    *a = p.alpha();
                }
            }
            MaskType::Luminance => {
                for (p, ma) in pixmap.pixels().iter().zip(mask.data.as_mut_slice()) {
                    // Normalize.
                    let mut r = f32::from(p.red()) / 255.0;
                    let mut g = f32::from(p.green()) / 255.0;
                    let mut b = f32::from(p.blue()) / 255.0;
                    let a = f32::from(p.alpha()) / 255.0;

                    // Demultiply.
                    if p.alpha() != 0 {
                        r /= a;
                        g /= a;
                        b /= a;
                    }

                    let luma = r * 0.2126 + g * 0.7152 + b * 0.0722;
                    *ma = ((luma * a) * 255.0).clamp(0.0, 255.0).ceil() as u8;
                }
            }
        }

        mask
    }

    /// Creates a new mask by taking ownership over a mask buffer.
    ///
    /// The size needs to match the data provided.
    pub fn from_vec(data: Vec<u8>, size: IntSize) -> Option<Self> {
        let data_len = size.width() as usize * size.height() as usize;
        if data.len() != data_len {
            return None;
        }

        Some(Mask { data, size })
    }

    /// Returns mask's width.
    #[inline]
    pub fn width(&self) -> u32 {
        self.size.width()
    }

    /// Returns mask's height.
    #[inline]
    pub fn height(&self) -> u32 {
        self.size.height()
    }

    /// Returns mask's size.
    #[allow(dead_code)]
    pub(crate) fn size(&self) -> IntSize {
        self.size
    }

    /// Returns the internal data.
    pub fn data(&self) -> &[u8] {
        self.data.as_slice()
    }

    /// Returns the mutable internal data.
    pub fn data_mut(&mut self) -> &mut [u8] {
        self.data.as_mut_slice()
    }

    pub(crate) fn as_submask(&self) -> SubMaskRef<'_> {
        SubMaskRef {
            size: self.size,
            real_width: self.size.width(),
            data: &self.data,
        }
    }

    pub(crate) fn submask(&self, rect: IntRect) -> Option<SubMaskRef<'_>> {
        let rect = self.size.to_int_rect(0, 0).intersect(&rect)?;
        let row_bytes = self.width() as usize;
        let offset = rect.top() as usize * row_bytes + rect.left() as usize;

        Some(SubMaskRef {
            size: rect.size(),
            real_width: self.size.width(),
            data: &self.data[offset..],
        })
    }

    pub(crate) fn as_subpixmap(&mut self) -> SubPixmapMut<'_> {
        SubPixmapMut {
            size: self.size,
            real_width: self.size.width() as usize,
            data: &mut self.data,
        }
    }

    pub(crate) fn subpixmap(&mut self, rect: IntRect) -> Option<SubPixmapMut<'_>> {
        let rect = self.size.to_int_rect(0, 0).intersect(&rect)?;
        let row_bytes = self.width() as usize;
        let offset = rect.top() as usize * row_bytes + rect.left() as usize;

        Some(SubPixmapMut {
            size: rect.size(),
            real_width: self.size.width() as usize,
            data: &mut self.data[offset..],
        })
    }

    /// Loads a PNG file into a `Mask`.
    ///
    /// Only grayscale images are supported.
    #[cfg(feature = "png-format")]
    pub fn decode_png(data: &[u8]) -> Result<Self, png::DecodingError> {
        fn make_custom_png_error(msg: &str) -> png::DecodingError {
            std::io::Error::new(std::io::ErrorKind::Other, msg).into()
        }

        let mut decoder = png::Decoder::new(data);
        decoder.set_transformations(png::Transformations::normalize_to_color8());
        let mut reader = decoder.read_info()?;
        let mut img_data = vec![0; reader.output_buffer_size()];
        let info = reader.next_frame(&mut img_data)?;

        if info.bit_depth != png::BitDepth::Eight {
            return Err(make_custom_png_error("unsupported bit depth"));
        }

        if info.color_type != png::ColorType::Grayscale {
            return Err(make_custom_png_error("only grayscale masks are supported"));
        }

        let size = IntSize::from_wh(info.width, info.height)
            .ok_or_else(|| make_custom_png_error("invalid image size"))?;

        Mask::from_vec(img_data, size)
            .ok_or_else(|| make_custom_png_error("failed to create a mask"))
    }

    /// Loads a PNG file into a `Mask`.
    ///
    /// Only grayscale images are supported.
    #[cfg(feature = "png-format")]
    pub fn load_png<P: AsRef<std::path::Path>>(path: P) -> Result<Self, png::DecodingError> {
        // `png::Decoder` is generic over input, which means that it will instance
        // two copies: one for `&[]` and one for `File`. Which will simply bloat the code.
        // Therefore we're using only one type for input.
        let data = std::fs::read(path)?;
        Self::decode_png(&data)
    }

    /// Encodes mask into a PNG data.
    #[cfg(feature = "png-format")]
    pub fn encode_png(&self) -> Result<Vec<u8>, png::EncodingError> {
        let mut data = Vec::new();
        {
            let mut encoder = png::Encoder::new(&mut data, self.width(), self.height());
            encoder.set_color(png::ColorType::Grayscale);
            encoder.set_depth(png::BitDepth::Eight);
            let mut writer = encoder.write_header()?;
            writer.write_image_data(&self.data)?;
        }

        Ok(data)
    }

    /// Saves mask as a PNG file.
    #[cfg(feature = "png-format")]
    pub fn save_png<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), png::EncodingError> {
        let data = self.encode_png()?;
        std::fs::write(path, data)?;
        Ok(())
    }

    // Almost a direct copy of PixmapMut::fill_path
    /// Draws a filled path onto the mask.
    ///
    /// In terms of RGB (no alpha) image, draws a white path on top of black mask.
    ///
    /// Doesn't reset the existing mask content and draws the path on top of existing data.
    ///
    /// If the above behavior is undesired, [`Mask::clear()`] should be called first.
    ///
    /// This method is intended to be used for simple cases. For more complex masks
    /// prefer [`Mask::from_pixmap()`].
    pub fn fill_path(
        &mut self,
        path: &Path,
        fill_rule: FillRule,
        anti_alias: bool,
        transform: Transform,
    ) {
        if transform.is_identity() {
            // This is sort of similar to SkDraw::drawPath

            // Skip empty paths and horizontal/vertical lines.
            let path_bounds = path.bounds();
            if path_bounds.width().is_nearly_zero() || path_bounds.height().is_nearly_zero() {
                log::warn!("empty paths and horizontal/vertical lines cannot be filled");
                return;
            }

            if crate::painter::is_too_big_for_math(path) {
                log::warn!("path coordinates are too big");
                return;
            }

            // TODO: ignore paths outside the pixmap

            if let Some(tiler) = DrawTiler::new(self.width(), self.height()) {
                let mut path = path.clone(); // TODO: avoid cloning

                for tile in tiler {
                    let ts = Transform::from_translate(-(tile.x() as f32), -(tile.y() as f32));
                    path = match path.transform(ts) {
                        Some(v) => v,
                        None => {
                            log::warn!("path transformation failed");
                            return;
                        }
                    };

                    let clip_rect = tile.size().to_screen_int_rect(0, 0);
                    let mut subpix = match self.subpixmap(tile.to_int_rect()) {
                        Some(v) => v,
                        None => continue, // technically unreachable
                    };

                    let mut blitter = match RasterPipelineBlitter::new_mask(&mut subpix) {
                        Some(v) => v,
                        None => continue, // nothing to do, all good
                    };

                    // We're ignoring "errors" here, because `fill_path` will return `None`
                    // when rendering a tile that doesn't have a path on it.
                    // Which is not an error in this case.
                    if anti_alias {
                        scan::path_aa::fill_path(&path, fill_rule, &clip_rect, &mut blitter);
                    } else {
                        scan::path::fill_path(&path, fill_rule, &clip_rect, &mut blitter);
                    }

                    let ts = Transform::from_translate(tile.x() as f32, tile.y() as f32);
                    path = match path.transform(ts) {
                        Some(v) => v,
                        None => return, // technically unreachable
                    };
                }
            } else {
                let clip_rect = self.size().to_screen_int_rect(0, 0);
                let mut subpix = self.as_subpixmap();
                let mut blitter = match RasterPipelineBlitter::new_mask(&mut subpix) {
                    Some(v) => v,
                    None => return, // nothing to do, all good
                };

                if anti_alias {
                    scan::path_aa::fill_path(path, fill_rule, &clip_rect, &mut blitter);
                } else {
                    scan::path::fill_path(path, fill_rule, &clip_rect, &mut blitter);
                }
            }
        } else {
            let path = match path.clone().transform(transform) {
                Some(v) => v,
                None => {
                    log::warn!("path transformation failed");
                    return;
                }
            };

            self.fill_path(&path, fill_rule, anti_alias, Transform::identity());
        }
    }

    /// Intersects the provided path with the current clipping path.
    ///
    /// A temporary mask with the same size as the current one will be created.
    pub fn intersect_path(
        &mut self,
        path: &Path,
        fill_rule: FillRule,
        anti_alias: bool,
        transform: Transform,
    ) {
        let mut submask = Mask::new(self.width(), self.height()).unwrap();
        submask.fill_path(path, fill_rule, anti_alias, transform);

        for (a, b) in self.data.iter_mut().zip(submask.data.iter()) {
            *a = crate::color::premultiply_u8(*a, *b);
        }
    }

    /// Inverts the mask.
    pub fn invert(&mut self) {
        self.data.iter_mut().for_each(|a| *a = 255 - *a);
    }

    /// Clears the mask.
    ///
    /// Zero-fills the internal data buffer.
    pub fn clear(&mut self) {
        self.data.fill(0);
    }
}

impl core::fmt::Debug for Mask {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_struct("Mask")
            .field("data", &"...")
            .field("width", &self.size.width())
            .field("height", &self.size.height())
            .finish()
    }
}

#[derive(Clone, Copy)]
pub struct SubMaskRef<'a> {
    pub data: &'a [u8],
    pub size: IntSize,
    pub real_width: u32,
}

impl<'a> SubMaskRef<'a> {
    pub(crate) fn mask_ctx(&self) -> crate::pipeline::MaskCtx<'a> {
        crate::pipeline::MaskCtx {
            data: self.data,
            real_width: self.real_width,
        }
    }
}