1#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
7use ruisa_path::NoStdFloat;
8
9use alloc::vec;
10use alloc::vec::Vec;
11
12use ruisa_path::{IntRect, IntSize, Path, Scalar, Transform};
13
14use crate::painter::DrawTiler;
15use crate::pipeline::RasterPipelineBlitter;
16use crate::canvas::SubCanvasMut;
17use crate::scan;
18use crate::{FillRule, CanvasRef};
19
20#[derive(Clone, Copy, PartialEq, Debug)]
22pub enum MaskType {
23 Alpha,
25 Luminance,
29}
30
31#[derive(Clone, PartialEq)]
40pub struct Mask {
41 data: Vec<u8>,
42 size: IntSize,
43}
44
45impl Mask {
46 pub fn new(width: u32, height: u32) -> Option<Self> {
50 let size = IntSize::from_wh(width, height)?;
51 Some(Mask {
52 data: vec![0; width as usize * height as usize],
53 size,
54 })
55 }
56
57 pub fn from_canvas(canvas: CanvasRef, mask_type: MaskType) -> Self {
59 let data_len = canvas.width() as usize * canvas.height() as usize;
60 let mut mask = Mask {
61 data: vec![0; data_len],
62 size: canvas.size(),
63 };
64
65 match mask_type {
67 MaskType::Alpha => {
68 for (p, a) in canvas.pixels().iter().zip(mask.data.as_mut_slice()) {
69 *a = p.alpha();
70 }
71 }
72 MaskType::Luminance => {
73 for (p, ma) in canvas.pixels().iter().zip(mask.data.as_mut_slice()) {
74 let mut r = f32::from(p.red()) / 255.0;
76 let mut g = f32::from(p.green()) / 255.0;
77 let mut b = f32::from(p.blue()) / 255.0;
78 let a = f32::from(p.alpha()) / 255.0;
79
80 if p.alpha() != 0 {
82 r /= a;
83 g /= a;
84 b /= a;
85 }
86
87 let luma = r * 0.2126 + g * 0.7152 + b * 0.0722;
88 *ma = ((luma * a) * 255.0).clamp(0.0, 255.0).ceil() as u8;
89 }
90 }
91 }
92
93 mask
94 }
95
96 pub fn from_vec(data: Vec<u8>, size: IntSize) -> Option<Self> {
100 let data_len = size.width() as usize * size.height() as usize;
101 if data.len() != data_len {
102 return None;
103 }
104
105 Some(Mask { data, size })
106 }
107
108 #[inline]
110 pub fn width(&self) -> u32 {
111 self.size.width()
112 }
113
114 #[inline]
116 pub fn height(&self) -> u32 {
117 self.size.height()
118 }
119
120 #[allow(dead_code)]
122 pub(crate) fn size(&self) -> IntSize {
123 self.size
124 }
125
126 pub fn data(&self) -> &[u8] {
128 self.data.as_slice()
129 }
130
131 pub fn data_mut(&mut self) -> &mut [u8] {
133 self.data.as_mut_slice()
134 }
135
136 pub(crate) fn as_submask<'a>(&'a self) -> SubMaskRef<'a> {
137 SubMaskRef {
138 size: self.size,
139 real_width: self.size.width(),
140 data: &self.data,
141 }
142 }
143
144 pub(crate) fn submask<'a>(&'a self, rect: IntRect) -> Option<SubMaskRef<'a>> {
145 let rect = self.size.to_int_rect(0, 0).intersect(&rect)?;
146 let row_bytes = self.width() as usize;
147 let offset = rect.top() as usize * row_bytes + rect.left() as usize;
148
149 Some(SubMaskRef {
150 size: rect.size(),
151 real_width: self.size.width(),
152 data: &self.data[offset..],
153 })
154 }
155
156 pub(crate) fn as_subcanvas<'a>(&'a mut self) -> SubCanvasMut<'a> {
157 SubCanvasMut {
158 size: self.size,
159 real_width: self.size.width() as usize,
160 data: &mut self.data,
161 }
162 }
163
164 pub(crate) fn subcanvas<'a>(&'a mut self, rect: IntRect) -> Option<SubCanvasMut<'a>> {
165 let rect = self.size.to_int_rect(0, 0).intersect(&rect)?;
166 let row_bytes = self.width() as usize;
167 let offset = rect.top() as usize * row_bytes + rect.left() as usize;
168
169 Some(SubCanvasMut {
170 size: rect.size(),
171 real_width: self.size.width() as usize,
172 data: &mut self.data[offset..],
173 })
174 }
175
176 pub fn decode_png(data: &[u8]) -> Result<Self, png::DecodingError> {
180 fn make_custom_png_error(msg: &str) -> png::DecodingError {
181 std::io::Error::new(std::io::ErrorKind::Other, msg).into()
182 }
183
184 let mut decoder = png::Decoder::new(data);
185 decoder.set_transformations(png::Transformations::normalize_to_color8());
186 let mut reader = decoder.read_info()?;
187 let mut img_data = vec![0; reader.output_buffer_size()];
188 let info = reader.next_frame(&mut img_data)?;
189
190 if info.bit_depth != png::BitDepth::Eight {
191 return Err(make_custom_png_error("unsupported bit depth"));
192 }
193
194 if info.color_type != png::ColorType::Grayscale {
195 return Err(make_custom_png_error("only grayscale masks are supported"));
196 }
197
198 let size = IntSize::from_wh(info.width, info.height)
199 .ok_or_else(|| make_custom_png_error("invalid image size"))?;
200
201 Mask::from_vec(img_data, size)
202 .ok_or_else(|| make_custom_png_error("failed to create a mask"))
203 }
204
205 pub fn from_image_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self, png::DecodingError> {
209 let data = std::fs::read(path)?;
213 Self::decode_png(&data)
214 }
215
216 pub fn to_png(&self) -> Result<Vec<u8>, png::EncodingError> {
218 let mut data = Vec::new();
219 {
220 let mut encoder = png::Encoder::new(&mut data, self.width(), self.height());
221 encoder.set_color(png::ColorType::Grayscale);
222 encoder.set_depth(png::BitDepth::Eight);
223 let mut writer = encoder.write_header()?;
224 writer.write_image_data(&self.data)?;
225 }
226
227 Ok(data)
228 }
229
230 pub fn save_png<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), png::EncodingError> {
232 let data = self.to_png()?;
233 std::fs::write(path, data)?;
234 Ok(())
235 }
236
237 pub fn fill_path(
249 &mut self,
250 path: &Path,
251 fill_rule: FillRule,
252 anti_alias: bool,
253 transform: Transform,
254 ) {
255 if transform.is_identity() {
256 let path_bounds = path.bounds();
260 if path_bounds.width().is_nearly_zero() || path_bounds.height().is_nearly_zero() {
261 log::warn!("empty paths and horizontal/vertical lines cannot be filled");
262 return;
263 }
264
265 if crate::painter::is_too_big_for_math(path) {
266 log::warn!("path coordinates are too big");
267 return;
268 }
269
270 if let Some(tiler) = DrawTiler::new(self.width(), self.height()) {
273 let mut path = path.clone(); for tile in tiler {
276 let ts = Transform::from_translate(-(tile.x() as f32), -(tile.y() as f32));
277 path = match path.transform(ts) {
278 Some(v) => v,
279 None => {
280 log::warn!("path transformation failed");
281 return;
282 }
283 };
284
285 let clip_rect = tile.size().to_screen_int_rect(0, 0);
286 let mut subpix = match self.subcanvas(tile.to_int_rect()) {
287 Some(v) => v,
288 None => continue, };
290
291 let mut blitter = match RasterPipelineBlitter::new_mask(&mut subpix) {
292 Some(v) => v,
293 None => continue, };
295
296 if anti_alias {
300 scan::path_aa::fill_path(&path, fill_rule, &clip_rect, &mut blitter);
301 } else {
302 scan::path::fill_path(&path, fill_rule, &clip_rect, &mut blitter);
303 }
304
305 let ts = Transform::from_translate(tile.x() as f32, tile.y() as f32);
306 path = match path.transform(ts) {
307 Some(v) => v,
308 None => return, };
310 }
311 } else {
312 let clip_rect = self.size().to_screen_int_rect(0, 0);
313 let mut subpix = self.as_subcanvas();
314 let mut blitter = match RasterPipelineBlitter::new_mask(&mut subpix) {
315 Some(v) => v,
316 None => return, };
318
319 if anti_alias {
320 scan::path_aa::fill_path(path, fill_rule, &clip_rect, &mut blitter);
321 } else {
322 scan::path::fill_path(path, fill_rule, &clip_rect, &mut blitter);
323 }
324 }
325 } else {
326 let path = match path.clone().transform(transform) {
327 Some(v) => v,
328 None => {
329 log::warn!("path transformation failed");
330 return;
331 }
332 };
333
334 self.fill_path(&path, fill_rule, anti_alias, Transform::identity());
335 }
336 }
337
338 pub fn intersect_path(
342 &mut self,
343 path: &Path,
344 fill_rule: FillRule,
345 anti_alias: bool,
346 transform: Transform,
347 ) {
348 let mut submask = Mask::new(self.width(), self.height()).unwrap();
349 submask.fill_path(path, fill_rule, anti_alias, transform);
350
351 for (a, b) in self.data.iter_mut().zip(submask.data.iter()) {
352 *a = crate::color::premultiply_u8(*a, *b);
353 }
354 }
355
356 pub fn invert(&mut self) {
358 self.data.iter_mut().for_each(|a| *a = 255 - *a);
359 }
360
361 pub fn clear(&mut self) {
365 self.data.fill(0);
366 }
367}
368
369impl core::fmt::Debug for Mask {
370 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
371 f.debug_struct("Mask")
372 .field("data", &"...")
373 .field("width", &self.size.width())
374 .field("height", &self.size.height())
375 .finish()
376 }
377}
378
379#[derive(Clone, Copy)]
380pub struct SubMaskRef<'a> {
381 pub data: &'a [u8],
382 pub size: IntSize,
383 pub real_width: u32,
384}
385
386impl<'a> SubMaskRef<'a> {
387 pub(crate) fn mask_ctx(&self) -> crate::pipeline::MaskCtx<'a> {
388 crate::pipeline::MaskCtx {
389 data: &self.data,
390 real_width: self.real_width,
391 }
392 }
393}