tiny_skia/scan/
hairline_aa.rs

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::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 = match rect.intersect(&clip.to_rect()) {
49        Some(v) => v,
50        None => return, // everything was clipped out
51    };
52
53    let fr = 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        fdot8::from_fdot16(rect.left),
60        fdot8::from_fdot16(rect.top),
61        fdot8::from_fdot16(rect.right),
62        fdot8::from_fdot16(rect.bottom),
63        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, 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 = ((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 = 0;
903    let mut width = 0;
904    while let Some(count) = runs[i] {
905        width += u32::from(count.get());
906        i += usize::from(count.get());
907    }
908
909    width
910}