diff --git a/canvas/src/lib.rs b/canvas/src/lib.rs index dde559f4..89ec7a50 100644 --- a/canvas/src/lib.rs +++ b/canvas/src/lib.rs @@ -44,6 +44,9 @@ const DEFAULT_FONT_SIZE: f32 = 10.0; #[cfg_attr(not(feature = "pf-text"), path = "text_no_text.rs")] mod text; +#[cfg(test)] +mod tests; + pub struct CanvasRenderingContext2D { scene: Scene, current_state: State, diff --git a/canvas/src/tests.rs b/canvas/src/tests.rs new file mode 100644 index 00000000..56b3c85b --- /dev/null +++ b/canvas/src/tests.rs @@ -0,0 +1,19 @@ +// pathfinder/canvas/src/tests.rs +// +// For this file only, any copyright is dedicated to the Public Domain. +// https://creativecommons.org/publicdomain/zero/1.0/ + +use pathfinder_geometry::vector::Vector2F; +use super::Path2D; + +#[test] +pub fn test_path2d_formatting() { + let mut path = Path2D::new(); + path.move_to(Vector2F::new(0.0, 1.0)); + path.line_to(Vector2F::new(2.0, 3.0)); + assert_eq!(format!("{:?}", path), "M 0 1 L 2 3"); + path.line_to(Vector2F::new(4.0, 5.0)); + assert_eq!(format!("{:?}", path), "M 0 1 L 2 3 L 4 5"); + path.close_path(); + assert_eq!(format!("{:?}", path), "M 0 1 L 2 3 L 4 5 z"); +} diff --git a/content/src/clip.rs b/content/src/clip.rs index fc4a0146..c5d4aa70 100644 --- a/content/src/clip.rs +++ b/content/src/clip.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::outline::{Contour, PointFlags, PushSegmentFlags}; +use crate::outline::{Contour, ContourIterFlags, PointFlags, PushSegmentFlags}; use crate::segment::{CubicSegment, Segment}; use arrayvec::ArrayVec; use pathfinder_geometry::line_segment::LineSegment2F; @@ -200,7 +200,7 @@ where } let input = self.contour_mut().take(); - for segment in input.iter() { + for segment in input.iter(ContourIterFlags::empty()) { self.clip_segment_against(segment, &edge); } if input.is_closed() { @@ -266,7 +266,7 @@ where fn check_for_fast_clip(&mut self, edge: &Self::Edge) -> FastClipResult { let mut result = None; - for segment in self.contour_mut().iter() { + for segment in self.contour_mut().iter(ContourIterFlags::empty()) { let location = edge.trivially_test_segment(&segment); match (result, location) { (None, EdgeRelativeLocation::Outside) => { diff --git a/content/src/dash.rs b/content/src/dash.rs index d25f70e0..9c820a97 100644 --- a/content/src/dash.rs +++ b/content/src/dash.rs @@ -10,7 +10,7 @@ //! Line dashing support. -use crate::outline::{Contour, Outline, PushSegmentFlags}; +use crate::outline::{Contour, ContourIterFlags, Outline, PushSegmentFlags}; use std::mem; const EPSILON: f32 = 0.0001; @@ -54,7 +54,8 @@ impl<'a, 'b, 'c> ContourDash<'a, 'b, 'c> { } fn dash(&mut self) { - let (mut iterator, mut queued_segment) = (self.input.iter(), None); + let mut iterator = self.input.iter(ContourIterFlags::empty()); + let mut queued_segment = None; loop { if queued_segment.is_none() { match iterator.next() { diff --git a/content/src/outline.rs b/content/src/outline.rs index 74a20df2..e9a6d4eb 100644 --- a/content/src/outline.rs +++ b/content/src/outline.rs @@ -282,10 +282,11 @@ impl Contour { } #[inline] - pub fn iter(&self) -> ContourIter { + pub fn iter(&self, flags: ContourIterFlags) -> ContourIter { ContourIter { contour: self, index: 1, + flags, } } @@ -743,7 +744,8 @@ impl Contour { impl Debug for Contour { fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { - for (segment_index, segment) in self.iter().enumerate() { + for (segment_index, segment) in self.iter(ContourIterFlags::IGNORE_CLOSE_SEGMENT) + .enumerate() { if segment_index == 0 { write!( formatter, @@ -821,6 +823,7 @@ impl PointIndex { pub struct ContourIter<'a> { contour: &'a Contour, index: u32, + flags: ContourIterFlags, } impl<'a> Iterator for ContourIter<'a> { @@ -829,8 +832,11 @@ impl<'a> Iterator for ContourIter<'a> { #[inline] fn next(&mut self) -> Option { let contour = self.contour; - if (self.index == contour.len() && !self.contour.closed) || self.index == contour.len() + 1 - { + + let include_close_segment = self.contour.closed && + !self.flags.contains(ContourIterFlags::IGNORE_CLOSE_SEGMENT); + if (self.index == contour.len() && !include_close_segment) || + self.index == contour.len() + 1 { return None; } @@ -873,6 +879,12 @@ pub enum ArcDirection { CCW, } +bitflags! { + pub struct ContourIterFlags: u8 { + const IGNORE_CLOSE_SEGMENT = 1; + } +} + #[inline] pub(crate) fn union_rect(bounds: &mut RectF, new_point: Vector2F, first: bool) { if first { diff --git a/content/src/stroke.rs b/content/src/stroke.rs index cd9e6034..46b1b3f8 100644 --- a/content/src/stroke.rs +++ b/content/src/stroke.rs @@ -10,7 +10,7 @@ //! Utilities for converting path strokes to fills. -use crate::outline::{ArcDirection, Contour, Outline, PushSegmentFlags}; +use crate::outline::{ArcDirection, Contour, ContourIterFlags, Outline, PushSegmentFlags}; use crate::segment::Segment; use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::rect::RectF; @@ -161,7 +161,7 @@ impl<'a> ContourStrokeToFill<'a> { } fn offset_forward(&mut self) { - for (segment_index, segment) in self.input.iter().enumerate() { + for (segment_index, segment) in self.input.iter(ContourIterFlags::empty()).enumerate() { // FIXME(pcwalton): We negate the radius here so that round end caps can be drawn // clockwise. Of course, we should just implement anticlockwise arcs to begin with... let join = if segment_index == 0 { LineJoin::Bevel } else { self.join }; @@ -172,7 +172,7 @@ impl<'a> ContourStrokeToFill<'a> { fn offset_backward(&mut self) { let mut segments: Vec<_> = self .input - .iter() + .iter(ContourIterFlags::empty()) .map(|segment| segment.reversed()) .collect(); segments.reverse(); diff --git a/export/src/lib.rs b/export/src/lib.rs index 6422662d..0ab54d49 100644 --- a/export/src/lib.rs +++ b/export/src/lib.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use pathfinder_content::outline::ContourIterFlags; use pathfinder_content::segment::SegmentKind; use pathfinder_renderer::paint::Paint; use pathfinder_renderer::scene::Scene; @@ -87,7 +88,7 @@ fn export_pdf(scene: &Scene, writer: &mut W) -> io::Result<()> { } for contour in outline.contours() { - for (segment_index, segment) in contour.iter().enumerate() { + for (segment_index, segment) in contour.iter(ContourIterFlags::empty()).enumerate() { if segment_index == 0 { pdf.move_to(tr(segment.baseline.from())); } @@ -103,7 +104,11 @@ fn export_pdf(scene: &Scene, writer: &mut W) -> io::Result<()> { let c2 = Vector2F::splat(2./3.) * c + Vector2F::splat(1./3.) * p; pdf.cubic_to(c1, c2, p); } - SegmentKind::Cubic => pdf.cubic_to(tr(segment.ctrl.from()), tr(segment.ctrl.to()), tr(segment.baseline.to())) + SegmentKind::Cubic => { + pdf.cubic_to(tr(segment.ctrl.from()), + tr(segment.ctrl.to()), + tr(segment.baseline.to())) + } } } @@ -147,7 +152,7 @@ fn export_ps(scene: &Scene, writer: &mut W) -> io::Result<()> { } for contour in outline.contours() { - for (segment_index, segment) in contour.iter().enumerate() { + for (segment_index, segment) in contour.iter(ContourIterFlags::empty()).enumerate() { if segment_index == 0 { writeln!(writer, "{} moveto", P(segment.baseline.from()))?; }