Skip to main content

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