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
7use core::convert::TryFrom;
8
9use crate::{FillRule, IntRect, LengthU32, Path, Rect};
10
11use crate::alpha_runs::AlphaRuns;
12use crate::blitter::Blitter;
13use crate::color::AlphaU8;
14use crate::geom::{IntRectExt, ScreenIntRect};
15use crate::math::left_shift;
16
17#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
18use tiny_skia_path::NoStdFloat;
19
20/// controls how much we super-sample (when we use that scan conversion)
21const SUPERSAMPLE_SHIFT: u32 = 2;
22
23const SHIFT: u32 = SUPERSAMPLE_SHIFT;
24const SCALE: u32 = 1 << SHIFT;
25const MASK: u32 = SCALE - 1;
26
27pub 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?
78fn 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
92fn overflows_short_shift(value: i32, shift: i32) -> i32 {
93 let s: i32 = 16 + shift;
94 (left_shift(value, shift:s) >> s) - value
95}
96
97fn 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
132struct 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
150impl<'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
171struct SuperBlitter<'a> {
172 base: BaseSuperBlitter<'a>,
173 runs: AlphaRuns,
174 offset_x: usize,
175}
176
177impl<'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
212impl Drop for SuperBlitter<'_> {
213 fn drop(&mut self) {
214 self.flush();
215 }
216}
217
218impl 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().
284fn coverage_to_partial_alpha(mut aa: u32) -> AlphaU8 {
285 aa <<= 8 - 2 * SHIFT;
286 aa as AlphaU8
287}
288