1 | // Copyright 2011 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 | use core::num::NonZeroU16; |
9 | |
10 | use crate::{IntRect, LengthU32, LineCap, Path, Point, Rect}; |
11 | |
12 | use crate::alpha_runs::{AlphaRun, AlphaRuns}; |
13 | use crate::blitter::Blitter; |
14 | use crate::color::AlphaU8; |
15 | use crate::fixed_point::{fdot16, fdot6, fdot8, FDot16, FDot6, FDot8}; |
16 | use crate::geom::{IntRectExt, ScreenIntRect}; |
17 | use crate::line_clipper; |
18 | use crate::math::LENGTH_U32_ONE; |
19 | |
20 | #[derive (Copy, Clone, Debug)] |
21 | struct FixedRect { |
22 | left: FDot16, |
23 | top: FDot16, |
24 | right: FDot16, |
25 | bottom: FDot16, |
26 | } |
27 | |
28 | impl FixedRect { |
29 | fn from_rect(src: &Rect) -> Self { |
30 | FixedRect { |
31 | left: fdot16::from_f32(src.left()), |
32 | top: fdot16::from_f32(src.top()), |
33 | right: fdot16::from_f32(src.right()), |
34 | bottom: fdot16::from_f32(src.bottom()), |
35 | } |
36 | } |
37 | } |
38 | |
39 | /// Multiplies value by 0..256, and shift the result down 8 |
40 | /// (i.e. return (value * alpha256) >> 8) |
41 | fn alpha_mul(value: AlphaU8, alpha256: i32) -> u8 { |
42 | let a: i32 = (i32::from(value) * alpha256) >> 8; |
43 | debug_assert!(a >= 0 && a <= 255); |
44 | a as u8 |
45 | } |
46 | |
47 | pub fn fill_rect(rect: &Rect, clip: &ScreenIntRect, blitter: &mut dyn Blitter) { |
48 | let rect: Rect = match rect.intersect(&clip.to_rect()) { |
49 | Some(v: Rect) => v, |
50 | None => return, // everything was clipped out |
51 | }; |
52 | |
53 | let fr: FixedRect = FixedRect::from_rect(&rect); |
54 | fill_fixed_rect(&fr, blitter); |
55 | } |
56 | |
57 | fn fill_fixed_rect(rect: &FixedRect, blitter: &mut dyn Blitter) { |
58 | fill_dot8( |
59 | l:fdot8::from_fdot16(rect.left), |
60 | t:fdot8::from_fdot16(rect.top), |
61 | r:fdot8::from_fdot16(rect.right), |
62 | b:fdot8::from_fdot16(rect.bottom), |
63 | fill_inner:true, |
64 | blitter, |
65 | ) |
66 | } |
67 | |
68 | fn fill_dot8(l: FDot8, t: FDot8, r: FDot8, b: FDot8, fill_inner: bool, blitter: &mut dyn Blitter) { |
69 | fn to_alpha(a: i32) -> u8 { |
70 | debug_assert!(a >= 0 && a <= 255); |
71 | a as u8 |
72 | } |
73 | |
74 | // check for empty now that we're in our reduced precision space |
75 | if l >= r || t >= b { |
76 | return; |
77 | } |
78 | |
79 | let mut top = t >> 8; |
80 | if top == ((b - 1) >> 8) { |
81 | // just one scanline high |
82 | do_scanline(l, top, r, to_alpha(b - t - 1), blitter); |
83 | return; |
84 | } |
85 | |
86 | if t & 0xFF != 0 { |
87 | do_scanline(l, top, r, to_alpha(256 - (t & 0xFF)), blitter); |
88 | top += 1; |
89 | } |
90 | |
91 | let bottom = b >> 8; |
92 | let height = bottom - top; |
93 | if let Some(height) = u32::try_from(height).ok().and_then(LengthU32::new) { |
94 | let mut left = l >> 8; |
95 | if left == ((r - 1) >> 8) { |
96 | // just 1-pixel wide |
97 | if let (Ok(left), Ok(top)) = (u32::try_from(left), u32::try_from(top)) { |
98 | blitter.blit_v(left, top, height, to_alpha(r - l - 1)); |
99 | } else { |
100 | debug_assert!(false); |
101 | } |
102 | } else { |
103 | if l & 0xFF != 0 { |
104 | if let (Ok(left), Ok(top)) = (u32::try_from(left), u32::try_from(top)) { |
105 | blitter.blit_v(left, top, height, to_alpha(256 - (l & 0xFF))); |
106 | } else { |
107 | debug_assert!(false); |
108 | } |
109 | |
110 | left += 1; |
111 | } |
112 | |
113 | let right = r >> 8; |
114 | let width = right - left; |
115 | if fill_inner { |
116 | if let Some(width) = u32::try_from(width).ok().and_then(LengthU32::new) { |
117 | if let (Ok(left), Ok(top)) = (u32::try_from(left), u32::try_from(top)) { |
118 | let rect = ScreenIntRect::from_xywh_safe(left, top, width, height); |
119 | blitter.blit_rect(&rect); |
120 | } else { |
121 | debug_assert!(false); |
122 | } |
123 | } else { |
124 | debug_assert!(false); |
125 | } |
126 | } |
127 | |
128 | if r & 0xFF != 0 { |
129 | if let (Ok(right), Ok(top)) = (u32::try_from(right), u32::try_from(top)) { |
130 | blitter.blit_v(right, top, height, to_alpha(r & 0xFF)); |
131 | } else { |
132 | debug_assert!(false); |
133 | } |
134 | } |
135 | } |
136 | } |
137 | |
138 | if b & 0xFF != 0 { |
139 | do_scanline(l, bottom, r, to_alpha(b & 0xFF), blitter); |
140 | } |
141 | } |
142 | |
143 | fn do_scanline(l: FDot8, top: i32, r: FDot8, alpha: AlphaU8, blitter: &mut dyn Blitter) { |
144 | debug_assert!(l < r); |
145 | |
146 | let one_len = LENGTH_U32_ONE; |
147 | let top = match u32::try_from(top) { |
148 | Ok(n) => n, |
149 | _ => return, |
150 | }; |
151 | |
152 | if (l >> 8) == ((r - 1) >> 8) { |
153 | // 1x1 pixel |
154 | if let Ok(left) = u32::try_from(l >> 8) { |
155 | blitter.blit_v(left, top, one_len, alpha_mul(alpha, r - l)); |
156 | } |
157 | |
158 | return; |
159 | } |
160 | |
161 | let mut left = l >> 8; |
162 | |
163 | if l & 0xFF != 0 { |
164 | if let Ok(left) = u32::try_from(l >> 8) { |
165 | blitter.blit_v(left, top, one_len, alpha_mul(alpha, 256 - (l & 0xFF))); |
166 | } |
167 | |
168 | left += 1; |
169 | } |
170 | |
171 | let right = r >> 8; |
172 | let width = right - left; |
173 | if let Some(width) = u32::try_from(width).ok().and_then(LengthU32::new) { |
174 | if let Ok(left) = u32::try_from(left) { |
175 | call_hline_blitter(left, Some(top), width, alpha, blitter); |
176 | } |
177 | } |
178 | |
179 | if r & 0xFF != 0 { |
180 | if let Ok(right) = u32::try_from(right) { |
181 | blitter.blit_v(right, top, one_len, alpha_mul(alpha, r & 0xFF)); |
182 | } |
183 | } |
184 | } |
185 | |
186 | fn call_hline_blitter( |
187 | mut x: u32, |
188 | y: Option<u32>, |
189 | count: LengthU32, |
190 | alpha: AlphaU8, |
191 | blitter: &mut dyn Blitter, |
192 | ) { |
193 | const HLINE_STACK_BUFFER: usize = 100; |
194 | |
195 | let mut runs = [None; HLINE_STACK_BUFFER + 1]; |
196 | let mut aa = [0u8; HLINE_STACK_BUFFER]; |
197 | |
198 | let mut count = count.get(); |
199 | loop { |
200 | // In theory, we should be able to just do this once (outside of the loop), |
201 | // since aa[] and runs[] are supposed" to be const when we call the blitter. |
202 | // In reality, some wrapper-blitters (e.g. RgnClipBlitter) cast away that |
203 | // constness, and modify the buffers in-place. Hence the need to be defensive |
204 | // here and reseed the aa value. |
205 | aa[0] = alpha; |
206 | |
207 | let mut n = count; |
208 | if n > HLINE_STACK_BUFFER as u32 { |
209 | n = HLINE_STACK_BUFFER as u32; |
210 | } |
211 | |
212 | debug_assert!(n <= u16::MAX as u32); |
213 | runs[0] = NonZeroU16::new(n as u16); |
214 | runs[n as usize] = None; |
215 | if let Some(y) = y { |
216 | blitter.blit_anti_h(x, y, &mut aa, &mut runs); |
217 | } |
218 | x += n; |
219 | |
220 | if n >= count || count == 0 { |
221 | break; |
222 | } |
223 | |
224 | count -= n; |
225 | } |
226 | } |
227 | |
228 | pub fn stroke_path( |
229 | path: &Path, |
230 | line_cap: LineCap, |
231 | clip: &ScreenIntRect, |
232 | blitter: &mut dyn Blitter, |
233 | ) { |
234 | super::hairline::stroke_path_impl(path, line_cap, clip, line_proc:anti_hair_line_rgn, blitter); |
235 | } |
236 | |
237 | fn anti_hair_line_rgn(points: &[Point], clip: Option<&ScreenIntRect>, blitter: &mut dyn Blitter) { |
238 | let max = 32767.0; |
239 | let fixed_bounds = Rect::from_ltrb(-max, -max, max, max).unwrap(); |
240 | |
241 | let clip_bounds = if let Some(clip) = clip { |
242 | // We perform integral clipping later on, but we do a scalar clip first |
243 | // to ensure that our coordinates are expressible in fixed/integers. |
244 | // |
245 | // antialiased hairlines can draw up to 1/2 of a pixel outside of |
246 | // their bounds, so we need to outset the clip before calling the |
247 | // clipper. To make the numerics safer, we outset by a whole pixel, |
248 | // since the 1/2 pixel boundary is important to the antihair blitter, |
249 | // we don't want to risk numerical fate by chopping on that edge. |
250 | clip.to_rect().outset(1.0, 1.0) |
251 | } else { |
252 | None |
253 | }; |
254 | |
255 | for i in 0..points.len() - 1 { |
256 | let mut pts = [Point::zero(); 2]; |
257 | |
258 | // We have to pre-clip the line to fit in a Fixed, so we just chop the line. |
259 | if !line_clipper::intersect(&[points[i], points[i + 1]], &fixed_bounds, &mut pts) { |
260 | continue; |
261 | } |
262 | |
263 | if let Some(clip_bounds) = clip_bounds { |
264 | let tmp = pts; |
265 | if !line_clipper::intersect(&tmp, &clip_bounds, &mut pts) { |
266 | continue; |
267 | } |
268 | } |
269 | |
270 | let x0 = fdot6::from_f32(pts[0].x); |
271 | let y0 = fdot6::from_f32(pts[0].y); |
272 | let x1 = fdot6::from_f32(pts[1].x); |
273 | let y1 = fdot6::from_f32(pts[1].y); |
274 | |
275 | if let Some(clip) = clip { |
276 | let left = x0.min(x1); |
277 | let top = y0.min(y1); |
278 | let right = x0.max(x1); |
279 | let bottom = y0.max(y1); |
280 | |
281 | let ir = IntRect::from_ltrb( |
282 | fdot6::floor(left) - 1, |
283 | fdot6::floor(top) - 1, |
284 | fdot6::ceil(right) + 1, |
285 | fdot6::ceil(bottom) + 1, |
286 | ); |
287 | let ir = match ir { |
288 | Some(v) => v, |
289 | None => return, |
290 | }; |
291 | |
292 | if clip.to_int_rect().intersect(&ir).is_none() { |
293 | continue; |
294 | } |
295 | |
296 | if !clip.to_int_rect().contains(&ir) { |
297 | let subclip = ir |
298 | .intersect(&clip.to_int_rect()) |
299 | .and_then(|r| r.to_screen_int_rect()); |
300 | |
301 | if let Some(subclip) = subclip { |
302 | do_anti_hairline(x0, y0, x1, y1, Some(subclip), blitter); |
303 | } |
304 | |
305 | continue; |
306 | } |
307 | |
308 | // fall through to no-clip case |
309 | } |
310 | |
311 | do_anti_hairline(x0, y0, x1, y1, None, blitter); |
312 | } |
313 | } |
314 | |
315 | #[derive (Copy, Clone, Debug)] |
316 | enum BlitterKind { |
317 | HLine, |
318 | Horish, |
319 | VLine, |
320 | Vertish, |
321 | } |
322 | |
323 | fn do_anti_hairline( |
324 | mut x0: FDot6, |
325 | mut y0: FDot6, |
326 | mut x1: FDot6, |
327 | mut y1: FDot6, |
328 | mut clip_opt: Option<ScreenIntRect>, |
329 | blitter: &mut dyn Blitter, |
330 | ) { |
331 | // check for integer NaN (0x80000000) which we can't handle (can't negate it) |
332 | // It appears typically from a huge float (inf or nan) being converted to int. |
333 | // If we see it, just don't draw. |
334 | if any_bad_ints(x0, y0, x1, y1) != 0 { |
335 | return; |
336 | } |
337 | |
338 | // The caller must clip the line to [-32767.0 ... 32767.0] ahead of time (in dot6 format) |
339 | debug_assert!(fdot6::can_convert_to_fdot16(x0)); |
340 | debug_assert!(fdot6::can_convert_to_fdot16(y0)); |
341 | debug_assert!(fdot6::can_convert_to_fdot16(x1)); |
342 | debug_assert!(fdot6::can_convert_to_fdot16(y1)); |
343 | |
344 | if (x1 - x0).abs() > fdot6::from_i32(511) || (y1 - y0).abs() > fdot6::from_i32(511) { |
345 | // instead of (x0 + x1) >> 1, we shift each separately. This is less |
346 | // precise, but avoids overflowing the intermediate result if the |
347 | // values are huge. A better fix might be to clip the original pts |
348 | // directly (i.e. do the divide), so we don't spend time subdividing |
349 | // huge lines at all. |
350 | let hx = (x0 >> 1) + (x1 >> 1); |
351 | let hy = (y0 >> 1) + (y1 >> 1); |
352 | do_anti_hairline(x0, y0, hx, hy, clip_opt, blitter); |
353 | do_anti_hairline(hx, hy, x1, y1, clip_opt, blitter); |
354 | return; // we're done |
355 | } |
356 | |
357 | let mut scale_start; |
358 | let mut scale_stop; |
359 | let mut istart; |
360 | let mut istop; |
361 | let mut fstart; |
362 | let slope; |
363 | let blitter_kind; |
364 | |
365 | if (x1 - x0).abs() > (y1 - y0).abs() { |
366 | // mostly horizontal |
367 | |
368 | if x0 > x1 { |
369 | // we want to go left-to-right |
370 | core::mem::swap(&mut x0, &mut x1); |
371 | core::mem::swap(&mut y0, &mut y1); |
372 | } |
373 | |
374 | istart = fdot6::floor(x0); |
375 | istop = fdot6::ceil(x1); |
376 | fstart = fdot6::to_fdot16(y0); |
377 | if y0 == y1 { |
378 | // completely horizontal, take fast case |
379 | slope = 0; |
380 | blitter_kind = Some(BlitterKind::HLine); |
381 | } else { |
382 | slope = fdot16::fast_div(y1 - y0, x1 - x0); |
383 | debug_assert!(slope >= -fdot16::ONE && slope <= fdot16::ONE); |
384 | fstart += (slope * (32 - (x0 & 63)) + 32) >> 6; |
385 | blitter_kind = Some(BlitterKind::Horish); |
386 | } |
387 | |
388 | debug_assert!(istop > istart); |
389 | if istop - istart == 1 { |
390 | // we are within a single pixel |
391 | scale_start = x1 - x0; |
392 | debug_assert!(scale_start >= 0 && scale_start <= 64); |
393 | scale_stop = 0; |
394 | } else { |
395 | scale_start = 64 - (x0 & 63); |
396 | scale_stop = x1 & 63; |
397 | } |
398 | |
399 | if let Some(clip) = clip_opt { |
400 | let clip = clip.to_int_rect(); |
401 | |
402 | if istart >= clip.right() || istop <= clip.left() { |
403 | return; // we're done |
404 | } |
405 | |
406 | if istart < clip.left() { |
407 | fstart += slope * (clip.left() - istart); |
408 | istart = clip.left(); |
409 | scale_start = 64; |
410 | if istop - istart == 1 { |
411 | // we are within a single pixel |
412 | scale_start = contribution_64(x1); |
413 | scale_stop = 0; |
414 | } |
415 | } |
416 | |
417 | if istop > clip.right() { |
418 | istop = clip.right(); |
419 | scale_stop = 0; // so we don't draw this last column |
420 | } |
421 | |
422 | debug_assert!(istart <= istop); |
423 | if istart == istop { |
424 | return; // we're done |
425 | } |
426 | |
427 | // now test if our Y values are completely inside the clip |
428 | let (mut top, mut bottom) = if slope >= 0 { |
429 | // T2B |
430 | let top = fdot16::floor_to_i32(fstart - fdot16::HALF); |
431 | let bottom = |
432 | fdot16::ceil_to_i32(fstart + (istop - istart - 1) * slope + fdot16::HALF); |
433 | (top, bottom) |
434 | } else { |
435 | // B2T |
436 | let bottom = fdot16::ceil_to_i32(fstart + fdot16::HALF); |
437 | let top = |
438 | fdot16::floor_to_i32(fstart + (istop - istart - 1) * slope - fdot16::HALF); |
439 | (top, bottom) |
440 | }; |
441 | |
442 | top -= 1; |
443 | bottom += 1; |
444 | |
445 | if top >= clip.bottom() || bottom <= clip.top() { |
446 | return; // we're done |
447 | } |
448 | |
449 | if clip.top() <= top && clip.bottom() >= bottom { |
450 | clip_opt = None; |
451 | } |
452 | } |
453 | } else { |
454 | // mostly vertical |
455 | |
456 | if y0 > y1 { |
457 | // we want to go top-to-bottom |
458 | core::mem::swap(&mut x0, &mut x1); |
459 | core::mem::swap(&mut y0, &mut y1); |
460 | } |
461 | |
462 | istart = fdot6::floor(y0); |
463 | istop = fdot6::ceil(y1); |
464 | fstart = fdot6::to_fdot16(x0); |
465 | if x0 == x1 { |
466 | if y0 == y1 { |
467 | // are we zero length? nothing to do |
468 | return; // we're done |
469 | } |
470 | |
471 | slope = 0; |
472 | blitter_kind = Some(BlitterKind::VLine); |
473 | } else { |
474 | slope = fdot16::fast_div(x1 - x0, y1 - y0); |
475 | debug_assert!(slope <= fdot16::ONE && slope >= -fdot16::ONE); |
476 | fstart += (slope * (32 - (y0 & 63)) + 32) >> 6; |
477 | blitter_kind = Some(BlitterKind::Vertish); |
478 | } |
479 | |
480 | debug_assert!(istop > istart); |
481 | if istop - istart == 1 { |
482 | // we are within a single pixel |
483 | scale_start = y1 - y0; |
484 | debug_assert!(scale_start >= 0 && scale_start <= 64); |
485 | scale_stop = 0; |
486 | } else { |
487 | scale_start = 64 - (y0 & 63); |
488 | scale_stop = y1 & 63; |
489 | } |
490 | |
491 | if let Some(clip) = clip_opt { |
492 | let clip = clip.to_int_rect(); |
493 | |
494 | if istart >= clip.bottom() || istop <= clip.top() { |
495 | return; // we're done |
496 | } |
497 | |
498 | if istart < clip.top() { |
499 | fstart += slope * (clip.top() - istart); |
500 | istart = clip.top(); |
501 | scale_start = 64; |
502 | if istop - istart == 1 { |
503 | // we are within a single pixel |
504 | scale_start = contribution_64(y1); |
505 | scale_stop = 0; |
506 | } |
507 | } |
508 | if istop > clip.bottom() { |
509 | istop = clip.bottom(); |
510 | scale_stop = 0; // so we don't draw this last row |
511 | } |
512 | |
513 | debug_assert!(istart <= istop); |
514 | if istart == istop { |
515 | return; // we're done |
516 | } |
517 | |
518 | // now test if our X values are completely inside the clip |
519 | let (mut left, mut right) = if slope >= 0 { |
520 | // L2R |
521 | let left = fdot16::floor_to_i32(fstart - fdot16::HALF); |
522 | let right = |
523 | fdot16::ceil_to_i32(fstart + (istop - istart - 1) * slope + fdot16::HALF); |
524 | (left, right) |
525 | } else { |
526 | // R2L |
527 | let right = fdot16::ceil_to_i32(fstart + fdot16::HALF); |
528 | let left = |
529 | fdot16::floor_to_i32(fstart + (istop - istart - 1) * slope - fdot16::HALF); |
530 | (left, right) |
531 | }; |
532 | |
533 | left -= 1; |
534 | right += 1; |
535 | |
536 | if left >= clip.right() || right <= clip.left() { |
537 | return; // we're done |
538 | } |
539 | |
540 | if clip.left() <= left && clip.right() >= right { |
541 | clip_opt = None; |
542 | } |
543 | } |
544 | } |
545 | |
546 | let mut clip_blitter; |
547 | let blitter = if let Some(clip) = clip_opt { |
548 | clip_blitter = RectClipBlitter { blitter, clip }; |
549 | &mut clip_blitter |
550 | } else { |
551 | blitter |
552 | }; |
553 | |
554 | let blitter_kind = match blitter_kind { |
555 | Some(v) => v, |
556 | None => return, |
557 | }; |
558 | |
559 | // A bit ugly, but looks like this is the only way to have stack allocated object trait. |
560 | let mut hline_blitter; |
561 | let mut horish_blitter; |
562 | let mut vline_blitter; |
563 | let mut vertish_blitter; |
564 | let hair_blitter: &mut dyn AntiHairBlitter = match blitter_kind { |
565 | BlitterKind::HLine => { |
566 | hline_blitter = HLineAntiHairBlitter(blitter); |
567 | &mut hline_blitter |
568 | } |
569 | BlitterKind::Horish => { |
570 | horish_blitter = HorishAntiHairBlitter(blitter); |
571 | &mut horish_blitter |
572 | } |
573 | BlitterKind::VLine => { |
574 | vline_blitter = VLineAntiHairBlitter(blitter); |
575 | &mut vline_blitter |
576 | } |
577 | BlitterKind::Vertish => { |
578 | vertish_blitter = VertishAntiHairBlitter(blitter); |
579 | &mut vertish_blitter |
580 | } |
581 | }; |
582 | |
583 | debug_assert!(istart >= 0); |
584 | let mut istart = istart as u32; |
585 | |
586 | debug_assert!(istop >= 0); |
587 | let istop = istop as u32; |
588 | |
589 | fstart = hair_blitter.draw_cap(istart, fstart, slope, scale_start); |
590 | istart += 1; |
591 | let full_spans = istop - istart - (scale_stop > 0) as u32; |
592 | if full_spans > 0 { |
593 | fstart = hair_blitter.draw_line(istart, istart + full_spans, fstart, slope); |
594 | } |
595 | |
596 | if scale_stop > 0 { |
597 | hair_blitter.draw_cap(istop - 1, fstart, slope, scale_stop); |
598 | } |
599 | } |
600 | |
601 | // returns high-bit set if x == 0x8000 |
602 | fn bad_int(x: i32) -> i32 { |
603 | x & -x |
604 | } |
605 | |
606 | fn any_bad_ints(a: i32, b: i32, c: i32, d: i32) -> i32 { |
607 | (bad_int(a) | bad_int(b) | bad_int(c) | bad_int(d)) >> ((core::mem::size_of::<i32>() << 3) - 1) |
608 | } |
609 | |
610 | // We want the fractional part of ordinate, but we want multiples of 64 to |
611 | // return 64, not 0, so we can't just say (ordinate & 63). |
612 | // We basically want to compute those bits, and if they're 0, return 64. |
613 | // We can do that w/o a branch with an extra sub and add. |
614 | fn contribution_64(ordinate: FDot6) -> i32 { |
615 | let result: i32 = ((ordinate - 1) & 63) + 1; |
616 | debug_assert!(result > 0 && result <= 64); |
617 | result |
618 | } |
619 | |
620 | trait AntiHairBlitter { |
621 | fn draw_cap(&mut self, x: u32, fy: FDot16, slope: FDot16, mod64: i32) -> FDot16; |
622 | fn draw_line(&mut self, x: u32, stopx: u32, fy: FDot16, slope: FDot16) -> FDot16; |
623 | } |
624 | |
625 | struct HLineAntiHairBlitter<'a>(&'a mut dyn Blitter); |
626 | |
627 | impl AntiHairBlitter for HLineAntiHairBlitter<'_> { |
628 | fn draw_cap(&mut self, x: u32, mut fy: FDot16, _: FDot16, mod64: i32) -> FDot16 { |
629 | fy += fdot16::ONE / 2; |
630 | fy = fy.max(0); |
631 | |
632 | let y = (fy >> 16) as u32; |
633 | let a = i32_to_alpha(fy >> 8); |
634 | |
635 | // lower line |
636 | let mut ma = fdot6::small_scale(a, mod64); |
637 | if ma != 0 { |
638 | call_hline_blitter(x, Some(y), LENGTH_U32_ONE, ma, self.0); |
639 | } |
640 | |
641 | // upper line |
642 | ma = fdot6::small_scale(255 - a, mod64); |
643 | if ma != 0 { |
644 | call_hline_blitter(x, y.checked_sub(1), LENGTH_U32_ONE, ma, self.0); |
645 | } |
646 | |
647 | fy - fdot16::ONE / 2 |
648 | } |
649 | |
650 | fn draw_line(&mut self, x: u32, stop_x: u32, mut fy: FDot16, _: FDot16) -> FDot16 { |
651 | let count = match LengthU32::new(stop_x - x) { |
652 | Some(n) => n, |
653 | None => return fy, |
654 | }; |
655 | |
656 | fy += fdot16::ONE / 2; |
657 | fy = fy.max(0); |
658 | |
659 | let y = (fy >> 16) as u32; |
660 | let mut a = i32_to_alpha(fy >> 8); |
661 | |
662 | // lower line |
663 | if a != 0 { |
664 | call_hline_blitter(x, Some(y), count, a, self.0); |
665 | } |
666 | |
667 | // upper line |
668 | a = 255 - a; |
669 | if a != 0 { |
670 | call_hline_blitter(x, y.checked_sub(1), count, a, self.0); |
671 | } |
672 | |
673 | fy - fdot16::ONE / 2 |
674 | } |
675 | } |
676 | |
677 | struct HorishAntiHairBlitter<'a>(&'a mut dyn Blitter); |
678 | |
679 | impl AntiHairBlitter for HorishAntiHairBlitter<'_> { |
680 | fn draw_cap(&mut self, x: u32, mut fy: FDot16, dy: FDot16, mod64: i32) -> FDot16 { |
681 | fy += fdot16::ONE / 2; |
682 | fy = fy.max(0); |
683 | |
684 | let lower_y = (fy >> 16) as u32; |
685 | let a = i32_to_alpha(fy >> 8); |
686 | let a0 = fdot6::small_scale(255 - a, mod64); |
687 | let a1 = fdot6::small_scale(a, mod64); |
688 | self.0.blit_anti_v2(x, lower_y.max(1) - 1, a0, a1); |
689 | |
690 | fy + dy - fdot16::ONE / 2 |
691 | } |
692 | |
693 | fn draw_line(&mut self, mut x: u32, stop_x: u32, mut fy: FDot16, dy: FDot16) -> FDot16 { |
694 | debug_assert!(x < stop_x); |
695 | |
696 | fy += fdot16::ONE / 2; |
697 | loop { |
698 | fy = fy.max(0); |
699 | let lower_y = (fy >> 16) as u32; |
700 | let a = i32_to_alpha(fy >> 8); |
701 | self.0.blit_anti_v2(x, lower_y.max(1) - 1, 255 - a, a); |
702 | fy += dy; |
703 | |
704 | x += 1; |
705 | if x >= stop_x { |
706 | break; |
707 | } |
708 | } |
709 | |
710 | fy - fdot16::ONE / 2 |
711 | } |
712 | } |
713 | |
714 | struct VLineAntiHairBlitter<'a>(&'a mut dyn Blitter); |
715 | |
716 | impl AntiHairBlitter for VLineAntiHairBlitter<'_> { |
717 | fn draw_cap(&mut self, y: u32, mut fx: FDot16, dx: FDot16, mod64: i32) -> FDot16 { |
718 | debug_assert!(dx == 0); |
719 | fx += fdot16::ONE / 2; |
720 | fx = fx.max(0); |
721 | |
722 | let x = (fx >> 16) as u32; |
723 | let a = i32_to_alpha(fx >> 8); |
724 | |
725 | let mut ma = fdot6::small_scale(a, mod64); |
726 | if ma != 0 { |
727 | self.0.blit_v(x, y, LENGTH_U32_ONE, ma); |
728 | } |
729 | |
730 | ma = fdot6::small_scale(255 - a, mod64); |
731 | if ma != 0 { |
732 | self.0.blit_v(x.max(1) - 1, y, LENGTH_U32_ONE, ma); |
733 | } |
734 | |
735 | fx - fdot16::ONE / 2 |
736 | } |
737 | |
738 | fn draw_line(&mut self, y: u32, stop_y: u32, mut fx: FDot16, dx: FDot16) -> FDot16 { |
739 | debug_assert!(dx == 0); |
740 | let height = match LengthU32::new(stop_y - y) { |
741 | Some(n) => n, |
742 | None => return fx, |
743 | }; |
744 | |
745 | fx += fdot16::ONE / 2; |
746 | fx = fx.max(0); |
747 | |
748 | let x = (fx >> 16) as u32; |
749 | let mut a = i32_to_alpha(fx >> 8); |
750 | |
751 | if a != 0 { |
752 | self.0.blit_v(x, y, height, a); |
753 | } |
754 | |
755 | a = 255 - a; |
756 | if a != 0 { |
757 | self.0.blit_v(x.max(1) - 1, y, height, a); |
758 | } |
759 | |
760 | fx - fdot16::ONE / 2 |
761 | } |
762 | } |
763 | |
764 | struct VertishAntiHairBlitter<'a>(&'a mut dyn Blitter); |
765 | |
766 | impl AntiHairBlitter for VertishAntiHairBlitter<'_> { |
767 | fn draw_cap(&mut self, y: u32, mut fx: FDot16, dx: FDot16, mod64: i32) -> FDot16 { |
768 | fx += fdot16::ONE / 2; |
769 | fx = fx.max(0); |
770 | |
771 | let x = (fx >> 16) as u32; |
772 | let a = i32_to_alpha(fx >> 8); |
773 | self.0.blit_anti_h2( |
774 | x.max(1) - 1, |
775 | y, |
776 | fdot6::small_scale(255 - a, mod64), |
777 | fdot6::small_scale(a, mod64), |
778 | ); |
779 | |
780 | fx + dx - fdot16::ONE / 2 |
781 | } |
782 | |
783 | fn draw_line(&mut self, mut y: u32, stop_y: u32, mut fx: FDot16, dx: FDot16) -> FDot16 { |
784 | debug_assert!(y < stop_y); |
785 | |
786 | fx += fdot16::ONE / 2; |
787 | loop { |
788 | fx = fx.max(0); |
789 | let x = (fx >> 16) as u32; |
790 | let a = i32_to_alpha(fx >> 8); |
791 | self.0.blit_anti_h2(x.max(1) - 1, y, 255 - a, a); |
792 | fx += dx; |
793 | |
794 | y += 1; |
795 | if y >= stop_y { |
796 | break; |
797 | } |
798 | } |
799 | |
800 | fx - fdot16::ONE / 2 |
801 | } |
802 | } |
803 | |
804 | fn i32_to_alpha(a: i32) -> u8 { |
805 | (a & 0xFF) as u8 |
806 | } |
807 | |
808 | struct RectClipBlitter<'a> { |
809 | blitter: &'a mut dyn Blitter, |
810 | clip: ScreenIntRect, |
811 | } |
812 | |
813 | impl Blitter for RectClipBlitter<'_> { |
814 | fn blit_anti_h( |
815 | &mut self, |
816 | x: u32, |
817 | y: u32, |
818 | mut antialias: &mut [AlphaU8], |
819 | mut runs: &mut [AlphaRun], |
820 | ) { |
821 | fn y_in_rect(y: u32, rect: ScreenIntRect) -> bool { |
822 | (y - rect.top()) < rect.height() |
823 | } |
824 | |
825 | if !y_in_rect(y, self.clip) || x >= self.clip.right() { |
826 | return; |
827 | } |
828 | |
829 | let mut x0 = x; |
830 | let mut x1 = x + compute_anti_width(runs); |
831 | |
832 | if x1 <= self.clip.left() { |
833 | return; |
834 | } |
835 | |
836 | debug_assert!(x0 < x1); |
837 | if x0 < self.clip.left() { |
838 | let dx = self.clip.left() - x0; |
839 | AlphaRuns::break_at(antialias, runs, dx as i32); |
840 | antialias = &mut antialias[dx as usize..]; |
841 | runs = &mut runs[dx as usize..]; |
842 | x0 = self.clip.left(); |
843 | } |
844 | |
845 | debug_assert!(x0 < x1 && runs[(x1 - x0) as usize].is_none()); |
846 | if x1 > self.clip.right() { |
847 | x1 = self.clip.right(); |
848 | AlphaRuns::break_at(antialias, runs, (x1 - x0) as i32); |
849 | runs[(x1 - x0) as usize] = None; |
850 | } |
851 | |
852 | debug_assert!(x0 < x1 && runs[(x1 - x0) as usize].is_none()); |
853 | debug_assert!(compute_anti_width(runs) == x1 - x0); |
854 | |
855 | self.blitter.blit_anti_h(x0, y, antialias, runs); |
856 | } |
857 | |
858 | fn blit_v(&mut self, x: u32, y: u32, height: LengthU32, alpha: AlphaU8) { |
859 | fn x_in_rect(x: u32, rect: ScreenIntRect) -> bool { |
860 | (x - rect.left()) < rect.width() |
861 | } |
862 | |
863 | if !x_in_rect(x, self.clip) { |
864 | return; |
865 | } |
866 | |
867 | let mut y0 = y; |
868 | let mut y1 = y + height.get(); |
869 | |
870 | if y0 < self.clip.top() { |
871 | y0 = self.clip.top(); |
872 | } |
873 | |
874 | if y1 > self.clip.bottom() { |
875 | y1 = self.clip.bottom(); |
876 | } |
877 | |
878 | if y0 < y1 { |
879 | if let Some(h) = LengthU32::new(y1 - y0) { |
880 | self.blitter.blit_v(x, y0, h, alpha); |
881 | } |
882 | } |
883 | } |
884 | |
885 | fn blit_anti_h2(&mut self, x: u32, y: u32, alpha0: AlphaU8, alpha1: AlphaU8) { |
886 | self.blit_anti_h( |
887 | x, |
888 | y, |
889 | &mut [alpha0, alpha1], |
890 | &mut [NonZeroU16::new(1), NonZeroU16::new(1), None], |
891 | ); |
892 | } |
893 | |
894 | fn blit_anti_v2(&mut self, x: u32, y: u32, alpha0: AlphaU8, alpha1: AlphaU8) { |
895 | self.blit_anti_h(x, y, &mut [alpha0], &mut [NonZeroU16::new(1), None]); |
896 | |
897 | self.blit_anti_h(x, y + 1, &mut [alpha1], &mut [NonZeroU16::new(1), None]); |
898 | } |
899 | } |
900 | |
901 | fn compute_anti_width(runs: &[AlphaRun]) -> u32 { |
902 | let mut i: usize = 0; |
903 | let mut width: u32 = 0; |
904 | while let Some(count: NonZero) = runs[i] { |
905 | width += u32::from(count.get()); |
906 | i += usize::from(count.get()); |
907 | } |
908 | |
909 | width |
910 | } |
911 | |