use crate::basics::{iround, Rect, POLY_SUBPIXEL_SCALE};
use crate::clip_liang_barsky::{clipping_flags, clipping_flags_y};
use crate::rasterizer_cells_aa::RasterizerCellsAa;
#[inline]
fn upscale(v: f64) -> i32 {
iround(v * POLY_SUBPIXEL_SCALE as f64)
}
#[inline]
fn xi(v: i32) -> i32 {
v
}
#[inline]
fn yi(v: i32) -> i32 {
v
}
#[inline]
fn mul_div(a: i32, b: i32, c: i32) -> i32 {
iround(a as f64 * b as f64 / c as f64)
}
pub struct RasterizerSlClipInt {
clip_box: Rect<i32>,
x1: i32,
y1: i32,
f1: u32,
clipping: bool,
}
impl RasterizerSlClipInt {
pub fn new() -> Self {
Self {
clip_box: Rect::new(0, 0, 0, 0),
x1: 0,
y1: 0,
f1: 0,
clipping: false,
}
}
pub fn reset_clipping(&mut self) {
self.clipping = false;
}
pub fn clip_box(&mut self, x1: i32, y1: i32, x2: i32, y2: i32) {
self.clip_box = Rect::new(x1, y1, x2, y2);
self.clip_box.normalize();
self.clipping = true;
}
pub fn move_to(&mut self, x1: i32, y1: i32) {
self.x1 = x1;
self.y1 = y1;
if self.clipping {
self.f1 = clipping_flags(x1, y1, &self.clip_box);
}
}
pub fn move_to_d(&mut self, x: f64, y: f64) {
self.move_to(upscale(x), upscale(y));
}
pub fn line_to(&mut self, ras: &mut RasterizerCellsAa, x2: i32, y2: i32) {
if self.clipping {
let f2 = clipping_flags(x2, y2, &self.clip_box);
if (self.f1 & 10) == (f2 & 10) && (self.f1 & 10) != 0 {
self.x1 = x2;
self.y1 = y2;
self.f1 = f2;
return;
}
let x1 = self.x1;
let y1 = self.y1;
let f1 = self.f1;
match ((f1 & 5) << 1) | (f2 & 5) {
0 => {
self.line_clip_y(ras, x1, y1, x2, y2, f1, f2);
}
1 => {
let y3 = y1 + mul_div(self.clip_box.x2 - x1, y2 - y1, x2 - x1);
let f3 = clipping_flags_y(y3, &self.clip_box);
self.line_clip_y(ras, x1, y1, self.clip_box.x2, y3, f1, f3);
self.line_clip_y(ras, self.clip_box.x2, y3, self.clip_box.x2, y2, f3, f2);
}
2 => {
let y3 = y1 + mul_div(self.clip_box.x2 - x1, y2 - y1, x2 - x1);
let f3 = clipping_flags_y(y3, &self.clip_box);
self.line_clip_y(ras, self.clip_box.x2, y1, self.clip_box.x2, y3, f1, f3);
self.line_clip_y(ras, self.clip_box.x2, y3, x2, y2, f3, f2);
}
3 => {
self.line_clip_y(ras, self.clip_box.x2, y1, self.clip_box.x2, y2, f1, f2);
}
4 => {
let y3 = y1 + mul_div(self.clip_box.x1 - x1, y2 - y1, x2 - x1);
let f3 = clipping_flags_y(y3, &self.clip_box);
self.line_clip_y(ras, x1, y1, self.clip_box.x1, y3, f1, f3);
self.line_clip_y(ras, self.clip_box.x1, y3, self.clip_box.x1, y2, f3, f2);
}
6 => {
let y3 = y1 + mul_div(self.clip_box.x2 - x1, y2 - y1, x2 - x1);
let y4 = y1 + mul_div(self.clip_box.x1 - x1, y2 - y1, x2 - x1);
let f3 = clipping_flags_y(y3, &self.clip_box);
let f4 = clipping_flags_y(y4, &self.clip_box);
self.line_clip_y(ras, self.clip_box.x2, y1, self.clip_box.x2, y3, f1, f3);
self.line_clip_y(ras, self.clip_box.x2, y3, self.clip_box.x1, y4, f3, f4);
self.line_clip_y(ras, self.clip_box.x1, y4, self.clip_box.x1, y2, f4, f2);
}
8 => {
let y3 = y1 + mul_div(self.clip_box.x1 - x1, y2 - y1, x2 - x1);
let f3 = clipping_flags_y(y3, &self.clip_box);
self.line_clip_y(ras, self.clip_box.x1, y1, self.clip_box.x1, y3, f1, f3);
self.line_clip_y(ras, self.clip_box.x1, y3, x2, y2, f3, f2);
}
9 => {
let y3 = y1 + mul_div(self.clip_box.x1 - x1, y2 - y1, x2 - x1);
let y4 = y1 + mul_div(self.clip_box.x2 - x1, y2 - y1, x2 - x1);
let f3 = clipping_flags_y(y3, &self.clip_box);
let f4 = clipping_flags_y(y4, &self.clip_box);
self.line_clip_y(ras, self.clip_box.x1, y1, self.clip_box.x1, y3, f1, f3);
self.line_clip_y(ras, self.clip_box.x1, y3, self.clip_box.x2, y4, f3, f4);
self.line_clip_y(ras, self.clip_box.x2, y4, self.clip_box.x2, y2, f4, f2);
}
12 => {
self.line_clip_y(ras, self.clip_box.x1, y1, self.clip_box.x1, y2, f1, f2);
}
_ => {
}
}
self.f1 = f2;
} else {
ras.line(xi(self.x1), yi(self.y1), xi(x2), yi(y2));
}
self.x1 = x2;
self.y1 = y2;
}
pub fn line_to_d(&mut self, ras: &mut RasterizerCellsAa, x: f64, y: f64) {
self.line_to(ras, upscale(x), upscale(y));
}
#[allow(clippy::too_many_arguments)]
fn line_clip_y(
&self,
ras: &mut RasterizerCellsAa,
x1: i32,
y1: i32,
x2: i32,
y2: i32,
f1: u32,
f2: u32,
) {
let f1 = f1 & 10;
let f2 = f2 & 10;
if (f1 | f2) == 0 {
ras.line(xi(x1), yi(y1), xi(x2), yi(y2));
} else if f1 != f2 {
let mut tx1 = x1;
let mut ty1 = y1;
let mut tx2 = x2;
let mut ty2 = y2;
if f1 & 8 != 0 {
tx1 = x1 + mul_div(self.clip_box.y1 - y1, x2 - x1, y2 - y1);
ty1 = self.clip_box.y1;
}
if f1 & 2 != 0 {
tx1 = x1 + mul_div(self.clip_box.y2 - y1, x2 - x1, y2 - y1);
ty1 = self.clip_box.y2;
}
if f2 & 8 != 0 {
tx2 = x1 + mul_div(self.clip_box.y1 - y1, x2 - x1, y2 - y1);
ty2 = self.clip_box.y1;
}
if f2 & 2 != 0 {
tx2 = x1 + mul_div(self.clip_box.y2 - y1, x2 - x1, y2 - y1);
ty2 = self.clip_box.y2;
}
ras.line(xi(tx1), yi(ty1), xi(tx2), yi(ty2));
}
}
}
impl Default for RasterizerSlClipInt {
fn default() -> Self {
Self::new()
}
}
pub struct RasterizerSlNoClip {
x1: i32,
y1: i32,
}
impl RasterizerSlNoClip {
pub fn new() -> Self {
Self { x1: 0, y1: 0 }
}
pub fn reset_clipping(&mut self) {}
pub fn clip_box(&mut self, _x1: i32, _y1: i32, _x2: i32, _y2: i32) {}
pub fn move_to(&mut self, x1: i32, y1: i32) {
self.x1 = x1;
self.y1 = y1;
}
pub fn move_to_d(&mut self, x: f64, y: f64) {
self.move_to(upscale(x), upscale(y));
}
pub fn line_to(&mut self, ras: &mut RasterizerCellsAa, x2: i32, y2: i32) {
ras.line(self.x1, self.y1, x2, y2);
self.x1 = x2;
self.y1 = y2;
}
pub fn line_to_d(&mut self, ras: &mut RasterizerCellsAa, x: f64, y: f64) {
self.line_to(ras, upscale(x), upscale(y));
}
}
impl Default for RasterizerSlNoClip {
fn default() -> Self {
Self::new()
}
}
pub fn poly_coord(v: f64) -> i32 {
upscale(v)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::basics::POLY_SUBPIXEL_SCALE;
#[test]
fn test_upscale() {
assert_eq!(upscale(0.0), 0);
assert_eq!(upscale(1.0), POLY_SUBPIXEL_SCALE as i32);
assert_eq!(upscale(10.5), iround(10.5 * POLY_SUBPIXEL_SCALE as f64));
assert_eq!(upscale(-1.0), -(POLY_SUBPIXEL_SCALE as i32));
}
#[test]
fn test_mul_div() {
assert_eq!(mul_div(10, 20, 5), 40);
assert_eq!(mul_div(0, 100, 1), 0);
assert_eq!(mul_div(7, 3, 2), 11); }
#[test]
fn test_clip_int_new() {
let clip = RasterizerSlClipInt::new();
assert!(!clip.clipping);
}
#[test]
fn test_clip_int_no_clip_passthrough() {
let mut clip = RasterizerSlClipInt::new();
let mut ras = RasterizerCellsAa::new();
let s = POLY_SUBPIXEL_SCALE as i32;
clip.move_to(0, 0);
clip.line_to(&mut ras, 10 * s, 10 * s);
ras.sort_cells();
assert!(ras.total_cells() > 0);
}
#[test]
fn test_clip_int_visible_line() {
let mut clip = RasterizerSlClipInt::new();
let s = POLY_SUBPIXEL_SCALE as i32;
clip.clip_box(0, 0, 100 * s, 100 * s);
let mut ras = RasterizerCellsAa::new();
clip.move_to(10 * s, 10 * s);
clip.line_to(&mut ras, 50 * s, 50 * s);
ras.sort_cells();
assert!(ras.total_cells() > 0);
assert!(ras.min_x() >= 10);
assert!(ras.max_x() <= 50);
}
#[test]
fn test_clip_int_fully_clipped_by_y() {
let mut clip = RasterizerSlClipInt::new();
let s = POLY_SUBPIXEL_SCALE as i32;
clip.clip_box(0, 10 * s, 100 * s, 90 * s);
let mut ras = RasterizerCellsAa::new();
clip.move_to(10 * s, 0);
clip.line_to(&mut ras, 50 * s, 5 * s);
ras.sort_cells();
assert_eq!(ras.total_cells(), 0);
}
#[test]
fn test_clip_int_clipped_by_x_right() {
let mut clip = RasterizerSlClipInt::new();
let s = POLY_SUBPIXEL_SCALE as i32;
clip.clip_box(0, 0, 50 * s, 100 * s);
let mut ras = RasterizerCellsAa::new();
clip.move_to(10 * s, 10 * s);
clip.line_to(&mut ras, 80 * s, 80 * s);
ras.sort_cells();
assert!(ras.total_cells() > 0);
for cell in ras.cells() {
assert!(cell.x <= 50, "Cell x={} exceeds clip x2=50", cell.x);
}
}
#[test]
fn test_clip_int_reset_clipping() {
let mut clip = RasterizerSlClipInt::new();
let s = POLY_SUBPIXEL_SCALE as i32;
clip.clip_box(0, 0, 10 * s, 10 * s);
assert!(clip.clipping);
clip.reset_clipping();
assert!(!clip.clipping);
}
#[test]
fn test_clip_int_move_to_d() {
let mut clip = RasterizerSlClipInt::new();
clip.move_to_d(10.5, 20.5);
assert_eq!(clip.x1, upscale(10.5));
assert_eq!(clip.y1, upscale(20.5));
}
#[test]
fn test_clip_int_line_to_d() {
let mut clip = RasterizerSlClipInt::new();
let mut ras = RasterizerCellsAa::new();
clip.move_to_d(0.0, 0.0);
clip.line_to_d(&mut ras, 10.0, 10.0);
ras.sort_cells();
assert!(ras.total_cells() > 0);
}
#[test]
fn test_no_clip_passthrough() {
let mut clip = RasterizerSlNoClip::new();
let mut ras = RasterizerCellsAa::new();
let s = POLY_SUBPIXEL_SCALE as i32;
clip.move_to(0, 0);
clip.line_to(&mut ras, 10 * s, 10 * s);
ras.sort_cells();
assert!(ras.total_cells() > 0);
}
#[test]
fn test_no_clip_double_api() {
let mut clip = RasterizerSlNoClip::new();
let mut ras = RasterizerCellsAa::new();
clip.move_to_d(0.0, 0.0);
clip.line_to_d(&mut ras, 5.0, 5.0);
ras.sort_cells();
assert!(ras.total_cells() > 0);
}
#[test]
fn test_poly_coord() {
assert_eq!(poly_coord(1.0), POLY_SUBPIXEL_SCALE as i32);
assert_eq!(poly_coord(0.0), 0);
}
}