use super::{lerp, dev_squared, Rasterizer, Op, SimdVec};
use crate::image::{RGBA, Rectangle, Point};
const FPM_THRESHOLD: usize = 512;
impl Rasterizer {
pub fn new(w: usize, h: usize) -> Self {
Self {
size: [w, h],
first: [0.0, 0.0],
pen: [0.0, 0.0],
draw_op: Op::Over,
use_fpm: w > FPM_THRESHOLD || h > FPM_THRESHOLD,
buf: SimdVec::new(w * h),
}
}
pub fn reset(&mut self, w: usize, h: usize, op: Op) {
self.size = [w, h];
self.first = [0.0, 0.0];
self.pen = [0.0, 0.0];
self.draw_op = op;
self.use_fpm = w > FPM_THRESHOLD || h > FPM_THRESHOLD;
self.buf.recycle(w * h);
}
pub fn clear(&mut self) {
let [w, h] = self.size;
self.reset(w, h, Op::Over);
}
pub fn size(&self) -> [usize; 2] { self.size }
pub fn as_mask_f32(&self) -> &[f32] { self.buf.as_slice_f32() }
pub fn as_mask_u32(&self) -> &[u32] { self.buf.as_slice_u32() }
pub fn pen(&self) -> [f32; 2] { self.pen }
pub fn close_path(&mut self) {
self.line_to(self.first[0], self.first[1])
}
pub fn move_to(&mut self, ax: f32, ay: f32) {
self.first = [ax, ay];
self.pen = [ax, ay];
}
pub fn line_to(&mut self, bx: f32, by: f32) {
if self.use_fpm {
self.floating_line_to(bx, by)
} else {
self.fixed_line_to(bx, by)
}
}
pub fn quad_to(&mut self, bx: f32, by: f32, cx: f32, cy: f32) {
let [ax, ay] = self.pen;
let devsq = dev_squared(ax, ay, bx, by, cx, cy);
if devsq >= 0.333 {
const TOL: f64 = 3f64;
let n = 1 + (TOL * devsq as f64).sqrt().sqrt() as isize;
let (mut t, n_inv) = (0.0, 1.0 / n as f32);
for _ in 0..n-1 {
t += n_inv;
let (abx, aby) = lerp(t, ax, ay, bx, by);
let (bcx, bcy) = lerp(t, bx, by, cx, cy);
let (bx, by) = lerp(t, abx, aby, bcx, bcy);
self.line_to(bx, by);
}
}
self.line_to(cx, cy);
}
pub fn cube_to(&mut self, bx: f32, by: f32, cx: f32, cy: f32, dx: f32, dy: f32) {
let [ax, ay] = self.pen;
let devsq = dev_squared(ax, ay, bx, by, dx, dy);
let devsq_alt = dev_squared(ax, ay, cx, cy, dx, dy);
let devsq = if devsq < devsq_alt { devsq_alt } else { devsq };
if devsq >= 0.333 {
const TOL: f64 = 3f64;
let n = 1 + (TOL * devsq as f64).sqrt().sqrt() as isize;
let (mut t, n_inv) = (0.0, 1.0 / n as f32);
for _ in 0..n-1 {
t += n_inv;
let (abx, aby) = lerp(t, ax, ay, bx, by);
let (bcx, bcy) = lerp(t, bx, by, cx, cy);
let (cdx, cdy) = lerp(t, cx, cy, dx, dy);
let (abcx, abcy) = lerp(t, abx, aby, bcx, bcy);
let (bcdx, bcdy) = lerp(t, bcx, bcy, cdx, cdy);
let (bx, by) = lerp(t, abcx, abcy, bcdx, bcdy);
self.line_to(bx, by)
}
}
self.line_to(dx, dy);
}
fn accumulate_mask(&mut self) {
let simd = false;
if simd {
unimplemented!("SIMD version")
} else {
if self.use_fpm {
self.floating_accumulate_mask()
} else {
self.fixed_accumulate_mask()
}
}
}
pub fn rgba_uniform_over(&mut self, dst: &mut RGBA, r: Rectangle, color: [u32; 4]) {
self.accumulate_mask();
let [sr, sg, sb, sa] = color;
let idx = dst.pix_offset(r.min.x, r.min.y);
let pix = &mut dst.pix[idx as usize..];
let x1 = r.max.x-r.min.x;
let y1 = r.max.y-r.min.y;
for y in 0..y1 {
for x in 0..x1 {
let idx = y * self.size[0] as isize + x;
let ma = self.buf.as_u32()[idx as usize];
let a = 0xffff - (sa * ma / 0xffff);
let i = (y * dst.stride + 4 * x) as usize;
pix[i+0] = ((((pix[i+0] as u32) * 0x101 * a + sr * ma) / 0xffff) >> 8) as u8;
pix[i+1] = ((((pix[i+1] as u32) * 0x101 * a + sg * ma) / 0xffff) >> 8) as u8;
pix[i+2] = ((((pix[i+2] as u32) * 0x101 * a + sb * ma) / 0xffff) >> 8) as u8;
pix[i+3] = ((((pix[i+3] as u32) * 0x101 * a + sa * ma) / 0xffff) >> 8) as u8;
}
}
}
pub fn rgba_uniform_src(&mut self, dst: &mut RGBA, r: Rectangle, color: [u32; 4]) {
self.accumulate_mask();
let [sr, sg, sb, sa] = color;
let idx = dst.pix_offset(r.min.x, r.min.y);
let pix = &mut dst.pix[idx as usize..];
let x1 = r.max.x-r.min.x;
let y1 = r.max.y-r.min.y;
for y in 0..y1 {
for x in 0..x1 {
let idx = y * self.size[0] as isize + x;
let ma = self.buf.as_u32()[idx as usize];
let i = (y * dst.stride + 4 * x) as usize;
pix[i+0] = ((sr * ma / 0xffff) >> 8) as u8;
pix[i+1] = ((sg * ma / 0xffff) >> 8) as u8;
pix[i+2] = ((sb * ma / 0xffff) >> 8) as u8;
pix[i+3] = ((sa * ma / 0xffff) >> 8) as u8;
}
}
}
}