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
7use core::convert::TryFrom;
8use core::num::NonZeroU16;
9
10use crate::{IntRect, LengthU32, LineCap, Path, Point, Rect};
11
12use crate::alpha_runs::{AlphaRun, AlphaRuns};
13use crate::blitter::Blitter;
14use crate::color::AlphaU8;
15use crate::fixed_point::{fdot16, fdot6, fdot8, FDot16, FDot6, FDot8};
16use crate::geom::{IntRectExt, ScreenIntRect};
17use crate::line_clipper;
18use crate::math::LENGTH_U32_ONE;
19
20#[derive(Copy, Clone, Debug)]
21struct FixedRect {
22 left: FDot16,
23 top: FDot16,
24 right: FDot16,
25 bottom: FDot16,
26}
27
28impl 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)
41fn 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
47pub 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
57fn 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
68fn 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
143fn 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
186fn 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
228pub 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
237fn 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)]
316enum BlitterKind {
317 HLine,
318 Horish,
319 VLine,
320 Vertish,
321}
322
323fn 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
602fn bad_int(x: i32) -> i32 {
603 x & -x
604}
605
606fn 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.
614fn contribution_64(ordinate: FDot6) -> i32 {
615 let result: i32 = ((ordinate - 1) & 63) + 1;
616 debug_assert!(result > 0 && result <= 64);
617 result
618}
619
620trait 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
625struct HLineAntiHairBlitter<'a>(&'a mut dyn Blitter);
626
627impl 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
677struct HorishAntiHairBlitter<'a>(&'a mut dyn Blitter);
678
679impl 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
714struct VLineAntiHairBlitter<'a>(&'a mut dyn Blitter);
715
716impl 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
764struct VertishAntiHairBlitter<'a>(&'a mut dyn Blitter);
765
766impl 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
804fn i32_to_alpha(a: i32) -> u8 {
805 (a & 0xFF) as u8
806}
807
808struct RectClipBlitter<'a> {
809 blitter: &'a mut dyn Blitter,
810 clip: ScreenIntRect,
811}
812
813impl 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
901fn 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