| 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 | |