1 | // Copyright 2006 The Android Open Source Project |
2 | // Copyright 2020 Yevhenii Reizner |
3 | // |
4 | // Use of this source code is governed by a BSD-style license that can be |
5 | // found in the LICENSE file. |
6 | |
7 | use core::convert::TryFrom; |
8 | |
9 | use crate::{FillRule, IntRect, LengthU32, Path, Rect}; |
10 | |
11 | use crate::alpha_runs::AlphaRuns; |
12 | use crate::blitter::Blitter; |
13 | use crate::color::AlphaU8; |
14 | use crate::geom::{IntRectExt, ScreenIntRect}; |
15 | use crate::math::left_shift; |
16 | |
17 | #[cfg (all(not(feature = "std" ), feature = "no-std-float" ))] |
18 | use tiny_skia_path::NoStdFloat; |
19 | |
20 | /// controls how much we super-sample (when we use that scan conversion) |
21 | const SUPERSAMPLE_SHIFT: u32 = 2; |
22 | |
23 | const SHIFT: u32 = SUPERSAMPLE_SHIFT; |
24 | const SCALE: u32 = 1 << SHIFT; |
25 | const MASK: u32 = SCALE - 1; |
26 | |
27 | pub fn fill_path( |
28 | path: &Path, |
29 | fill_rule: FillRule, |
30 | clip: &ScreenIntRect, |
31 | blitter: &mut dyn Blitter, |
32 | ) { |
33 | // Unlike `path.bounds.to_rect()?.round_out()`, |
34 | // this method rounds out first and then converts into a Rect. |
35 | let ir = Rect::from_ltrb( |
36 | path.bounds().left().floor(), |
37 | path.bounds().top().floor(), |
38 | path.bounds().right().ceil(), |
39 | path.bounds().bottom().ceil(), |
40 | ) |
41 | .and_then(|r| r.round_out()); |
42 | let ir = match ir { |
43 | Some(v) => v, |
44 | None => return, |
45 | }; |
46 | |
47 | // TODO: remove |
48 | // If the intersection of the path bounds and the clip bounds |
49 | // will overflow 32767 when << by SHIFT, we can't supersample, |
50 | // so draw without antialiasing. |
51 | let clipped_ir = match ir.intersect(&clip.to_int_rect()) { |
52 | Some(v) => v, |
53 | None => return, |
54 | }; |
55 | if rect_overflows_short_shift(&clipped_ir, SHIFT as i32) != 0 { |
56 | super::path::fill_path(path, fill_rule, clip, blitter); |
57 | return; |
58 | } |
59 | |
60 | // TODO: remove |
61 | // Our antialiasing can't handle a clip larger than 32767. |
62 | // TODO: skia actually limits the clip to 32767 |
63 | { |
64 | const MAX_CLIP_COORD: u32 = 32767; |
65 | if clip.right() > MAX_CLIP_COORD || clip.bottom() > MAX_CLIP_COORD { |
66 | return; |
67 | } |
68 | } |
69 | |
70 | // TODO: SkScanClipper |
71 | // TODO: AAA |
72 | |
73 | fill_path_impl(path, fill_rule, &ir, clip, blitter) |
74 | } |
75 | |
76 | // Would any of the coordinates of this rectangle not fit in a short, |
77 | // when left-shifted by shift? |
78 | fn rect_overflows_short_shift(rect: &IntRect, shift: i32) -> i32 { |
79 | debug_assert!(overflows_short_shift(8191, shift) == 0); |
80 | debug_assert!(overflows_short_shift(8192, shift) != 0); |
81 | debug_assert!(overflows_short_shift(32767, 0) == 0); |
82 | debug_assert!(overflows_short_shift(32768, 0) != 0); |
83 | |
84 | // Since we expect these to succeed, we bit-or together |
85 | // for a tiny extra bit of speed. |
86 | overflows_short_shift(value:rect.left(), shift) |
87 | | overflows_short_shift(value:rect.top(), shift) |
88 | | overflows_short_shift(value:rect.right(), shift) |
89 | | overflows_short_shift(value:rect.bottom(), shift) |
90 | } |
91 | |
92 | fn overflows_short_shift(value: i32, shift: i32) -> i32 { |
93 | let s: i32 = 16 + shift; |
94 | (left_shift(value, shift:s) >> s) - value |
95 | } |
96 | |
97 | fn fill_path_impl( |
98 | path: &Path, |
99 | fill_rule: FillRule, |
100 | bounds: &IntRect, |
101 | clip: &ScreenIntRect, |
102 | blitter: &mut dyn Blitter, |
103 | ) { |
104 | // TODO: MaskSuperBlitter |
105 | |
106 | // TODO: 15% slower than skia, find out why |
107 | let mut blitter = match SuperBlitter::new(bounds, clip, blitter) { |
108 | Some(v) => v, |
109 | None => return, // clipped out, nothing else to do |
110 | }; |
111 | |
112 | let path_contained_in_clip = if let Some(bounds) = bounds.to_screen_int_rect() { |
113 | clip.contains(&bounds) |
114 | } else { |
115 | // If bounds cannot be converted into ScreenIntRect, |
116 | // the path is out of clip. |
117 | false |
118 | }; |
119 | |
120 | super::path::fill_path_impl( |
121 | path, |
122 | fill_rule, |
123 | clip, |
124 | bounds.top(), |
125 | bounds.bottom(), |
126 | SHIFT as i32, |
127 | path_contained_in_clip, |
128 | &mut blitter, |
129 | ); |
130 | } |
131 | |
132 | struct BaseSuperBlitter<'a> { |
133 | real_blitter: &'a mut dyn Blitter, |
134 | |
135 | /// Current y coordinate, in destination coordinates. |
136 | curr_iy: i32, |
137 | /// Widest row of region to be blitted, in destination coordinates. |
138 | width: LengthU32, |
139 | /// Leftmost x coordinate in any row, in destination coordinates. |
140 | left: u32, |
141 | /// Leftmost x coordinate in any row, in supersampled coordinates. |
142 | super_left: u32, |
143 | |
144 | /// Current y coordinate in supersampled coordinates. |
145 | curr_y: i32, |
146 | /// Initial y coordinate (top of bounds). |
147 | top: i32, |
148 | } |
149 | |
150 | impl<'a> BaseSuperBlitter<'a> { |
151 | fn new( |
152 | bounds: &IntRect, |
153 | clip_rect: &ScreenIntRect, |
154 | blitter: &'a mut dyn Blitter, |
155 | ) -> Option<Self> { |
156 | let sect: ScreenIntRect = boundsIntRect |
157 | .intersect(&clip_rect.to_int_rect())? |
158 | .to_screen_int_rect()?; |
159 | Some(BaseSuperBlitter { |
160 | real_blitter: blitter, |
161 | curr_iy: sect.top() as i32 - 1, |
162 | width: sect.width_safe(), |
163 | left: sect.left(), |
164 | super_left: sect.left() << SHIFT, |
165 | curr_y: (sect.top() << SHIFT) as i32 - 1, |
166 | top: sect.top() as i32, |
167 | }) |
168 | } |
169 | } |
170 | |
171 | struct SuperBlitter<'a> { |
172 | base: BaseSuperBlitter<'a>, |
173 | runs: AlphaRuns, |
174 | offset_x: usize, |
175 | } |
176 | |
177 | impl<'a> SuperBlitter<'a> { |
178 | fn new( |
179 | bounds: &IntRect, |
180 | clip_rect: &ScreenIntRect, |
181 | blitter: &'a mut dyn Blitter, |
182 | ) -> Option<Self> { |
183 | let base = BaseSuperBlitter::new(bounds, clip_rect, blitter)?; |
184 | let runs_width = base.width; |
185 | Some(SuperBlitter { |
186 | base, |
187 | runs: AlphaRuns::new(runs_width), |
188 | offset_x: 0, |
189 | }) |
190 | } |
191 | |
192 | /// Once `runs` contains a complete supersampled row, flush() blits |
193 | /// it out through the wrapped blitter. |
194 | fn flush(&mut self) { |
195 | if self.base.curr_iy >= self.base.top { |
196 | if !self.runs.is_empty() { |
197 | self.base.real_blitter.blit_anti_h( |
198 | self.base.left, |
199 | u32::try_from(self.base.curr_iy).unwrap(), |
200 | &mut self.runs.alpha, |
201 | &mut self.runs.runs, |
202 | ); |
203 | self.runs.reset(self.base.width); |
204 | self.offset_x = 0; |
205 | } |
206 | |
207 | self.base.curr_iy = self.base.top - 1; |
208 | } |
209 | } |
210 | } |
211 | |
212 | impl Drop for SuperBlitter<'_> { |
213 | fn drop(&mut self) { |
214 | self.flush(); |
215 | } |
216 | } |
217 | |
218 | impl Blitter for SuperBlitter<'_> { |
219 | /// Blits a row of pixels, with location and width specified |
220 | /// in supersampled coordinates. |
221 | fn blit_h(&mut self, mut x: u32, y: u32, mut width: LengthU32) { |
222 | let iy = (y >> SHIFT) as i32; |
223 | debug_assert!(iy >= self.base.curr_iy); |
224 | |
225 | // hack, until I figure out why my cubics (I think) go beyond the bounds |
226 | match x.checked_sub(self.base.super_left) { |
227 | Some(n) => x = n, |
228 | None => { |
229 | width = LengthU32::new(x + width.get()).unwrap(); |
230 | x = 0; |
231 | } |
232 | } |
233 | |
234 | debug_assert!(y as i32 >= self.base.curr_y); |
235 | if self.base.curr_y != y as i32 { |
236 | self.offset_x = 0; |
237 | self.base.curr_y = y as i32; |
238 | } |
239 | |
240 | if iy != self.base.curr_iy { |
241 | // new scanline |
242 | self.flush(); |
243 | self.base.curr_iy = iy; |
244 | } |
245 | |
246 | let start = x; |
247 | let stop = x + width.get(); |
248 | |
249 | debug_assert!(stop > start); |
250 | // integer-pixel-aligned ends of blit, rounded out |
251 | let mut fb = start & MASK; |
252 | let mut fe = stop & MASK; |
253 | let mut n: i32 = (stop as i32 >> SHIFT) - (start as i32 >> SHIFT) - 1; |
254 | |
255 | if n < 0 { |
256 | fb = fe - fb; |
257 | n = 0; |
258 | fe = 0; |
259 | } else { |
260 | if fb == 0 { |
261 | n += 1; |
262 | } else { |
263 | fb = SCALE - fb; |
264 | } |
265 | } |
266 | |
267 | let max_value = u8::try_from((1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT)).unwrap(); |
268 | self.offset_x = self.runs.add( |
269 | x >> SHIFT, |
270 | coverage_to_partial_alpha(fb), |
271 | n as usize, |
272 | coverage_to_partial_alpha(fe), |
273 | max_value, |
274 | self.offset_x, |
275 | ); |
276 | } |
277 | } |
278 | |
279 | // coverage_to_partial_alpha() is being used by AlphaRuns, which |
280 | // *accumulates* SCALE pixels worth of "alpha" in [0,(256/SCALE)] |
281 | // to produce a final value in [0, 255] and handles clamping 256->255 |
282 | // itself, with the same (alpha - (alpha >> 8)) correction as |
283 | // coverage_to_exact_alpha(). |
284 | fn coverage_to_partial_alpha(mut aa: u32) -> AlphaU8 { |
285 | aa <<= 8 - 2 * SHIFT; |
286 | aa as AlphaU8 |
287 | } |
288 | |