| 1 | use crate::color::{blend_over, lerp_premul, scale_premul, Color}; |
| 2 | use crate::geom::{pt, Point, Rect, Transform, IDENTITY}; |
| 3 | use crate::path::Path; |
| 4 | use crate::raster::Rasterizer; |
| 5 | use crate::simd; |
| 6 | |
| 7 | pub const TOL: f32 = 0.08; |
| 8 | |
| 9 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
| 10 | pub enum Cap { |
| 11 | Butt, |
| 12 | Round, |
| 13 | Square, |
| 14 | } |
| 15 | |
| 16 | #[derive(Clone, Debug)] |
| 17 | pub struct Stroke { |
| 18 | pub width: f32, |
| 19 | pub cap: Cap, |
| 20 | } |
| 21 | |
| 22 | impl Stroke { |
| 23 | pub fn new(width: f32) -> Stroke { |
| 24 | Stroke { width, cap: Cap::Butt } |
| 25 | } |
| 26 | pub fn round(width: f32) -> Stroke { |
| 27 | Stroke { width, cap: Cap::Round } |
| 28 | } |
| 29 | } |
| 30 | |
| 31 | #[derive(Clone, Debug)] |
| 32 | pub enum Paint { |
| 33 | Solid(Color), |
| 34 | Linear { p0: Point, p1: Point, stops: Vec<(f32, Color)> }, |
| 35 | Radial { c: Point, r: f32, stops: Vec<(f32, Color)> }, |
| 36 | } |
| 37 | |
| 38 | impl Paint { |
| 39 | pub fn solid(c: Color) -> Paint { |
| 40 | Paint::Solid(c) |
| 41 | } |
| 42 | pub fn vgrad(r: Rect, top: Color, bottom: Color) -> Paint { |
| 43 | Paint::Linear { p0: pt(r.x0, r.y0), p1: pt(r.x0, r.y1), stops: vec![(0.0, top), (1.0, bottom)] } |
| 44 | } |
| 45 | pub fn hgrad(r: Rect, left: Color, right: Color) -> Paint { |
| 46 | Paint::Linear { p0: pt(r.x0, r.y0), p1: pt(r.x1, r.y0), stops: vec![(0.0, left), (1.0, right)] } |
| 47 | } |
| 48 | pub fn dgrad(r: Rect, a: Color, b: Color) -> Paint { |
| 49 | Paint::Linear { p0: pt(r.x0, r.y0), p1: pt(r.x1, r.y1), stops: vec![(0.0, a), (1.0, b)] } |
| 50 | } |
| 51 | pub fn glow(c: Point, r: f32, inner: Color, outer: Color) -> Paint { |
| 52 | Paint::Radial { c, r, stops: vec![(0.0, inner), (1.0, outer)] } |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | fn build_lut(stops: &[(f32, Color)]) -> [u32; 256] { |
| 57 | let mut lut = [0u32; 256]; |
| 58 | if stops.is_empty() { |
| 59 | return lut; |
| 60 | } |
| 61 | for (i, slot) in lut.iter_mut().enumerate() { |
| 62 | let t = i as f32 / 255.0; |
| 63 | let mut col = stops[0].1; |
| 64 | if t >= stops[stops.len() - 1].0 { |
| 65 | col = stops[stops.len() - 1].1; |
| 66 | } else if t > stops[0].0 { |
| 67 | for w in stops.windows(2) { |
| 68 | let (t0, c0) = w[0]; |
| 69 | let (t1, c1) = w[1]; |
| 70 | if t >= t0 && t <= t1 { |
| 71 | col = c0.lerp(c1, (t - t0) / (t1 - t0).max(1e-6)); |
| 72 | break; |
| 73 | } |
| 74 | } |
| 75 | } |
| 76 | *slot = col.premul(); |
| 77 | } |
| 78 | lut |
| 79 | } |
| 80 | |
| 81 | enum Shader { |
| 82 | Solid(u32), |
| 83 | Linear { ox: f32, oy: f32, dx: f32, dy: f32, lut: Box<[u32; 256]>, opaque: bool }, |
| 84 | Radial { cx: f32, cy: f32, inv_r: f32, lut: Box<[u32; 256]>, opaque: bool }, |
| 85 | } |
| 86 | |
| 87 | impl Shader { |
| 88 | fn build(paint: &Paint, xf: &Transform) -> Shader { |
| 89 | match paint { |
| 90 | Paint::Solid(c) => Shader::Solid(c.premul()), |
| 91 | Paint::Linear { p0, p1, stops } => { |
| 92 | let a = xf.apply(*p0); |
| 93 | let b = xf.apply(*p1); |
| 94 | let d = b - a; |
| 95 | let len2 = d.len_sq().max(1e-6); |
| 96 | let lut = build_lut(stops); |
| 97 | let opaque = lut.iter().all(|v| v >> 24 == 255); |
| 98 | Shader::Linear { ox: a.x, oy: a.y, dx: d.x / len2, dy: d.y / len2, lut: Box::new(lut), opaque } |
| 99 | } |
| 100 | Paint::Radial { c, r, stops } => { |
| 101 | let cc = xf.apply(*c); |
| 102 | let rr = (r * xf.scale_factor()).max(1e-4); |
| 103 | let lut = build_lut(stops); |
| 104 | let opaque = lut.iter().all(|v| v >> 24 == 255); |
| 105 | Shader::Radial { cx: cc.x, cy: cc.y, inv_r: 1.0 / rr, lut: Box::new(lut), opaque } |
| 106 | } |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | } |
| 111 | |
| 112 | #[inline] |
| 113 | fn cov_u32(c: f32) -> u32 { |
| 114 | (c * 255.0 + 0.5) as u32 |
| 115 | } |
| 116 | |
| 117 | pub struct Layer { |
| 118 | pub x: i32, |
| 119 | pub y: i32, |
| 120 | pub w: usize, |
| 121 | pub h: usize, |
| 122 | pub scale: usize, |
| 123 | pub px: Vec<u32>, |
| 124 | } |
| 125 | |
| 126 | impl Layer { |
| 127 | #[inline] |
| 128 | pub fn sample(&self, dx: usize, dy: usize) -> u32 { |
| 129 | if self.w == 0 || self.h == 0 { |
| 130 | return 0; |
| 131 | } |
| 132 | if self.scale == 1 { |
| 133 | let lx = dx as i32 - self.x; |
| 134 | let ly = dy as i32 - self.y; |
| 135 | let lx = lx.clamp(0, self.w as i32 - 1) as usize; |
| 136 | let ly = ly.clamp(0, self.h as i32 - 1) as usize; |
| 137 | return self.px[ly * self.w + lx]; |
| 138 | } |
| 139 | let k = self.scale as f32; |
| 140 | let fx = (dx as f32 + 0.5 - self.x as f32) / k - 0.5; |
| 141 | let fy = (dy as f32 + 0.5 - self.y as f32) / k - 0.5; |
| 142 | bilinear_u32(&self.px, self.w, self.h, fx, fy) |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | #[inline] |
| 147 | fn bilinear_u32(px: &[u32], w: usize, h: usize, fx: f32, fy: f32) -> u32 { |
| 148 | let xf = fx.floor(); |
| 149 | let yf = fy.floor(); |
| 150 | let tx = ((fx - xf) * 255.0) as u32; |
| 151 | let ty = ((fy - yf) * 255.0) as u32; |
| 152 | let x0 = (xf as i32).clamp(0, w as i32 - 1) as usize; |
| 153 | let y0 = (yf as i32).clamp(0, h as i32 - 1) as usize; |
| 154 | let x1 = (x0 + 1).min(w - 1); |
| 155 | let y1 = (y0 + 1).min(h - 1); |
| 156 | let r0 = y0 * w; |
| 157 | let r1 = y1 * w; |
| 158 | let a = lerp_premul(px[r0 + x0], px[r0 + x1], tx); |
| 159 | let b = lerp_premul(px[r1 + x0], px[r1 + x1], tx); |
| 160 | lerp_premul(a, b, ty) |
| 161 | } |
| 162 | |
| 163 | |
| 164 | pub struct Canvas { |
| 165 | pub w: usize, |
| 166 | pub h: usize, |
| 167 | pub px: Vec<u32>, |
| 168 | ras: Rasterizer, |
| 169 | cov: Vec<f32>, |
| 170 | clip: Rect, |
| 171 | clip_stack: Vec<Rect>, |
| 172 | scratch: Path, |
| 173 | sh_ras: Rasterizer, |
| 174 | sh_mask: Vec<u8>, |
| 175 | sh_tmp: Vec<u8>, |
| 176 | sh_row: Vec<u8>, |
| 177 | } |
| 178 | |
| 179 | impl Canvas { |
| 180 | pub fn new(w: usize, h: usize) -> Canvas { |
| 181 | Canvas { |
| 182 | w, |
| 183 | h, |
| 184 | px: vec![0; w * h], |
| 185 | ras: Rasterizer::new(w, h), |
| 186 | cov: Vec::new(), |
| 187 | clip: Rect::new(0.0, 0.0, w as f32, h as f32), |
| 188 | clip_stack: Vec::new(), |
| 189 | scratch: Path::new(), |
| 190 | sh_ras: Rasterizer::new(0, 0), |
| 191 | sh_mask: Vec::new(), |
| 192 | sh_tmp: Vec::new(), |
| 193 | sh_row: Vec::new(), |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | pub fn resize(&mut self, w: usize, h: usize) { |
| 198 | if w == self.w && h == self.h { |
| 199 | return; |
| 200 | } |
| 201 | self.w = w; |
| 202 | self.h = h; |
| 203 | self.px.clear(); |
| 204 | self.px.resize(w * h, 0); |
| 205 | self.ras.resize(w, h); |
| 206 | self.clip = Rect::new(0.0, 0.0, w as f32, h as f32); |
| 207 | self.clip_stack.clear(); |
| 208 | } |
| 209 | |
| 210 | pub fn clear(&mut self, c: Color) { |
| 211 | let v = c.premul(); |
| 212 | for p in &mut self.px { |
| 213 | *p = v; |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | pub fn full(&self) -> Rect { |
| 218 | Rect::new(0.0, 0.0, self.w as f32, self.h as f32) |
| 219 | } |
| 220 | |
| 221 | pub fn clip(&self) -> Rect { |
| 222 | self.clip |
| 223 | } |
| 224 | |
| 225 | pub fn push_clip(&mut self, r: Rect) { |
| 226 | self.clip_stack.push(self.clip); |
| 227 | let snapped = Rect::new(r.x0.round(), r.y0.round(), r.x1.round(), r.y1.round()); |
| 228 | self.clip = self.clip.intersect(snapped); |
| 229 | } |
| 230 | |
| 231 | pub fn pop_clip(&mut self) { |
| 232 | if let Some(r) = self.clip_stack.pop() { |
| 233 | self.clip = r; |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | fn clip_bounds(&self) -> (usize, usize, usize, usize) { |
| 238 | let c = self.clip; |
| 239 | let x0 = (c.x0.max(0.0) as usize).min(self.w); |
| 240 | let y0 = (c.y0.max(0.0) as usize).min(self.h); |
| 241 | let x1 = (c.x1.max(0.0) as usize).min(self.w); |
| 242 | let y1 = (c.y1.max(0.0) as usize).min(self.h); |
| 243 | (x0, y0, x1.max(x0), y1.max(y0)) |
| 244 | } |
| 245 | |
| 246 | pub fn fill(&mut self, path: &Path, xf: &Transform, paint: &Paint) { |
| 247 | if self.clip.is_empty() { |
| 248 | return; |
| 249 | } |
| 250 | self.ras.reset(); |
| 251 | { |
| 252 | let ras = &mut self.ras; |
| 253 | path.flatten(xf, TOL, |a, b| ras.line(a, b)); |
| 254 | } |
| 255 | let shader = Shader::build(paint, xf); |
| 256 | self.composite(&shader); |
| 257 | } |
| 258 | |
| 259 | fn composite(&mut self, shader: &Shader) { |
| 260 | let (cx0, cy0, cx1, cy1) = self.clip_bounds(); |
| 261 | let (ras, px, cov, w) = (&self.ras, &mut self.px, &mut self.cov, self.w); |
| 262 | ras.for_rows(cov, |y, xs, covs| { |
| 263 | if y < cy0 || y >= cy1 { |
| 264 | return; |
| 265 | } |
| 266 | let lo = xs.max(cx0); |
| 267 | let hi = (xs + covs.len()).min(cx1); |
| 268 | if hi <= lo { |
| 269 | return; |
| 270 | } |
| 271 | let row = &mut px[y * w + lo..y * w + hi]; |
| 272 | let cs = &covs[lo - xs..hi - xs]; |
| 273 | match shader { |
| 274 | Shader::Solid(v) => simd::blend_solid_cov(row, *v, cs), |
| 275 | Shader::Linear { ox, oy, dx, dy, lut, opaque } => { |
| 276 | let t0 = (lo as f32 + 0.5 - ox) * dx + (y as f32 + 0.5 - oy) * dy; |
| 277 | simd::blend_lut_cov(row, lut, t0, *dx, cs, *opaque); |
| 278 | } |
| 279 | Shader::Radial { cx, cy, inv_r, lut, opaque } => { |
| 280 | let py = y as f32 + 0.5 - cy; |
| 281 | simd::blend_radial_cov(row, lut, lo as f32 + 0.5 - cx, py * py, *inv_r, cs, *opaque); |
| 282 | } |
| 283 | } |
| 284 | }); |
| 285 | } |
| 286 | |
| 287 | pub fn fill_layer(&mut self, path: &Path, xf: &Transform, layer: &Layer, tint: Color) { |
| 288 | if self.clip.is_empty() { |
| 289 | return; |
| 290 | } |
| 291 | self.ras.reset(); |
| 292 | { |
| 293 | let ras = &mut self.ras; |
| 294 | path.flatten(xf, TOL, |a, b| ras.line(a, b)); |
| 295 | } |
| 296 | let (cx0, cy0, cx1, cy1) = self.clip_bounds(); |
| 297 | let tint_p = tint.premul(); |
| 298 | let (ras, px, cov, w) = (&self.ras, &mut self.px, &mut self.cov, self.w); |
| 299 | ras.for_rows(cov, |y, xs, covs| { |
| 300 | if y < cy0 || y >= cy1 { |
| 301 | return; |
| 302 | } |
| 303 | let lo = xs.max(cx0); |
| 304 | let hi = (xs + covs.len()).min(cx1); |
| 305 | if hi <= lo { |
| 306 | return; |
| 307 | } |
| 308 | let row = &mut px[y * w + lo..y * w + hi]; |
| 309 | let cs = &covs[lo - xs..hi - xs]; |
| 310 | for (i, (d, &c)) in row.iter_mut().zip(cs).enumerate() { |
| 311 | if c <= simd::ZERO { |
| 312 | continue; |
| 313 | } |
| 314 | let base = layer.sample(lo + i, y) | 0xff00_0000; |
| 315 | let v = blend_over(base, tint_p); |
| 316 | *d = if c >= simd::FULL { v } else { blend_over(*d, scale_premul(v, cov_u32(c))) }; |
| 317 | } |
| 318 | }); |
| 319 | } |
| 320 | |
| 321 | pub fn stroke(&mut self, path: &Path, xf: &Transform, paint: &Paint, style: &Stroke) { |
| 322 | if self.clip.is_empty() || style.width <= 0.0 { |
| 323 | return; |
| 324 | } |
| 325 | let hw = (style.width * xf.scale_factor()) * 0.5; |
| 326 | let mut segs: Vec<(Vec<Point>, bool)> = Vec::new(); |
| 327 | path.subpaths(xf, TOL, |pts, closed| segs.push((pts.to_vec(), closed))); |
| 328 | self.ras.reset(); |
| 329 | { |
| 330 | let ras = &mut self.ras; |
| 331 | for (pts, closed) in &segs { |
| 332 | emit_stroke(ras, pts, *closed, hw, style.cap); |
| 333 | } |
| 334 | } |
| 335 | let shader = Shader::build(paint, xf); |
| 336 | self.composite(&shader); |
| 337 | } |
| 338 | |
| 339 | pub fn fill_rect(&mut self, r: Rect, c: Color) { |
| 340 | let mut p = core::mem::take(&mut self.scratch); |
| 341 | p.clear(); |
| 342 | p.rect(r); |
| 343 | self.fill(&p, &IDENTITY, &Paint::Solid(c)); |
| 344 | self.scratch = p; |
| 345 | } |
| 346 | |
| 347 | pub fn fill_rrect(&mut self, r: Rect, radius: f32, paint: &Paint) { |
| 348 | let mut p = core::mem::take(&mut self.scratch); |
| 349 | p.clear(); |
| 350 | p.rrect(r, radius); |
| 351 | self.fill(&p, &IDENTITY, paint); |
| 352 | self.scratch = p; |
| 353 | } |
| 354 | |
| 355 | pub fn stroke_rrect(&mut self, r: Rect, radius: f32, c: Color, width: f32) { |
| 356 | let mut p = core::mem::take(&mut self.scratch); |
| 357 | p.clear(); |
| 358 | p.rrect(r, radius); |
| 359 | self.stroke(&p, &IDENTITY, &Paint::Solid(c), &Stroke::new(width)); |
| 360 | self.scratch = p; |
| 361 | } |
| 362 | |
| 363 | pub fn fill_circle(&mut self, c: Point, r: f32, paint: &Paint) { |
| 364 | let mut p = core::mem::take(&mut self.scratch); |
| 365 | p.clear(); |
| 366 | p.circle(c, r); |
| 367 | self.fill(&p, &IDENTITY, paint); |
| 368 | self.scratch = p; |
| 369 | } |
| 370 | |
| 371 | /// Blits an 8-bit coverage mask tinted with `color`. This is how text lands on |
| 372 | /// the canvas: the glyph cache owns the mask, the canvas owns the clipping. |
| 373 | pub fn blit_mask(&mut self, x: i32, y: i32, w: usize, h: usize, mask: &[u8], color: Color) { |
| 374 | if w == 0 || h == 0 || color.a == 0 || mask.len() < w * h { |
| 375 | return; |
| 376 | } |
| 377 | let (cx0, cy0, cx1, cy1) = self.clip_bounds(); |
| 378 | let sx = (cx0 as i64 - x as i64).max(0) as usize; |
| 379 | let sy = (cy0 as i64 - y as i64).max(0) as usize; |
| 380 | if sx >= w || sy >= h { |
| 381 | return; |
| 382 | } |
| 383 | let ex = ((cx1 as i64 - x as i64).max(0) as usize).min(w); |
| 384 | let ey = ((cy1 as i64 - y as i64).max(0) as usize).min(h); |
| 385 | if ex <= sx || ey <= sy { |
| 386 | return; |
| 387 | } |
| 388 | let cp = color.premul(); |
| 389 | let n = ex - sx; |
| 390 | for row in sy..ey { |
| 391 | let dy = (y + row as i32) as usize; |
| 392 | let dx = (x + sx as i32) as usize; |
| 393 | let base = dy * self.w + dx; |
| 394 | simd::blend_solid_alpha( |
| 395 | &mut self.px[base..base + n], |
| 396 | cp, |
| 397 | &mask[row * w + sx..row * w + ex], |
| 398 | ); |
| 399 | } |
| 400 | } |
| 401 | |
| 402 | pub fn snapshot(&self, r: Rect) -> Layer { |
| 403 | let x0 = (r.x0.floor() as i64).clamp(0, self.w as i64) as usize; |
| 404 | let y0 = (r.y0.floor() as i64).clamp(0, self.h as i64) as usize; |
| 405 | let x1 = (r.x1.ceil() as i64).clamp(0, self.w as i64) as usize; |
| 406 | let y1 = (r.y1.ceil() as i64).clamp(0, self.h as i64) as usize; |
| 407 | let (lw, lh) = (x1.saturating_sub(x0), y1.saturating_sub(y0)); |
| 408 | let mut px = vec![0u32; lw * lh]; |
| 409 | for y in 0..lh { |
| 410 | let src = (y0 + y) * self.w + x0; |
| 411 | px[y * lw..(y + 1) * lw].copy_from_slice(&self.px[src..src + lw]); |
| 412 | } |
| 413 | Layer { x: x0 as i32, y: y0 as i32, w: lw, h: lh, scale: 1, px } |
| 414 | } |
| 415 | |
| 416 | pub fn blurred_layer(&self, r: Rect, sigma: f32) -> Layer { |
| 417 | let k = ((sigma / 3.0).round() as usize).clamp(1, 8); |
| 418 | let pad = (sigma * 2.5).ceil(); |
| 419 | let x0 = ((r.x0 - pad).floor() as i64).clamp(0, self.w as i64) as usize; |
| 420 | let y0 = ((r.y0 - pad).floor() as i64).clamp(0, self.h as i64) as usize; |
| 421 | let x1 = ((r.x1 + pad).ceil() as i64).clamp(0, self.w as i64) as usize; |
| 422 | let y1 = ((r.y1 + pad).ceil() as i64).clamp(0, self.h as i64) as usize; |
| 423 | let (rw, rh) = (x1.saturating_sub(x0), y1.saturating_sub(y0)); |
| 424 | if rw == 0 || rh == 0 { |
| 425 | return Layer { x: 0, y: 0, w: 0, h: 0, scale: 1, px: Vec::new() }; |
| 426 | } |
| 427 | let lw = rw.div_ceil(k); |
| 428 | let lh = rh.div_ceil(k); |
| 429 | let mut px = vec![0u32; lw * lh]; |
| 430 | for j in 0..lh { |
| 431 | for i in 0..lw { |
| 432 | let mut acc = [0u32; 4]; |
| 433 | let mut n = 0u32; |
| 434 | for sy in 0..k { |
| 435 | let yy = y0 + j * k + sy; |
| 436 | if yy >= y1 { |
| 437 | break; |
| 438 | } |
| 439 | for sx in 0..k { |
| 440 | let xx = x0 + i * k + sx; |
| 441 | if xx >= x1 { |
| 442 | break; |
| 443 | } |
| 444 | let v = self.px[yy * self.w + xx]; |
| 445 | for (ch, a) in acc.iter_mut().enumerate() { |
| 446 | *a += (v >> (ch * 8)) & 0xff; |
| 447 | } |
| 448 | n += 1; |
| 449 | } |
| 450 | } |
| 451 | if n > 0 { |
| 452 | let mut o = 0u32; |
| 453 | for (ch, a) in acc.iter().enumerate() { |
| 454 | o |= (a / n) << (ch * 8); |
| 455 | } |
| 456 | px[j * lw + i] = o; |
| 457 | } |
| 458 | } |
| 459 | } |
| 460 | blur_premul(&mut px, lw, lh, sigma / k as f32); |
| 461 | Layer { x: x0 as i32, y: y0 as i32, w: lw, h: lh, scale: k, px } |
| 462 | } |
| 463 | |
| 464 | pub fn shadow(&mut self, path: &Path, xf: &Transform, sigma: f32, color: Color, offset: Point) { |
| 465 | self.shadow_occluded(path, xf, sigma, color, offset, Rect::new(0.0, 0.0, 0.0, 0.0)); |
| 466 | } |
| 467 | |
| 468 | pub fn shadow_occluded( |
| 469 | &mut self, |
| 470 | path: &Path, |
| 471 | xf: &Transform, |
| 472 | sigma: f32, |
| 473 | color: Color, |
| 474 | offset: Point, |
| 475 | occluder: Rect, |
| 476 | ) { |
| 477 | if self.clip.is_empty() || color.a == 0 { |
| 478 | return; |
| 479 | } |
| 480 | let k = ((sigma / 3.0).round() as usize).clamp(1, 8) as f32; |
| 481 | let b = path.transform(xf).bounds(); |
| 482 | let pad = sigma * 3.0 + 2.0; |
| 483 | let ox = (b.x0 + offset.x - pad).floor(); |
| 484 | let oy = (b.y0 + offset.y - pad).floor(); |
| 485 | let dev_w = (b.x1 + offset.x + pad).ceil() - ox; |
| 486 | let dev_h = (b.y1 + offset.y + pad).ceil() - oy; |
| 487 | if dev_w <= 0.0 || dev_h <= 0.0 { |
| 488 | return; |
| 489 | } |
| 490 | let mw = (dev_w / k).ceil() as usize + 1; |
| 491 | let mh = (dev_h / k).ceil() as usize + 1; |
| 492 | if mw == 0 || mh == 0 || mw > 4096 || mh > 4096 { |
| 493 | return; |
| 494 | } |
| 495 | self.sh_ras.resize(mw, mh); |
| 496 | self.sh_ras.reset(); |
| 497 | let total = xf |
| 498 | .then(&Transform::translate(offset.x - ox, offset.y - oy)) |
| 499 | .then(&Transform::scale(1.0 / k, 1.0 / k)); |
| 500 | { |
| 501 | let ras = &mut self.sh_ras; |
| 502 | path.flatten(&total, TOL / 2.0, |a, bb| ras.line(a, bb)); |
| 503 | } |
| 504 | self.sh_mask.clear(); |
| 505 | self.sh_mask.resize(mw * mh, 0); |
| 506 | { |
| 507 | let mask = &mut self.sh_mask; |
| 508 | self.sh_ras.spans(|x, y, c| mask[y * mw + x] = (c * 255.0 + 0.5) as u8); |
| 509 | } |
| 510 | let s = sigma / k; |
| 511 | if s > 0.3 { |
| 512 | self.sh_tmp.clear(); |
| 513 | self.sh_tmp.resize(mw * mh, 0); |
| 514 | box_blur(&mut self.sh_mask, &mut self.sh_tmp, mw, mh, s); |
| 515 | } |
| 516 | |
| 517 | let (mut bx0, mut by0, mut bx1, mut by1) = (mw, mh, 0usize, 0usize); |
| 518 | for y in 0..mh { |
| 519 | for x in 0..mw { |
| 520 | if self.sh_mask[y * mw + x] > 1 { |
| 521 | bx0 = bx0.min(x); |
| 522 | by0 = by0.min(y); |
| 523 | bx1 = bx1.max(x + 1); |
| 524 | by1 = by1.max(y + 1); |
| 525 | } |
| 526 | } |
| 527 | } |
| 528 | if bx1 <= bx0 || by1 <= by0 { |
| 529 | return; |
| 530 | } |
| 531 | |
| 532 | let (cx0, cy0, cx1, cy1) = self.clip_bounds(); |
| 533 | let dx0 = ((ox + bx0 as f32 * k).floor() as i64).clamp(cx0 as i64, cx1 as i64) as usize; |
| 534 | let dy0 = ((oy + by0 as f32 * k).floor() as i64).clamp(cy0 as i64, cy1 as i64) as usize; |
| 535 | let dx1 = ((ox + bx1 as f32 * k).ceil() as i64).clamp(cx0 as i64, cx1 as i64) as usize; |
| 536 | let dy1 = ((oy + by1 as f32 * k).ceil() as i64).clamp(cy0 as i64, cy1 as i64) as usize; |
| 537 | let cp = color.premul(); |
| 538 | let inv = 1.0 / k; |
| 539 | let fx0 = (dx0 as f32 + 0.5 - ox) * inv - 0.5; |
| 540 | let step = (inv * 65536.0) as i32; |
| 541 | let width = dx1 - dx0; |
| 542 | self.sh_row.clear(); |
| 543 | self.sh_row.resize(width, 0); |
| 544 | let occ = occluder.intersect(Rect::new(dx0 as f32, dy0 as f32, dx1 as f32, dy1 as f32)); |
| 545 | let (ox0, ox1) = if occ.is_empty() { |
| 546 | (0usize, 0usize) |
| 547 | } else { |
| 548 | ((occ.x0 as usize - dx0), (occ.x1 as usize - dx0)) |
| 549 | }; |
| 550 | for y in dy0..dy1 { |
| 551 | let fy = (y as f32 + 0.5 - oy) * inv - 0.5; |
| 552 | let yfl = fy.floor(); |
| 553 | let ty = (((fy - yfl) * 256.0) as u32).min(256); |
| 554 | let y0i = (yfl as i32).clamp(0, mh as i32 - 1) as usize; |
| 555 | let y1i = (y0i + 1).min(mh - 1); |
| 556 | let m0 = &self.sh_mask[y0i * mw..y0i * mw + mw]; |
| 557 | let m1 = &self.sh_mask[y1i * mw..y1i * mw + mw]; |
| 558 | let occluded = !occ.is_empty() && (y as f32) >= occ.y0 && (y as f32) < occ.y1; |
| 559 | let mut fixed = (fx0 * 65536.0) as i32; |
| 560 | let last = mw as i32 - 1; |
| 561 | for a in self.sh_row[..width].iter_mut() { |
| 562 | let xi = fixed >> 16; |
| 563 | let tx = ((fixed >> 8) & 0xff) as u32; |
| 564 | let x0i = xi.clamp(0, last) as usize; |
| 565 | let x1i = (xi + 1).clamp(0, last) as usize; |
| 566 | let p = m0[x0i] as u32 * (256 - tx) + m0[x1i] as u32 * tx; |
| 567 | let q = m1[x0i] as u32 * (256 - tx) + m1[x1i] as u32 * tx; |
| 568 | *a = ((((p >> 8) * (256 - ty) + (q >> 8) * ty) >> 8) as u8).min(255); |
| 569 | fixed += step; |
| 570 | } |
| 571 | let base = y * self.w + dx0; |
| 572 | if occluded { |
| 573 | if ox0 > 0 { |
| 574 | simd::blend_solid_alpha( |
| 575 | &mut self.px[base..base + ox0], |
| 576 | cp, |
| 577 | &self.sh_row[..ox0], |
| 578 | ); |
| 579 | } |
| 580 | if ox1 < width { |
| 581 | simd::blend_solid_alpha( |
| 582 | &mut self.px[base + ox1..base + width], |
| 583 | cp, |
| 584 | &self.sh_row[ox1..width], |
| 585 | ); |
| 586 | } |
| 587 | } else { |
| 588 | simd::blend_solid_alpha( |
| 589 | &mut self.px[base..base + width], |
| 590 | cp, |
| 591 | &self.sh_row[..width], |
| 592 | ); |
| 593 | } |
| 594 | } |
| 595 | } |
| 596 | |
| 597 | pub fn blur_region(&mut self, r: Rect, sigma: f32) { |
| 598 | let x0 = (r.x0.floor() as i64).clamp(0, self.w as i64) as usize; |
| 599 | let y0 = (r.y0.floor() as i64).clamp(0, self.h as i64) as usize; |
| 600 | let x1 = (r.x1.ceil() as i64).clamp(0, self.w as i64) as usize; |
| 601 | let y1 = (r.y1.ceil() as i64).clamp(0, self.h as i64) as usize; |
| 602 | let (lw, lh) = (x1.saturating_sub(x0), y1.saturating_sub(y0)); |
| 603 | if lw == 0 || lh == 0 { |
| 604 | return; |
| 605 | } |
| 606 | let mut buf = vec![0u32; lw * lh]; |
| 607 | for y in 0..lh { |
| 608 | let src = (y0 + y) * self.w + x0; |
| 609 | buf[y * lw..(y + 1) * lw].copy_from_slice(&self.px[src..src + lw]); |
| 610 | } |
| 611 | blur_premul(&mut buf, lw, lh, sigma); |
| 612 | for y in 0..lh { |
| 613 | let dst = (y0 + y) * self.w + x0; |
| 614 | self.px[dst..dst + lw].copy_from_slice(&buf[y * lw..(y + 1) * lw]); |
| 615 | } |
| 616 | } |
| 617 | |
| 618 | pub fn to_rgba(&self) -> Vec<u8> { |
| 619 | let mut out = vec![0u8; self.w * self.h * 4]; |
| 620 | for (i, v) in self.px.iter().enumerate() { |
| 621 | let c = Color::from_premul(*v); |
| 622 | out[i * 4] = c.r; |
| 623 | out[i * 4 + 1] = c.g; |
| 624 | out[i * 4 + 2] = c.b; |
| 625 | out[i * 4 + 3] = c.a; |
| 626 | } |
| 627 | out |
| 628 | } |
| 629 | |
| 630 | pub fn to_png(&self) -> Vec<u8> { |
| 631 | crate::png::encode_rgba(self.w, self.h, &self.to_rgba()) |
| 632 | } |
| 633 | } |
| 634 | |
| 635 | fn emit_stroke(ras: &mut Rasterizer, pts: &[Point], closed: bool, hw: f32, cap: Cap) { |
| 636 | if hw <= 0.0 { |
| 637 | return; |
| 638 | } |
| 639 | if pts.len() < 2 { |
| 640 | if cap == Cap::Round && pts.len() == 1 { |
| 641 | emit_disc(ras, pts[0], hw); |
| 642 | } |
| 643 | return; |
| 644 | } |
| 645 | let n = pts.len(); |
| 646 | let last = if closed { n } else { n - 1 }; |
| 647 | for i in 0..last { |
| 648 | let a = pts[i]; |
| 649 | let b = pts[(i + 1) % n]; |
| 650 | let d = b - a; |
| 651 | if d.len_sq() < 1e-12 { |
| 652 | continue; |
| 653 | } |
| 654 | let dir = d.norm(); |
| 655 | let (mut a, mut b) = (a, b); |
| 656 | if !closed && cap == Cap::Square { |
| 657 | if i == 0 { |
| 658 | a = a - dir * hw; |
| 659 | } |
| 660 | if i == last - 1 { |
| 661 | b = b + dir * hw; |
| 662 | } |
| 663 | } |
| 664 | let nrm = dir.perp() * hw; |
| 665 | let p0 = a + nrm; |
| 666 | let p1 = b + nrm; |
| 667 | let p2 = b - nrm; |
| 668 | let p3 = a - nrm; |
| 669 | ras.line(p0, p1); |
| 670 | ras.line(p1, p2); |
| 671 | ras.line(p2, p3); |
| 672 | ras.line(p3, p0); |
| 673 | } |
| 674 | let joint_start = if closed { 0 } else { 1 }; |
| 675 | let joint_end = if closed { n } else { n - 1 }; |
| 676 | if hw > 0.6 { |
| 677 | for p in pts.iter().take(joint_end).skip(joint_start) { |
| 678 | emit_disc(ras, *p, hw); |
| 679 | } |
| 680 | } |
| 681 | if !closed && cap == Cap::Round { |
| 682 | emit_disc(ras, pts[0], hw); |
| 683 | emit_disc(ras, pts[n - 1], hw); |
| 684 | } |
| 685 | } |
| 686 | |
| 687 | fn emit_disc(ras: &mut Rasterizer, c: Point, r: f32) { |
| 688 | if r < 0.35 { |
| 689 | return; |
| 690 | } |
| 691 | let steps = ((r * 2.2) as usize).clamp(6, 48); |
| 692 | let mut prev = pt(c.x + r, c.y); |
| 693 | for i in 1..=steps { |
| 694 | let a = -(i as f32) / steps as f32 * core::f32::consts::TAU; |
| 695 | let p = pt(c.x + r * a.cos(), c.y + r * a.sin()); |
| 696 | ras.line(prev, p); |
| 697 | prev = p; |
| 698 | } |
| 699 | } |
| 700 | |
| 701 | fn box_sizes(sigma: f32) -> [usize; 3] { |
| 702 | const N: f32 = 3.0; |
| 703 | let s = sigma.max(0.0); |
| 704 | let ideal = (12.0 * s * s / N + 1.0).sqrt(); |
| 705 | let mut wl = ideal.floor() as i32; |
| 706 | if wl % 2 == 0 { |
| 707 | wl -= 1; |
| 708 | } |
| 709 | let wl = wl.max(1); |
| 710 | let wu = wl + 2; |
| 711 | let m = ((12.0 * s * s - N * (wl * wl) as f32 - 4.0 * N * wl as f32 - 3.0 * N) |
| 712 | / (-4.0 * wl as f32 - 4.0)) |
| 713 | .round() as i32; |
| 714 | let mut out = [0usize; 3]; |
| 715 | for (i, o) in out.iter_mut().enumerate() { |
| 716 | let w = if (i as i32) < m { wl } else { wu }; |
| 717 | *o = ((w - 1) / 2).max(0) as usize; |
| 718 | } |
| 719 | out |
| 720 | } |
| 721 | |
| 722 | fn box_blur(mask: &mut [u8], tmp: &mut [u8], w: usize, h: usize, sigma: f32) { |
| 723 | for r in box_sizes(sigma) { |
| 724 | if r == 0 { |
| 725 | continue; |
| 726 | } |
| 727 | blur_h_u8(mask, tmp, w, h, r); |
| 728 | blur_v_u8(tmp, mask, w, h, r); |
| 729 | } |
| 730 | } |
| 731 | |
| 732 | fn blur_h_u8(src: &[u8], dst: &mut [u8], w: usize, h: usize, r: usize) { |
| 733 | let win = (2 * r + 1) as u32; |
| 734 | let mul = (1u32 << 16) / win; |
| 735 | for y in 0..h { |
| 736 | let s = &src[y * w..y * w + w]; |
| 737 | let d = &mut dst[y * w..y * w + w]; |
| 738 | let mut sum = s[0] as u32 * (r + 1) as u32; |
| 739 | for x in 1..=r.min(w - 1) { |
| 740 | sum += s[x] as u32; |
| 741 | } |
| 742 | if r >= w { |
| 743 | sum += s[w - 1] as u32 * (r + 1 - w) as u32; |
| 744 | } |
| 745 | for x in 0..w { |
| 746 | d[x] = ((sum * mul + (1 << 15)) >> 16) as u8; |
| 747 | sum += s[(x + r + 1).min(w - 1)] as u32; |
| 748 | sum -= s[x.saturating_sub(r)] as u32; |
| 749 | } |
| 750 | } |
| 751 | } |
| 752 | |
| 753 | fn blur_v_u8(src: &[u8], dst: &mut [u8], w: usize, h: usize, r: usize) { |
| 754 | let win = (2 * r + 1) as u32; |
| 755 | let mul = (1u32 << 16) / win; |
| 756 | let mut col = vec![0u32; w]; |
| 757 | for (x, c) in col.iter_mut().enumerate() { |
| 758 | *c = src[x] as u32 * (r + 1) as u32; |
| 759 | } |
| 760 | for y in 1..=r.min(h - 1) { |
| 761 | for x in 0..w { |
| 762 | col[x] += src[y * w + x] as u32; |
| 763 | } |
| 764 | } |
| 765 | if r >= h { |
| 766 | for x in 0..w { |
| 767 | col[x] += src[(h - 1) * w + x] as u32 * (r + 1 - h) as u32; |
| 768 | } |
| 769 | } |
| 770 | for y in 0..h { |
| 771 | let add = (y + r + 1).min(h - 1) * w; |
| 772 | let sub = y.saturating_sub(r) * w; |
| 773 | for x in 0..w { |
| 774 | dst[y * w + x] = ((col[x] * mul + (1 << 15)) >> 16) as u8; |
| 775 | col[x] += src[add + x] as u32; |
| 776 | col[x] -= src[sub + x] as u32; |
| 777 | } |
| 778 | } |
| 779 | } |
| 780 | |
| 781 | pub fn blur_premul(buf: &mut [u32], w: usize, h: usize, sigma: f32) { |
| 782 | if w == 0 || h == 0 || sigma <= 0.0 { |
| 783 | return; |
| 784 | } |
| 785 | let mut tmp = vec![0u32; w * h]; |
| 786 | for r in box_sizes(sigma) { |
| 787 | if r == 0 { |
| 788 | continue; |
| 789 | } |
| 790 | blur_h_u32(buf, &mut tmp, w, h, r); |
| 791 | blur_v_u32(&tmp, buf, w, h, r); |
| 792 | } |
| 793 | } |
| 794 | |
| 795 | #[inline] |
| 796 | fn split(v: u32) -> (u32, u32) { |
| 797 | (v & 0x00ff_00ff, (v >> 8) & 0x00ff_00ff) |
| 798 | } |
| 799 | |
| 800 | #[inline] |
| 801 | fn joinc(lo: u32, hi: u32) -> u32 { |
| 802 | (lo & 0x00ff_00ff) | ((hi & 0x00ff_00ff) << 8) |
| 803 | } |
| 804 | |
| 805 | fn blur_h_u32(src: &[u32], dst: &mut [u32], w: usize, h: usize, r: usize) { |
| 806 | let win = (2 * r + 1) as u32; |
| 807 | let mul = (1u32 << 14) / win; |
| 808 | for y in 0..h { |
| 809 | let s = &src[y * w..y * w + w]; |
| 810 | let d = &mut dst[y * w..y * w + w]; |
| 811 | let (a, b) = split(s[0]); |
| 812 | let mut lo = a * (r + 1) as u32; |
| 813 | let mut hi = b * (r + 1) as u32; |
| 814 | for x in 1..=r.min(w - 1) { |
| 815 | let (a, b) = split(s[x]); |
| 816 | lo += a; |
| 817 | hi += b; |
| 818 | } |
| 819 | if r >= w { |
| 820 | let (a, b) = split(s[w - 1]); |
| 821 | lo += a * (r + 1 - w) as u32; |
| 822 | hi += b * (r + 1 - w) as u32; |
| 823 | } |
| 824 | for x in 0..w { |
| 825 | let ol = ((lo >> 16) * mul >> 14) << 16 | (((lo & 0xffff) * mul) >> 14); |
| 826 | let oh = ((hi >> 16) * mul >> 14) << 16 | (((hi & 0xffff) * mul) >> 14); |
| 827 | d[x] = joinc(ol, oh); |
| 828 | let (a1, b1) = split(s[(x + r + 1).min(w - 1)]); |
| 829 | let (a2, b2) = split(s[x.saturating_sub(r)]); |
| 830 | lo = lo + a1 - a2; |
| 831 | hi = hi + b1 - b2; |
| 832 | } |
| 833 | } |
| 834 | } |
| 835 | |
| 836 | fn blur_v_u32(src: &[u32], dst: &mut [u32], w: usize, h: usize, r: usize) { |
| 837 | let win = (2 * r + 1) as u32; |
| 838 | let mul = (1u32 << 14) / win; |
| 839 | let mut clo = vec![0u32; w]; |
| 840 | let mut chi = vec![0u32; w]; |
| 841 | for x in 0..w { |
| 842 | let (a, b) = split(src[x]); |
| 843 | clo[x] = a * (r + 1) as u32; |
| 844 | chi[x] = b * (r + 1) as u32; |
| 845 | } |
| 846 | for y in 1..=r.min(h - 1) { |
| 847 | for x in 0..w { |
| 848 | let (a, b) = split(src[y * w + x]); |
| 849 | clo[x] += a; |
| 850 | chi[x] += b; |
| 851 | } |
| 852 | } |
| 853 | if r >= h { |
| 854 | for x in 0..w { |
| 855 | let (a, b) = split(src[(h - 1) * w + x]); |
| 856 | clo[x] += a * (r + 1 - h) as u32; |
| 857 | chi[x] += b * (r + 1 - h) as u32; |
| 858 | } |
| 859 | } |
| 860 | for y in 0..h { |
| 861 | let add = (y + r + 1).min(h - 1) * w; |
| 862 | let sub = y.saturating_sub(r) * w; |
| 863 | for x in 0..w { |
| 864 | let lo = clo[x]; |
| 865 | let hi = chi[x]; |
| 866 | let ol = ((lo >> 16) * mul >> 14) << 16 | (((lo & 0xffff) * mul) >> 14); |
| 867 | let oh = ((hi >> 16) * mul >> 14) << 16 | (((hi & 0xffff) * mul) >> 14); |
| 868 | dst[y * w + x] = joinc(ol, oh); |
| 869 | let (a1, b1) = split(src[add + x]); |
| 870 | let (a2, b2) = split(src[sub + x]); |
| 871 | clo[x] = lo + a1 - a2; |
| 872 | chi[x] = hi + b1 - b2; |
| 873 | } |
| 874 | } |
| 875 | } |
| 876 | |
| 877 | #[inline] |
| 878 | pub fn lerp_u32(a: u32, b: u32, t: f32) -> u32 { |
| 879 | lerp_premul(a, b, (t.clamp(0.0, 1.0) * 255.0 + 0.5) as u32) |
| 880 | } |
| 881 | |
| 882 | #[inline] |
| 883 | pub fn shade(a: Color, b: Color, t: f32) -> Color { |
| 884 | a.lerp(b, t) |
| 885 | } |