1use crate::geometry::Transform;
12use crate::path::{FillRule, Path};
13use crate::pixmap::FLATTEN_TOLERANCE;
14use crate::raster::Rasterizer;
15
16#[derive(Clone)]
18pub struct Mask {
19 width: u32,
20 height: u32,
21 data: Vec<u8>,
22}
23
24impl Mask {
25 pub fn new(width: u32, height: u32) -> Option<Mask> {
28 if width == 0 || height == 0 {
29 return None;
30 }
31 let len = (width as usize).checked_mul(height as usize)?;
32 Some(Mask { width, height, data: vec![0; len] })
33 }
34
35 pub fn from_path(
37 width: u32,
38 height: u32,
39 path: &Path,
40 fill_rule: FillRule,
41 anti_alias: bool,
42 transform: Transform,
43 ) -> Option<Mask> {
44 let mut mask = Mask::new(width, height)?;
45 mask.fill_path(path, fill_rule, anti_alias, transform);
46 Some(mask)
47 }
48
49 #[inline]
50 pub fn width(&self) -> u32 {
51 self.width
52 }
53
54 #[inline]
55 pub fn height(&self) -> u32 {
56 self.height
57 }
58
59 #[inline]
61 pub fn data(&self) -> &[u8] {
62 &self.data
63 }
64
65 #[inline]
68 pub(crate) fn coverage_at(&self, x: usize, y: usize) -> f32 {
69 if x >= self.width as usize || y >= self.height as usize {
70 return 0.0;
71 }
72 self.data[y * self.width as usize + x] as f32 / 255.0
73 }
74
75 pub fn fill_path(
77 &mut self,
78 path: &Path,
79 fill_rule: FillRule,
80 anti_alias: bool,
81 transform: Transform,
82 ) {
83 self.rasterize(path, fill_rule, anti_alias, transform, |old, cov| {
84 old.max(cov)
85 });
86 }
87
88 pub fn intersect_path(
91 &mut self,
92 path: &Path,
93 fill_rule: FillRule,
94 anti_alias: bool,
95 transform: Transform,
96 ) {
97 let coverage = match Mask::from_path(self.width, self.height, path, fill_rule, anti_alias, transform)
100 {
101 Some(m) => m,
102 None => return,
103 };
104 for (dst, &src) in self.data.iter_mut().zip(coverage.data.iter()) {
105 *dst = ((*dst as u16 * src as u16 + 127) / 255) as u8;
106 }
107 }
108
109 fn rasterize<F: Fn(f32, f32) -> f32>(
111 &mut self,
112 path: &Path,
113 fill_rule: FillRule,
114 anti_alias: bool,
115 transform: Transform,
116 combine: F,
117 ) {
118 let tol = FLATTEN_TOLERANCE / transform.max_scale().max(1e-3);
119 let contours = path.to_contours(transform, tol);
120 if contours.is_empty() {
121 return;
122 }
123
124 let (mut min_x, mut min_y) = (f32::INFINITY, f32::INFINITY);
127 let (mut max_x, mut max_y) = (f32::NEG_INFINITY, f32::NEG_INFINITY);
128 for c in contours.iter() {
129 for p in &c.points {
130 min_x = min_x.min(p.x);
131 min_y = min_y.min(p.y);
132 max_x = max_x.max(p.x);
133 max_y = max_y.max(p.y);
134 }
135 }
136 if !(min_x <= max_x && min_y <= max_y) {
137 return;
138 }
139
140 let mw = self.width as i32;
141 let mh = self.height as i32;
142 let x0 = (min_x.floor() as i32 - 1).clamp(0, mw);
143 let y0 = (min_y.floor() as i32 - 1).clamp(0, mh);
144 let x1 = (max_x.ceil() as i32 + 1).clamp(0, mw);
145 let y1 = (max_y.ceil() as i32 + 1).clamp(0, mh);
146 let bw = (x1 - x0) as usize;
147 let bh = (y1 - y0) as usize;
148 if bw == 0 || bh == 0 {
149 return;
150 }
151
152 let mut rast = Rasterizer::new(x0, y0, bw, bh);
153 for c in contours.iter() {
154 let n = c.points.len();
155 if n < 2 {
156 continue;
157 }
158 for i in 0..n {
159 rast.add_line(c.points[i], c.points[(i + 1) % n]);
160 }
161 }
162
163 let width = self.width as usize;
164 let data = &mut self.data;
165 rast.for_each_pixel(fill_rule, |x, y, coverage| {
166 let cov = if anti_alias {
167 coverage
168 } else if coverage >= 0.5 {
169 1.0
170 } else {
171 0.0
172 };
173 let idx = y * width + x;
174 let old = data[idx] as f32 / 255.0;
175 let new = combine(old, cov).clamp(0.0, 1.0);
176 data[idx] = (new * 255.0 + 0.5) as u8;
177 });
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184 use crate::geometry::Rect;
185 use crate::path::PathBuilder;
186
187 #[test]
188 fn rect_mask_covers_interior_only() {
189 let path = PathBuilder::from_rect(Rect::from_xywh(2.0, 2.0, 6.0, 6.0).unwrap());
190 let mask = Mask::from_path(10, 10, &path, FillRule::NonZero, true, Transform::identity())
191 .unwrap();
192 assert!(mask.coverage_at(5, 5) > 0.99, "inside");
193 assert!(mask.coverage_at(0, 0) < 0.01, "outside");
194 }
195
196 #[test]
197 fn intersect_keeps_overlap_only() {
198 let left = PathBuilder::from_rect(Rect::from_xywh(0.0, 0.0, 6.0, 10.0).unwrap());
199 let mut mask =
200 Mask::from_path(10, 10, &left, FillRule::NonZero, true, Transform::identity()).unwrap();
201 let right = PathBuilder::from_rect(Rect::from_xywh(4.0, 0.0, 6.0, 10.0).unwrap());
202 mask.intersect_path(&right, FillRule::NonZero, true, Transform::identity());
203 assert!(mask.coverage_at(5, 5) > 0.99, "intersection");
205 assert!(mask.coverage_at(1, 5) < 0.01, "outside the intersection");
206 }
207}