use super::{clamp, Rasterizer};
const ϕ: int1ϕ = 9;
const fxOne: int1ϕ = 1 << ϕ;
const fxOneAndAHalf: int1ϕ = 1<<ϕ + 1<<(ϕ-1);
const fxOneMinusIota: int1ϕ = 1<<ϕ - 1;
type int1ϕ = i32;
type int2ϕ = i32;
#[inline(always)] fn fmax(x: int1ϕ, y: int1ϕ) -> int1ϕ { if x > y { x } else { y } }
#[inline(always)] fn fmin(x: int1ϕ, y: int1ϕ) -> int1ϕ { if x < y { x } else { y } }
#[inline(always)] fn floor(x: int1ϕ) -> i32 { (x >> ϕ) }
#[inline(always)] fn ceil(x: int1ϕ) -> i32 { ((x + fxOneMinusIota) >> ϕ) }
impl Rasterizer {
pub fn fixed_accumulate_mask(&mut self) {
let buf = self.buf.as_u32();
let mut acc = 0i32;
for v in buf {
acc += (*v) as i32;
let mut a = acc;
if a < 0 { a = -a }
a >>= 2*ϕ - 16;
if a > 0xffff { a = 0xffff; }
*v = a as u32;
}
}
pub fn fixed_line_to(&mut self, bx: f32, by: f32) {
let [ax, ay] = self.pen;
self.pen = [bx, by];
let (dir, ax, ay, bx, by) = if ay > by {
(-1, bx, by, ax, ay)
} else {
(1, ax, ay, bx, by)
};
if by-ay <= 0.000001 { return }
let dxdy = (bx - ax) / (by - ay);
let ayϕ = (ay * (fxOne as f32)) as int1ϕ;
let byϕ = (by * (fxOne as f32)) as int1ϕ;
let mut x = (ax * (fxOne as f32)) as int1ϕ;
let mut y = floor(ayϕ);
let y_max = ceil(byϕ);
let y_max = if y_max > self.size[1] as i32 {
self.size[1] as i32
} else {
y_max
};
let width = self.size[0] as i32;
while y < y_max {
let dy = fmin((1 + y as int1ϕ)<<ϕ, byϕ) - fmax((y as int1ϕ)<<ϕ, ayϕ);
let x_next = x + ((dy as f32)*dxdy) as int1ϕ;
if y < 0 {
x = x_next;
continue;
}
let buf = &mut self.buf.as_u32()[(y*width) as usize..];
let d = dy * dir; let (x0, x1) = if x > x_next {
(x_next, x)
} else {
(x, x_next)
};
let x0i = floor(x0);
let x0floor = (x0i as int1ϕ) << ϕ;
let x1i = ceil(x1);
let x1ceil = (x1i as int1ϕ) << ϕ;
if x1i <= x0i+1 {
let xmf = (x+x_next)>>1 - x0floor;
let i = clamp(x0i+0, width);
if i < buf.len() {
buf[i] += (d * (fxOne - xmf)) as u32
}
let i = clamp(x0i+1, width);
if i < buf.len() {
buf[i] += (d * xmf) as u32
}
} else {
let one_over_s = x1 - x0;
let two_over_s = 2 * one_over_s;
let x0f = x0 - x0floor;
let one_minus_x0f = fxOne - x0f;
let one_minus_x0f_squared = one_minus_x0f * one_minus_x0f;
let x1f = x1 - x1ceil + fxOne;
let x1f_squared = x1f * x1f;
let i = clamp(x0i, width);
if i < buf.len() {
let mut D = one_minus_x0f_squared; D *= d; D /= two_over_s;
buf[i] += D as u32;
}
if x1i == x0i+2 {
let i = clamp(x0i+1, width);
if i < buf.len() {
let mut D = two_over_s<<ϕ - one_minus_x0f_squared - x1f_squared; D *= d; D /= two_over_s;
buf[i] += D as u32;
}
} else {
let i = clamp(x0i+1, width);
if i < buf.len() {
let mut D = (fxOneAndAHalf-x0f)<<(ϕ+1) - one_minus_x0f_squared; D *= d; D /= two_over_s;
buf[i] += D as u32;
}
let d_times_s = ((d << (2 * ϕ)) / one_over_s) as u32;
for xi in (x0i + 2)..(x1i-1) {
let i = clamp(xi, width);
if i < buf.len() {
buf[i] += d_times_s;
}
}
let i = clamp(x1i-1, width);
if i < buf.len() {
#[allow(exceeding_bitshifts)]
let mut D = x1f<<1 + (1<<(ϕ+2) - fxOneAndAHalf<<1); // D ranges up to ±1<<(1*ϕ+2).
D <<= ϕ; // D ranges up to ±1<<(2*ϕ+2).
D -= x1f_squared; // D ranges up to ±1<<(2*ϕ+3).
D *= d; // D ranges up to ±1<<(3*ϕ+3).
D /= two_over_s;
buf[i] += D as u32;
}
}
let i = clamp(x1i, width);
if i < buf.len() {
// In ideal math: buf[i] += uint32(d * am)
let mut D = x1f_squared; // D ranges up to ±1<<(2*ϕ).
D *= d; // D ranges up to ±1<<(3*ϕ).
D /= two_over_s;
buf[i] += D as u32;
}
}
x = x_next;
y += 1;
}
}
}
/*
fn fixedAccumulateOpOver(dst []uint8, src []uint32) {
// Sanity check that len(dst) >= len(src).
if len(dst) < len(src) {
return
}
acc := int2ϕ(0)
for i, v := range src {
acc += int2ϕ(v)
a := acc
if a < 0 {
a = -a
}
a >>= 2*ϕ - 16
if a > 0xffff {
a = 0xffff
}
// This algorithm comes from the standard library's image/draw package.
dstA := uint32(dst[i]) * 0x101
maskA := uint32(a)
outA := dstA*(0xffff-maskA)/0xffff + maskA
dst[i] = uint8(outA >> 8)
}
}
fn fixedAccumulateOpSrc(dst []uint8, src []uint32) {
// Sanity check that len(dst) >= len(src).
if len(dst) < len(src) {
return
}
acc := int2ϕ(0)
for i, v := range src {
acc += int2ϕ(v)
a := acc
if a < 0 {
a = -a
}
a >>= 2*ϕ - 8
if a > 0xff {
a = 0xff
}
dst[i] = uint8(a)
}
}
*/