use crate::{
Color, Curve, FillRule, ImageMut, ImageOwned, LinColor, Line, Path, Point, Scalar, Transform,
DEFAULT_FLATNESS, EPSILON, EPSILON_SQRT,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::{cmp::min, collections::VecDeque, fmt, rc::Rc, sync::Arc};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Size {
pub width: usize,
pub height: usize,
}
pub trait Rasterizer {
fn name(&self) -> &str;
fn mask(
&self,
path: &Path,
tr: Transform,
img: &mut dyn ImageMut<Pixel = Scalar>,
fill_rule: FillRule,
);
fn mask_iter(
&self,
path: &Path,
tr: Transform,
size: Size,
fill_rule: FillRule,
) -> Box<dyn Iterator<Item = Pixel> + '_>;
fn fill(
&self,
path: &Path,
tr: Transform,
fill_rule: FillRule,
paint: &dyn Paint,
img: &mut dyn ImageMut<Pixel = LinColor>,
) {
let pixels = self.mask_iter(path, tr, img.size(), fill_rule);
match paint.units() {
None => {
let color = paint.at(Point::new(0.0, 0.0));
fill_impl(pixels, img, |_| color);
}
Some(units) => {
let units_tr = match units {
Units::UserSpaceOnUse => tr * paint.transform(),
Units::BoundingBox => match path.bbox(Transform::identity()) {
None => return,
Some(bbox) => tr * bbox.unit_transform() * paint.transform(),
},
};
if let Some(pixel_tr) = units_tr.invert() {
fill_impl(pixels, img, |pixel| {
let point = Point::new(pixel.x as Scalar + 0.5, pixel.y as Scalar + 0.5);
paint.at(pixel_tr.apply(point))
})
}
}
}
}
}
fn fill_impl(
pixels: impl Iterator<Item = Pixel>,
mut img: impl ImageMut<Pixel = LinColor>,
color_at: impl Fn(Pixel) -> LinColor,
) {
let shape = img.shape();
let data = img.data_mut();
for pixel in pixels {
let dst = &mut data[shape.offset(pixel.y, pixel.x)];
let color = color_at(pixel);
*dst = dst.blend_over(color.with_alpha(pixel.alpha));
}
}
impl<'a, R: Rasterizer + ?Sized> Rasterizer for &'a R {
fn mask(
&self,
path: &Path,
tr: Transform,
img: &mut dyn ImageMut<Pixel = Scalar>,
fill_rule: FillRule,
) {
(**self).mask(path, tr, img, fill_rule)
}
fn mask_iter(
&self,
path: &Path,
tr: Transform,
size: Size,
fill_rule: FillRule,
) -> Box<dyn Iterator<Item = Pixel> + '_> {
(**self).mask_iter(path, tr, size, fill_rule)
}
fn name(&self) -> &str {
(**self).name()
}
}
impl Rasterizer for Box<dyn Rasterizer> {
fn mask(
&self,
path: &Path,
tr: Transform,
img: &mut dyn ImageMut<Pixel = Scalar>,
fill_rule: FillRule,
) {
(**self).mask(path, tr, img, fill_rule)
}
fn mask_iter(
&self,
path: &Path,
tr: Transform,
size: Size,
fill_rule: FillRule,
) -> Box<dyn Iterator<Item = Pixel> + '_> {
(**self).mask_iter(path, tr, size, fill_rule)
}
fn name(&self) -> &str {
(**self).name()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Units {
#[cfg_attr(feature = "serde", serde(rename = "userSpaceOnUse"))]
UserSpaceOnUse,
#[cfg_attr(feature = "serde", serde(rename = "objectBoundingBox"))]
BoundingBox,
}
impl Default for Units {
fn default() -> Self {
Self::UserSpaceOnUse
}
}
pub trait Paint: fmt::Debug {
fn at(&self, point: Point) -> LinColor;
fn units(&self) -> Option<Units>;
fn transform(&self) -> Transform;
#[cfg(feature = "serde")]
fn to_json(&self) -> Result<serde_json::Value, crate::SvgParserError>;
}
impl<'a, P: Paint> Paint for &'a P {
fn at(&self, point: Point) -> LinColor {
(**self).at(point)
}
fn units(&self) -> Option<Units> {
(**self).units()
}
fn transform(&self) -> Transform {
(**self).transform()
}
#[cfg(feature = "serde")]
fn to_json(&self) -> Result<serde_json::Value, crate::SvgParserError> {
(**self).to_json()
}
}
impl Paint for Box<dyn Paint> {
fn at(&self, point: Point) -> LinColor {
(**self).at(point)
}
fn units(&self) -> Option<Units> {
(**self).units()
}
fn transform(&self) -> Transform {
(**self).transform()
}
#[cfg(feature = "serde")]
fn to_json(&self) -> Result<serde_json::Value, crate::SvgParserError> {
(**self).to_json()
}
}
impl Paint for Rc<dyn Paint> {
fn at(&self, point: Point) -> LinColor {
(**self).at(point)
}
fn units(&self) -> Option<Units> {
(**self).units()
}
fn transform(&self) -> Transform {
(**self).transform()
}
#[cfg(feature = "serde")]
fn to_json(&self) -> Result<serde_json::Value, crate::SvgParserError> {
(**self).to_json()
}
}
impl Paint for Arc<dyn Paint> {
fn at(&self, point: Point) -> LinColor {
(**self).at(point)
}
fn units(&self) -> Option<Units> {
(**self).units()
}
fn transform(&self) -> Transform {
(**self).transform()
}
#[cfg(feature = "serde")]
fn to_json(&self) -> Result<serde_json::Value, crate::SvgParserError> {
(**self).to_json()
}
}
pub type ArcPaint = Arc<dyn Paint + Send + Sync + 'static>;
#[derive(Debug, Clone)]
pub struct SignedDifferenceRasterizer {
flatness: Scalar,
}
impl SignedDifferenceRasterizer {
pub fn new(flatness: Scalar) -> Self {
Self { flatness }
}
}
impl Default for SignedDifferenceRasterizer {
fn default() -> Self {
Self {
flatness: DEFAULT_FLATNESS,
}
}
}
impl Rasterizer for SignedDifferenceRasterizer {
fn mask(
&self,
path: &Path,
tr: Transform,
img: &mut dyn ImageMut<Pixel = Scalar>,
fill_rule: FillRule,
) {
let mut img = img.as_mut();
for line in path.flatten(tr, self.flatness, true) {
signed_difference_line(&mut img, line);
}
signed_difference_to_mask(img, fill_rule);
}
fn mask_iter(
&self,
path: &Path,
tr: Transform,
size: Size,
fill_rule: FillRule,
) -> Box<dyn Iterator<Item = Pixel> + '_> {
if size.width == 0 || size.height == 0 {
return Box::new(std::iter::empty());
}
let width = size.width;
let size = Size {
width: width + 1,
height: size.height,
};
let mut img = ImageOwned::new_default(size);
for line in path.flatten(tr, self.flatness, true) {
signed_difference_line(&mut img, line);
}
let mut winding = 0.0;
let iter =
img.into_vec()
.into_iter()
.enumerate()
.filter_map(move |(index, winding_diff)| {
let y = index / size.width;
let x = index - y * size.width;
if x == 0 {
winding = 0.0;
} else if x >= width {
return None;
}
winding += winding_diff;
let alpha = fill_rule.alpha_from_winding(winding);
if alpha.abs() < 1e-6 {
None
} else {
Some(Pixel { x, y, alpha })
}
});
Box::new(iter)
}
fn name(&self) -> &str {
"signed-difference"
}
}
fn signed_difference_line(mut img: impl ImageMut<Pixel = Scalar>, line: Line) {
let Line([p0, p1]) = line;
let width = img.width() as Scalar - 1.0;
let line = if p0.x() > width || p1.x() > width {
if p0.x() > width && p1.x() > width {
Line::new((width - 0.001, p0.y()), (width - 0.001, p1.y()))
} else {
let t = (p0.x() - width) / (p0.x() - p1.x());
let mid = Point::new(width, (1.0 - t) * p0.y() + t * p1.y());
if p0.x() < width {
Line::new(p0, mid)
} else {
Line::new(mid, p1)
}
}
} else {
line
};
let (line, rest) = split_at_zero_x(line);
if let Some(rest) = rest {
signed_difference_line(img.as_mut(), rest);
}
let Line([p0, p1]) = line;
let shape = img.shape();
let data = img.data_mut();
let stride = shape.col_stride;
if (p0.y() - p1.y()).abs() < EPSILON {
return;
}
let (dir, p0, p1) = if p0.y() < p1.y() {
(1.0, p0, p1)
} else {
(-1.0, p1, p0)
};
let dxdy = (p1.x() - p0.x()) / (p1.y() - p0.y());
let y = p0.y().max(0.0) as usize;
let mut x = if p0.y() < 0.0 {
p0.x() - p0.y() * dxdy
} else {
p0.x()
};
let mut x_next = x;
for y in y..min(shape.height, p1.y().ceil().max(0.0) as usize) {
x = x_next;
let row_offset = shape.offset(y, 0); let dy = ((y + 1) as Scalar).min(p1.y()) - (y as Scalar).max(p0.y());
let d = dir * dy;
x_next = x + dxdy * dy;
let (x0, x1) = if x < x_next { (x, x_next) } else { (x_next, x) };
let x0_floor = x0.floor().max(0.0);
let x0i = x0_floor as i32;
let x1_ceil = x1.ceil().min(width);
let x1i = x1_ceil as i32;
if x1i <= x0i + 1 {
let xmf = 0.5 * (x + x_next) - x0_floor; data[row_offset + (x0i as usize) * stride] += d * (1.0 - xmf);
let offset = row_offset + ((x0i + 1) as usize) * stride;
if crate::utils::likely(offset < data.len()) {
data[offset] += d * xmf;
}
} else {
let s = (x1 - x0).recip();
let x0f = x0 - x0_floor; let x1f = x1 - x1_ceil + 1.0; let a0 = 0.5 * s * (1.0 - x0f) * (1.0 - x0f); let am = 0.5 * s * x1f * x1f; data[row_offset + (x0i as usize) * stride] += d * a0;
if x1i == x0i + 2 {
data[row_offset + ((x0i + 1) as usize) * stride] += d * (1.0 - a0 - am);
} else {
let a1 = s * (1.5 - x0f);
data[row_offset + ((x0i + 1) as usize) * stride] += d * (a1 - a0);
for xi in x0i + 2..x1i - 1 {
data[row_offset + (xi as usize) * stride] += d * s;
}
let a2 = a1 + (x1i - x0i - 3) as Scalar * s;
data[row_offset + ((x1i - 1) as usize) * stride] += d * (1.0 - a2 - am);
}
data[row_offset + (x1i as usize) * stride] += d * am
}
}
}
fn signed_difference_to_mask(mut img: impl ImageMut<Pixel = Scalar>, fill_rule: FillRule) {
let shape = img.shape();
let data = img.data_mut();
match fill_rule {
FillRule::NonZero => {
for y in 0..shape.height {
let mut acc = 0.0;
for x in 0..shape.width {
let offset = shape.offset(y, x);
acc += data[offset];
let value = acc.abs();
data[offset] = if value > 1.0 {
1.0
} else if value < 1e-6 {
0.0
} else {
value
};
}
}
}
FillRule::EvenOdd => {
for y in 0..shape.height {
let mut acc = 0.0;
for x in 0..shape.width {
let offset = shape.offset(y, x);
acc += data[offset];
data[offset] = ((acc + 1.0).rem_euclid(2.0) - 1.0).abs()
}
}
}
}
}
#[derive(Debug, Clone)]
pub struct ActiveEdgeRasterizer {
flatness: Scalar,
}
impl ActiveEdgeRasterizer {
pub fn new(flatness: Scalar) -> Self {
Self { flatness }
}
}
impl Default for ActiveEdgeRasterizer {
fn default() -> Self {
Self {
flatness: DEFAULT_FLATNESS,
}
}
}
impl Rasterizer for ActiveEdgeRasterizer {
fn mask(
&self,
path: &Path,
tr: Transform,
img: &mut dyn ImageMut<Pixel = Scalar>,
fill_rule: FillRule,
) {
let shape = img.shape();
let data = img.data_mut();
for pixel in ActiveEdgeIter::new(
Size {
width: shape.width,
height: shape.height,
},
fill_rule,
path.flatten(tr, self.flatness, true),
) {
data[shape.offset(pixel.y, pixel.x)] = pixel.alpha;
}
}
fn mask_iter(
&self,
path: &Path,
tr: Transform,
size: Size,
fill_rule: FillRule,
) -> Box<dyn Iterator<Item = Pixel> + '_> {
Box::new(ActiveEdgeIter::new(
size,
fill_rule,
path.flatten(tr, self.flatness, true),
))
}
fn name(&self) -> &str {
"active-edge"
}
}
pub struct ActiveEdgeIter {
edge_inactive: Vec<Vec<Edge>>,
edge_active: VecDeque<Edge>,
iters_inactive: Vec<EdgeRowIter>,
iters_active: EdgeAccIter,
winding: Scalar,
fill_rule: FillRule,
size: Size,
column: usize,
row: usize,
}
impl ActiveEdgeIter {
pub fn new(size: Size, fill_rule: FillRule, lines: impl Iterator<Item = Line>) -> Self {
let mut edge_table: Vec<Vec<Edge>> = Vec::new();
edge_table.resize_with(size.height, Default::default);
for line in lines.flat_map(|line| {
let (line, rest) = split_at_zero_x(line);
std::iter::once(line).chain(rest)
}) {
if let Some(edge) = Edge::new(line) {
if edge.row >= size.height {
continue;
}
edge_table[size.height - edge.row - 1].push(edge);
}
}
let mut this = Self {
edge_inactive: edge_table,
edge_active: Default::default(),
iters_inactive: Default::default(),
iters_active: EdgeAccIter::new(),
winding: 0.0,
fill_rule,
size,
column: 0,
row: 0,
};
this.next_row();
this
}
fn next_row(&mut self) {
self.iters_inactive.clear();
self.iters_active.clear();
for _ in 0..self.edge_active.len() {
if let Some(edge) = self.edge_active.pop_front() {
if let Some((edge_rest, iter)) = edge.next_row() {
self.iters_inactive.push(iter);
self.edge_active.push_back(edge_rest);
}
}
}
if let Some(edges) = self.edge_inactive.pop() {
for edge in edges {
if let Some((edge, iter)) = edge.next_row() {
self.iters_inactive.push(iter);
self.edge_active.push_back(edge);
}
}
}
self.iters_inactive
.sort_unstable_by(|a, b| b.column.cmp(&a.column));
}
}
#[derive(Debug, Clone, Copy)]
pub struct Pixel {
pub x: usize,
pub y: usize,
pub alpha: Scalar,
}
impl Iterator for ActiveEdgeIter {
type Item = Pixel;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.row >= self.size.height {
return None;
}
while let Some(iter) = self.iters_inactive.pop() {
if iter.column <= self.column {
self.iters_active.push(iter);
} else {
self.iters_inactive.push(iter);
break;
}
}
match self.iters_active.next() {
Some(winding_delta) => self.winding += winding_delta,
None if self.winding.abs() < EPSILON_SQRT => {
match self.iters_inactive.last() {
Some(iter) if iter.column < self.size.width => {
self.column = iter.column;
}
_ => {
self.column = 0;
self.row += 1;
self.winding = 0.0;
self.next_row();
}
}
continue;
}
None => {}
}
let pixel = Pixel {
x: self.column,
y: self.row,
alpha: self.fill_rule.alpha_from_winding(self.winding),
};
self.column += 1;
if self.column >= self.size.width {
self.column = 0;
self.row += 1;
self.winding = 0.0;
self.next_row();
}
return Some(pixel);
}
}
}
#[inline]
fn next_ceil(value: Scalar) -> Scalar {
value.floor() + 1.0
}
#[derive(Debug)]
struct Edge {
line: Line,
dxdy: Scalar,
dydx: Option<Scalar>,
dir: Scalar,
row: usize,
}
impl Edge {
fn new(line: Line) -> Option<Self> {
let Line([p0, p1]) = line;
if (p0.y() - p1.y()).abs() < EPSILON {
return None;
}
let (dir, p0, p1) = if p0.y() <= p1.y() {
(1.0, p0, p1)
} else {
(-1.0, p1, p0)
};
let dx = p1.x() - p0.x();
let dy = p1.y() - p0.y();
let dxdy = dx / dy;
let dydx = (dxdy.abs() > EPSILON_SQRT).then(|| dy / dx);
let p0 = if p0.y() < 0.0 {
Point::new(p0.x() - p0.y() * dxdy, 0.0)
} else {
p0
};
Some(Self {
line: Line::new(p0, p1),
dxdy,
dydx,
dir,
row: p0.y().floor() as usize,
})
}
fn next_row(self) -> Option<(Edge, EdgeRowIter)> {
EdgeRowIter::new(self)
}
}
#[derive(Debug)]
struct EdgeRowIter {
line: Option<Line>,
reminder: Option<Scalar>,
dydx: Option<Scalar>,
column: usize,
dir: Scalar,
}
impl EdgeRowIter {
fn new(mut edge: Edge) -> Option<(Edge, Self)> {
let Line([p0, p1]) = edge.line;
if p1.y() - p0.y() < EPSILON {
return None;
}
let y_split = next_ceil(p0.y()).min(p1.y());
let x_split = p0.x() + edge.dxdy * (y_split - p0.y());
let p_split = Point::new(x_split, y_split);
edge.line = Line::new(p_split, p1);
let (dir, line) = if p0.x() <= p_split.x() {
(edge.dir, Line::new(p0, p_split))
} else {
(-edge.dir, Line::new(p_split, p0))
};
let iter = Self {
line: Some(line),
reminder: None,
dydx: edge.dydx,
column: line.start().x() as usize,
dir,
};
Some((edge, iter))
}
}
impl Iterator for EdgeRowIter {
type Item = Scalar;
fn next(&mut self) -> Option<Self::Item> {
if let Some(Line([p0, p1])) = self.line.take() {
let x_ceil = next_ceil(p0.x());
let x = x_ceil.min(p1.x());
let h = match self.dydx {
Some(dydx) => dydx * (x - p0.x()),
None => next_ceil(p0.y()).min(p1.y()) - p0.y(),
};
let y = p0.y() + h;
let s0 = (2.0 * x_ceil - x - p0.x()) * h / 2.0;
let s1 = h - s0;
if p1.x() > x {
self.line = Some(Line::new((x, y), p1));
}
let s_prev = self.reminder.replace(self.dir * s1).unwrap_or(0.0);
Some(self.dir * s0 + s_prev)
} else {
self.reminder.take()
}
}
}
struct EdgeAccIter {
iters: VecDeque<EdgeRowIter>,
}
impl EdgeAccIter {
fn new() -> Self {
Self {
iters: Default::default(),
}
}
fn push(&mut self, iter: EdgeRowIter) {
self.iters.push_back(iter)
}
fn clear(&mut self) {
self.iters.clear()
}
}
impl Iterator for EdgeAccIter {
type Item = Scalar;
fn next(&mut self) -> Option<Self::Item> {
if self.iters.is_empty() {
return None;
}
let mut acc = 0.0;
for _ in 0..self.iters.len() {
if let Some(mut iter) = self.iters.pop_front() {
if let Some(value) = iter.next() {
acc += value;
self.iters.push_back(iter);
}
}
}
Some(acc)
}
}
fn split_at_zero_x(line: Line) -> (Line, Option<Line>) {
let Line([p0, p1]) = line;
if p0.x() >= 0.0 && p1.x() >= 0.0 {
return (line, None);
}
if p0.x() <= 0.0 && p1.x() <= 0.0 {
return (Line::new((0.0, p0.y()), (0.0, p1.y())), None);
}
let mid = line.at(p0.x() / (p0.x() - p1.x()));
if p0.x() < 0.0 {
(Line::new(mid, p1), Some(Line::new((0.0, p0.y()), mid)))
} else {
(Line::new(p0, mid), Some(Line::new(mid, (0.0, p1.y()))))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{assert_approx_eq, Image};
type Error = Box<dyn std::error::Error>;
#[test]
fn test_signed_difference_line() {
let mut img = ImageOwned::new_default(Size {
height: 2,
width: 5,
});
signed_difference_line(&mut img, Line::new((0.5, 1.0), (3.5, 0.0)));
let a0 = (0.5 * (1.0 / 6.0)) / 2.0;
let a1 = ((1.0 / 6.0) + (3.0 / 6.0)) / 2.0;
let a2 = ((3.0 / 6.0) + (5.0 / 6.0)) / 2.0;
assert_approx_eq!(*img.get(0, 0).unwrap(), -a0);
assert_approx_eq!(*img.get(0, 1).unwrap(), a0 - a1);
assert_approx_eq!(*img.get(0, 2).unwrap(), a1 - a2);
assert_approx_eq!(*img.get(0, 3).unwrap(), a0 - a1);
assert_approx_eq!(*img.get(0, 4).unwrap(), -a0);
let a: Scalar = img.iter().sum();
assert_approx_eq!(a, -1.0);
img.clear();
signed_difference_line(&mut img, Line::new((-1.0, 0.0), (1.0, 1.0)));
assert_approx_eq!(*img.get(0, 0).unwrap(), 3.0 / 4.0);
assert_approx_eq!(*img.get(0, 1).unwrap(), 1.0 / 4.0);
img.clear();
signed_difference_line(&mut img, Line::new((0.0, -0.5), (2.0, 1.5)));
assert_approx_eq!(*img.get(0, 0).unwrap(), 1.0 / 8.0);
assert_approx_eq!(*img.get(0, 1).unwrap(), 1.0 - 2.0 / 8.0);
assert_approx_eq!(*img.get(0, 2).unwrap(), 1.0 / 8.0);
assert_approx_eq!(*img.get(1, 1).unwrap(), 1.0 / 8.0);
assert_approx_eq!(*img.get(1, 2).unwrap(), 0.5 - 1.0 / 8.0);
img.clear();
signed_difference_line(&mut img, Line::new((0.1, 0.1), (1.9, 0.9)));
assert_approx_eq!(*img.get(0, 0).unwrap(), 0.18);
assert_approx_eq!(*img.get(0, 1).unwrap(), 0.44);
assert_approx_eq!(*img.get(0, 2).unwrap(), 0.18);
img.clear();
signed_difference_line(&mut img, Line::new((0.1, 0.1), (0.9, 0.9)));
assert_approx_eq!(*img.get(0, 0).unwrap(), 0.4);
assert_approx_eq!(*img.get(0, 1).unwrap(), 0.8 - 0.4);
img.clear();
signed_difference_line(&mut img, Line::new((0.5, 0.5), (0.5, 1.75)));
assert_approx_eq!(*img.get(0, 0).unwrap(), 1.0 / 4.0);
assert_approx_eq!(*img.get(0, 1).unwrap(), 1.0 / 4.0);
assert_approx_eq!(*img.get(1, 0).unwrap(), 3.0 / 8.0);
assert_approx_eq!(*img.get(1, 1).unwrap(), 3.0 / 8.0);
img.clear();
}
#[test]
fn test_edge_iter() {
let line = Line::new((0.0, 0.0), (6.0, 2.0));
let edge = Edge::new(line).unwrap();
assert_eq!(edge.row, 0);
let (edge, mut iter) = edge.next_row().unwrap();
assert_eq!(iter.column, 0);
assert_approx_eq!(iter.next().unwrap(), 1.0 / 6.0);
assert_approx_eq!(iter.next().unwrap(), 2.0 / 6.0);
assert_approx_eq!(iter.next().unwrap(), 2.0 / 6.0);
assert_approx_eq!(iter.next().unwrap(), 1.0 / 6.0);
assert!(iter.next().is_none());
let (edge, iter) = edge.next_row().unwrap();
assert_eq!(iter.column, 3);
assert_approx_eq!(iter.sum::<Scalar>(), 1.0);
assert!(edge.next_row().is_none());
let line = Line::new((1.0, 1.0), (4.0, 0.0));
let edge = Edge::new(line).unwrap();
assert_eq!(edge.row, 0);
let (edge, mut iter) = edge.next_row().unwrap();
assert_eq!(iter.column, 1);
assert_approx_eq!(iter.next().unwrap(), -1.0 / 6.0);
assert_approx_eq!(iter.next().unwrap(), -2.0 / 6.0);
assert_approx_eq!(iter.next().unwrap(), -2.0 / 6.0);
assert_approx_eq!(iter.next().unwrap(), -1.0 / 6.0);
assert!(edge.next_row().is_none());
let line = Line::new((0.5, 0.5), (0.5, 1.5));
let edge = Edge::new(line).unwrap();
assert_eq!(edge.row, 0);
let (edge, mut iter) = edge.next_row().unwrap();
assert_eq!(iter.column, 0);
assert_approx_eq!(iter.next().unwrap(), 1.0 / 4.0);
assert_approx_eq!(iter.next().unwrap(), 1.0 / 4.0);
assert!(iter.next().is_none());
let (edge, iter) = edge.next_row().unwrap();
assert_approx_eq!(iter.sum::<Scalar>(), 0.5);
assert!(edge.next_row().is_none());
let line = Line::new((0.5, 0.2), (0.5 + EPSILON_SQRT, 0.7));
let edge = Edge::new(line).unwrap();
assert_eq!(edge.row, 0);
let (edge, mut iter) = edge.next_row().unwrap();
assert_eq!(iter.column, 0);
assert_approx_eq!(iter.next().unwrap(), 1.0 / 4.0, 1e-6);
assert_approx_eq!(iter.next().unwrap(), 1.0 / 4.0, 1e-6);
assert!(iter.next().is_none());
assert!(edge.next_row().is_none());
}
fn test_rasterizer(rasterizer: &dyn Rasterizer) -> Result<(), Error> {
#[rustfmt::skip]
let expected = [
0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 0.0, 0.0, 0.0, 0.25, 0.75, 1.0, 0.75, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, ];
let path = Path::builder()
.move_to((1.0, 0.0))
.line_to((1.0, 1.0))
.line_to((2.0, 2.0))
.line_to((4.0, 3.0))
.line_to((5.0, 3.0))
.line_to((7.0, 2.0))
.line_to((8.0, 1.0))
.line_to((8.0, 0.0))
.close()
.build();
let mut img = ImageOwned::new_default(Size {
width: 9,
height: 4,
});
rasterizer.mask(&path, Transform::identity(), &mut img, FillRule::EvenOdd);
for (expected, pixel) in expected.iter().zip(img.data()) {
assert_approx_eq!(expected, pixel);
}
let path: Path = "M1,1 v2 h1 v-2 Z".parse()?;
let mut img = ImageOwned::new_default(Size {
width: 3,
height: 3,
});
rasterizer.mask(&path, Transform::identity(), &mut img, FillRule::EvenOdd);
{
let file = std::fs::File::create("/tmp/bla.bmp")?;
img.write_bmp(file)?;
}
Ok(())
}
#[test]
fn test_active_edge_rasterizer() -> Result<(), Error> {
test_rasterizer(&ActiveEdgeRasterizer::default())
}
#[test]
fn test_sigend_difference_rasterizer() -> Result<(), Error> {
test_rasterizer(&SignedDifferenceRasterizer::default())
}
#[test]
fn test_fill_rule() {
let tr = Transform::identity();
let path: Path = r#"
M50,0 21,90 98,35 2,35 79,90z
M110,0 h90 v90 h-90z
M130,20 h50 v50 h-50 z
M210,0 h90 v90 h-90 z
M230,20 v50 h50 v-50 z
"#
.parse()
.expect("failed to parse path");
let (size, tr, _) = path.size(tr).expect("path is empty");
let mut img = ImageOwned::new_default(size);
let y = 50;
let x0 = 50; let x1 = 150; let x2 = 250;
let r0: Box<dyn Rasterizer> = Box::new(SignedDifferenceRasterizer::default());
let r1: Box<dyn Rasterizer> = Box::new(ActiveEdgeRasterizer::default());
for rasterizer in &[r0, r1] {
img.clear();
path.mask(&rasterizer, tr, FillRule::EvenOdd, &mut img);
assert_approx_eq!(img.get(y, x0).unwrap(), 0.0, 1e-6);
assert_approx_eq!(img.get(y, x1).unwrap(), 0.0, 1e-6);
assert_approx_eq!(img.get(y, x2).unwrap(), 0.0, 1e-6);
let area = img.iter().sum::<Scalar>();
assert_approx_eq!(area, 13130.0, 1.0);
img.clear();
path.mask(&rasterizer, tr, FillRule::NonZero, &mut img);
assert_approx_eq!(img.get(y, x0).unwrap(), 1.0, 1e-6);
assert_approx_eq!(img.get(y, x1).unwrap(), 1.0, 1e-6);
assert_approx_eq!(img.get(y, x2).unwrap(), 0.0, 1e-6);
let area = img.iter().sum::<Scalar>();
assert_approx_eq!(area, 16492.5, 1.0);
}
}
}