Document the rest of `pathfinder_content`

This commit is contained in:
Patrick Walton 2020-07-29 12:17:03 -07:00
parent bad735e88d
commit 4060baa80c
9 changed files with 211 additions and 23 deletions

View File

@ -55,6 +55,7 @@ pub enum Filter {
uv_origin: Vector2F, uv_origin: Vector2F,
}, },
/// One of the `PatternFilter` filters.
PatternFilter(PatternFilter), PatternFilter(PatternFilter),
} }
@ -80,7 +81,9 @@ pub enum PatternFilter {
/// To produce a full Gaussian blur, perform two successive blur operations, one in each /// To produce a full Gaussian blur, perform two successive blur operations, one in each
/// direction. /// direction.
Blur { Blur {
/// The axis of the blur: horizontal or vertical.
direction: BlurDirection, direction: BlurDirection,
/// Half the blur radius.
sigma: f32, sigma: f32,
}, },
@ -95,43 +98,86 @@ pub enum PatternFilter {
#[derive(Clone, Copy, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
pub enum BlendMode { pub enum BlendMode {
// Porter-Duff, supported by GPU blender // Porter-Duff, supported by GPU blender
/// No regions are enabled.
Clear, Clear,
/// Only the source will be present.
Copy, Copy,
/// The source that overlaps the destination, replaces the destination.
SrcIn, SrcIn,
/// Source is placed, where it falls outside of the destination.
SrcOut, SrcOut,
/// Source is placed over the destination.
SrcOver, SrcOver,
/// Source which overlaps the destination, replaces the destination. Destination is placed
/// elsewhere.
SrcAtop, SrcAtop,
/// Destination which overlaps the source, replaces the source.
DestIn, DestIn,
/// Destination is placed, where it falls outside of the source.
DestOut, DestOut,
/// Destination is placed over the source.
DestOver, DestOver,
/// Destination which overlaps the source replaces the source. Source is placed elsewhere.
DestAtop, DestAtop,
/// The non-overlapping regions of source and destination are combined.
Xor, Xor,
/// Display the sum of the source image and destination image. It is defined in the Porter-Duff
/// paper as the plus operator.
Lighter, Lighter,
// Others, unsupported by GPU blender // Others, unsupported by GPU blender
/// Selects the darker of the backdrop and source colors.
Darken, Darken,
/// Selects the lighter of the backdrop and source colors.
Lighten, Lighten,
/// The source color is multiplied by the destination color and replaces the destination.
Multiply, Multiply,
/// Multiplies the complements of the backdrop and source color values, then complements the
/// result.
Screen, Screen,
/// Multiplies or screens the colors, depending on the source color value. The effect is
/// similar to shining a harsh spotlight on the backdrop.
HardLight, HardLight,
/// Multiplies or screens the colors, depending on the backdrop color value.
Overlay, Overlay,
/// Brightens the backdrop color to reflect the source color.
ColorDodge, ColorDodge,
/// Darkens the backdrop color to reflect the source color.
ColorBurn, ColorBurn,
/// Darkens or lightens the colors, depending on the source color value. The effect is similar
/// to shining a diffused spotlight on the backdrop.
SoftLight, SoftLight,
/// Subtracts the darker of the two constituent colors from the lighter color.
Difference, Difference,
/// Produces an effect similar to that of the Difference mode but lower in contrast.
Exclusion, Exclusion,
/// Creates a color with the hue of the source color and the saturation and luminosity of the
/// backdrop color.
Hue, Hue,
/// Creates a color with the saturation of the source color and the hue and luminosity of the
/// backdrop color.
Saturation, Saturation,
/// Creates a color with the hue and saturation of the source color and the luminosity of the
/// backdrop color.
Color, Color,
/// Creates a color with the luminosity of the source color and the hue and saturation of the
/// backdrop color. This produces an inverse effect to that of the Color mode.
Luminosity, Luminosity,
} }
/// The convolution kernel that will be applied horizontally to reduce color fringes when
/// performing subpixel antialiasing. This kernel is automatically mirrored horizontally. The
/// fourth element of this kernel is applied to the center of the pixel, the third element is
/// applied one pixel to the left, and so on.
#[derive(Clone, Copy, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
pub struct DefringingKernel(pub [f32; 4]); pub struct DefringingKernel(pub [f32; 4]);
/// The axis a Gaussian blur is applied to.
#[derive(Clone, Copy, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
pub enum BlurDirection { pub enum BlurDirection {
/// The horizontal axis.
X, X,
/// The vertical axis.
Y, Y,
} }

View File

@ -22,27 +22,45 @@ use std::convert;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::mem; use std::mem;
/// A gradient, either linear or radial.
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub struct Gradient { pub struct Gradient {
/// Information specific to the type of gradient (linear or radial).
pub geometry: GradientGeometry, pub geometry: GradientGeometry,
stops: Vec<ColorStop>, stops: Vec<ColorStop>,
/// What should be rendered upon reaching the end of the color stops.
pub wrap: GradientWrap, pub wrap: GradientWrap,
} }
/// A color in a gradient. Points in a gradient between two stops interpolate linearly between the
/// stops.
#[derive(Clone, Copy, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
pub struct ColorStop { pub struct ColorStop {
/// The offset of the color stop, between 0.0 and 1.0 inclusive. The value 0.0 represents the
/// start of the gradient, and 1.0 represents the end.
pub offset: f32, pub offset: f32,
/// The color of the gradient stop.
pub color: ColorU, pub color: ColorU,
} }
/// The type of gradient: linear or radial.
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub enum GradientGeometry { pub enum GradientGeometry {
/// A linear gradient that follows a line.
///
/// The line is in scene coordinates, not relative to the bounding box of the path.
Linear(LineSegment2F), Linear(LineSegment2F),
/// A radial gradient that radiates outward from a line connecting two circles (or from one
/// circle).
Radial { Radial {
/// The line that connects the two circles. It may have zero length for simple radial /// The line that connects the centers of the two circles. For single-circle radial
/// gradients. /// gradients (the common case), this line has zero length, with start point and endpoint
/// both at the circle's center point.
///
/// This is in scene coordinates, not relative to the bounding box of the path.
line: LineSegment2F, line: LineSegment2F,
/// The radii of the two circles. The first value may be zero. /// The radii of the two circles. The first value may be zero to start the gradient at the
/// center of the circle.
radii: F32x2, radii: F32x2,
/// Transform from radial gradient space into screen space. /// Transform from radial gradient space into screen space.
/// ///
@ -52,9 +70,13 @@ pub enum GradientGeometry {
} }
} }
/// What should be rendered outside the color stops.
#[derive(Clone, Copy, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
pub enum GradientWrap { pub enum GradientWrap {
/// The area before the gradient is filled with the color of the first stop, and the area after
/// the gradient is filled with the color of the last stop.
Clamp, Clamp,
/// The gradient repeats indefinitely.
Repeat, Repeat,
} }
@ -97,6 +119,9 @@ impl Hash for ColorStop {
} }
impl Gradient { impl Gradient {
/// Creates a new linear gradient with the given line.
///
/// The line is in scene coordinates, not relative to the bounding box of the current path.
#[inline] #[inline]
pub fn linear(line: LineSegment2F) -> Gradient { pub fn linear(line: LineSegment2F) -> Gradient {
Gradient { Gradient {
@ -106,11 +131,19 @@ impl Gradient {
} }
} }
/// A convenience method equivalent to `Gradient::linear(LineSegment2F::new(from, to))`.
#[inline] #[inline]
pub fn linear_from_points(from: Vector2F, to: Vector2F) -> Gradient { pub fn linear_from_points(from: Vector2F, to: Vector2F) -> Gradient {
Gradient::linear(LineSegment2F::new(from, to)) Gradient::linear(LineSegment2F::new(from, to))
} }
/// Creates a new radial gradient from a line connecting the centers of two circles with the
/// given radii, or a point at the center of one circle.
///
/// To create a radial gradient with a single circle (the common case), pass a `Vector2F`
/// representing the center of the circle for `line`; otherwise, to create a radial gradient
/// with two circles, pass a `LineSegment2F`. To start the gradient at the center of the
/// circle, pass zero for the first radius.
#[inline] #[inline]
pub fn radial<L>(line: L, radii: F32x2) -> Gradient where L: RadialGradientLine { pub fn radial<L>(line: L, radii: F32x2) -> Gradient where L: RadialGradientLine {
let transform = Transform2F::default(); let transform = Transform2F::default();
@ -121,6 +154,7 @@ impl Gradient {
} }
} }
/// Adds a new color stop to the radial gradient.
#[inline] #[inline]
pub fn add(&mut self, stop: ColorStop) { pub fn add(&mut self, stop: ColorStop) {
let index = self.stops.binary_search_by(|other| { let index = self.stops.binary_search_by(|other| {
@ -129,22 +163,28 @@ impl Gradient {
self.stops.insert(index, stop); self.stops.insert(index, stop);
} }
/// A convenience method to add a color stop. /// A convenience method equivalent to
/// `gradient.add_color_stop(ColorStop::new(color, offset))`.
#[inline] #[inline]
pub fn add_color_stop(&mut self, color: ColorU, offset: f32) { pub fn add_color_stop(&mut self, color: ColorU, offset: f32) {
self.add(ColorStop::new(color, offset)) self.add(ColorStop::new(color, offset))
} }
/// Returns the list of color stops in this gradient.
#[inline] #[inline]
pub fn stops(&self) -> &[ColorStop] { pub fn stops(&self) -> &[ColorStop] {
&self.stops &self.stops
} }
/// Returns a mutable version of the list of color stops in this gradient.
#[inline] #[inline]
pub fn stops_mut(&mut self) -> &mut [ColorStop] { pub fn stops_mut(&mut self) -> &mut [ColorStop] {
&mut self.stops &mut self.stops
} }
/// Returns the value of the gradient at offset `t`, which will be clamped between 0.0 and 1.0.
///
/// FIXME(pcwalton): This should probably take `wrap` into account…
pub fn sample(&self, mut t: f32) -> ColorU { pub fn sample(&self, mut t: f32) -> ColorU {
if self.stops.is_empty() { if self.stops.is_empty() {
return ColorU::transparent_black(); return ColorU::transparent_black();
@ -170,16 +210,23 @@ impl Gradient {
lower_stop.color.to_f32().lerp(upper_stop.color.to_f32(), ratio).to_u8() lower_stop.color.to_f32().lerp(upper_stop.color.to_f32(), ratio).to_u8()
} }
/// Returns true if all colors of all stops in this gradient are opaque (alpha is 1.0).
#[inline] #[inline]
pub fn is_opaque(&self) -> bool { pub fn is_opaque(&self) -> bool {
self.stops.iter().all(|stop| stop.color.is_opaque()) self.stops.iter().all(|stop| stop.color.is_opaque())
} }
/// Returns true if all colors of all stops in this gradient are fully transparent (alpha is
/// 0.0).
#[inline] #[inline]
pub fn is_fully_transparent(&self) -> bool { pub fn is_fully_transparent(&self) -> bool {
self.stops.iter().all(|stop| stop.color.is_fully_transparent()) self.stops.iter().all(|stop| stop.color.is_fully_transparent())
} }
/// Applies the given affine transform to this gradient.
///
/// FIXME(pcwalton): This isn't correct for radial gradients, as transforms can transform the
/// circles into ellipses…
pub fn apply_transform(&mut self, new_transform: Transform2F) { pub fn apply_transform(&mut self, new_transform: Transform2F) {
if new_transform.is_identity() { if new_transform.is_identity() {
return; return;
@ -195,13 +242,16 @@ impl Gradient {
} }
impl ColorStop { impl ColorStop {
/// Creates a new color stop from a color and offset between 0.0 and 1.0 inclusive.
#[inline] #[inline]
pub fn new(color: ColorU, offset: f32) -> ColorStop { pub fn new(color: ColorU, offset: f32) -> ColorStop {
ColorStop { color, offset } ColorStop { color, offset }
} }
} }
/// Allows `Gradient::radial` to be called with either a `LineSegment2F` or a `Vector2F`.
pub trait RadialGradientLine { pub trait RadialGradientLine {
/// Represents this value as a line.
fn to_line(self) -> LineSegment2F; fn to_line(self) -> LineSegment2F;
} }

View File

@ -10,6 +10,8 @@
//! Components of a vector scene, and various path utilities. //! Components of a vector scene, and various path utilities.
#![warn(missing_docs)]
#[macro_use] #[macro_use]
extern crate bitflags; extern crate bitflags;
#[macro_use] #[macro_use]

View File

@ -8,15 +8,22 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
//! Determining whether an outline is wound clockwise or counterclockwise.
use crate::outline::Outline; use crate::outline::Outline;
/// Winding order: counterclockwise or clockwise, with Y down.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum Orientation { pub enum Orientation {
/// Counterclockwise, with Y down.
Ccw = -1, Ccw = -1,
/// Clockwise, with Y down.
Cw = 1, Cw = 1,
} }
impl Orientation { impl Orientation {
/// Determines whether the outermost winding of an outline is counterclockwise or clockwise.
///
/// This follows the FreeType algorithm. /// This follows the FreeType algorithm.
pub fn from_outline(outline: &Outline) -> Orientation { pub fn from_outline(outline: &Outline) -> Orientation {
let mut area = 0.0; let mut area = 0.0;

View File

@ -36,9 +36,15 @@ pub struct Pattern {
/// Where a raster image pattern comes from. /// Where a raster image pattern comes from.
#[derive(Clone, PartialEq, Eq, Hash, Debug)] #[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum PatternSource { pub enum PatternSource {
/// A image whose pixels are stored in CPU memory.
Image(Image), Image(Image),
/// Previously-rendered vector content.
///
/// This value allows you to render content and then later use that content as a pattern.
RenderTarget { RenderTarget {
/// The ID of the render target, including the ID of the scene it came from.
id: RenderTargetId, id: RenderTargetId,
/// The device pixel size of the render target.
size: Vector2I, size: Vector2I,
} }
} }

View File

@ -8,10 +8,13 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
//! Render targets. //! Raster images that vector graphics can be rendered to and later used as a pattern.
/// Identifies a drawing surface for vector graphics that can be later used as a pattern.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct RenderTargetId { pub struct RenderTargetId {
/// The ID of the scene that this render target ID belongs to.
pub scene: u32, pub scene: u32,
/// The ID of the render target within this scene.
pub render_target: u32, pub render_target: u32,
} }

View File

@ -1,6 +1,6 @@
// pathfinder/content/src/segment.rs // pathfinder/content/src/segment.rs
// //
// Copyright © 2019 The Pathfinder Project Developers. // Copyright © 2020 The Pathfinder Project Developers.
// //
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
@ -58,6 +58,7 @@ impl Segment {
} }
} }
/// Returns a segment representing a quadratic Bézier curve.
#[inline] #[inline]
pub fn quadratic(baseline: LineSegment2F, ctrl: Vector2F) -> Segment { pub fn quadratic(baseline: LineSegment2F, ctrl: Vector2F) -> Segment {
Segment { Segment {
@ -68,6 +69,7 @@ impl Segment {
} }
} }
/// Returns a segment representing a cubic Bézier curve.
#[inline] #[inline]
pub fn cubic(baseline: LineSegment2F, ctrl: LineSegment2F) -> Segment { pub fn cubic(baseline: LineSegment2F, ctrl: LineSegment2F) -> Segment {
Segment { Segment {
@ -108,6 +110,8 @@ impl Segment {
return Segment::cubic(LineSegment2F(p3p0), LineSegment2F(p2p1)); return Segment::cubic(LineSegment2F(p3p0), LineSegment2F(p2p1));
} }
/// Returns a cubic Bézier segment that approximates a quarter of an arc, centered on the +x
/// axis.
#[inline] #[inline]
pub fn quarter_circle_arc() -> Segment { pub fn quarter_circle_arc() -> Segment {
let p0 = Vector2F::splat(SQRT_2 * 0.5); let p0 = Vector2F::splat(SQRT_2 * 0.5);
@ -117,40 +121,52 @@ impl Segment {
Segment::cubic(LineSegment2F::new(p3, p0), LineSegment2F::new(p2, p1)) Segment::cubic(LineSegment2F::new(p3, p0), LineSegment2F::new(p2, p1))
} }
/// If this segment is a line, returns it. In debug builds, panics otherwise.
#[inline] #[inline]
pub fn as_line_segment(&self) -> LineSegment2F { pub fn as_line_segment(&self) -> LineSegment2F {
debug_assert!(self.is_line()); debug_assert!(self.is_line());
self.baseline self.baseline
} }
/// Returns true if this segment is invalid.
#[inline] #[inline]
pub fn is_none(&self) -> bool { pub fn is_none(&self) -> bool {
self.kind == SegmentKind::None self.kind == SegmentKind::None
} }
/// Returns true if this segment represents a straight line.
#[inline] #[inline]
pub fn is_line(&self) -> bool { pub fn is_line(&self) -> bool {
self.kind == SegmentKind::Line self.kind == SegmentKind::Line
} }
/// Returns true if this segment represents a quadratic Bézier curve.
#[inline] #[inline]
pub fn is_quadratic(&self) -> bool { pub fn is_quadratic(&self) -> bool {
self.kind == SegmentKind::Quadratic self.kind == SegmentKind::Quadratic
} }
/// Returns true if this segment represents a cubic Bézier curve.
#[inline] #[inline]
pub fn is_cubic(&self) -> bool { pub fn is_cubic(&self) -> bool {
self.kind == SegmentKind::Cubic self.kind == SegmentKind::Cubic
} }
/// If this segment is a cubic Bézier curve, returns it. In debug builds, panics otherwise.
#[inline] #[inline]
pub fn as_cubic_segment(&self) -> CubicSegment { pub fn as_cubic_segment(&self) -> CubicSegment {
debug_assert!(self.is_cubic()); debug_assert!(self.is_cubic());
CubicSegment(self) CubicSegment(self)
} }
/// If this segment is a quadratic Bézier curve, elevates it to a cubic Bézier curve and
/// returns it. If this segment is a cubic Bézier curve, this method simply returns it.
///
/// If this segment is neither a quadratic Bézier curve nor a cubic Bézier curve, this method
/// returns an unspecified result.
///
/// FIXME(pcwalton): Handle lines!
// FIXME(pcwalton): We should basically never use this function. // FIXME(pcwalton): We should basically never use this function.
// FIXME(pcwalton): Handle lines!
#[inline] #[inline]
pub fn to_cubic(&self) -> Segment { pub fn to_cubic(&self) -> Segment {
if self.is_cubic() { if self.is_cubic() {
@ -165,6 +181,7 @@ impl Segment {
new_segment new_segment
} }
/// Returns this segment with endpoints and control points reversed.
#[inline] #[inline]
pub fn reversed(&self) -> Segment { pub fn reversed(&self) -> Segment {
Segment { Segment {
@ -179,23 +196,17 @@ impl Segment {
} }
} }
// Reverses if necessary so that the from point is above the to point. Calling this method /// Returns true if this segment is smaller than an implementation-defined epsilon value.
// again will undo the transformation.
#[inline]
pub fn orient(&self, y_winding: i32) -> Segment {
if y_winding >= 0 {
*self
} else {
self.reversed()
}
}
#[inline] #[inline]
pub fn is_tiny(&self) -> bool { pub fn is_tiny(&self) -> bool {
const EPSILON: f32 = 0.0001; const EPSILON: f32 = 0.0001;
self.baseline.square_length() < EPSILON self.baseline.square_length() < EPSILON
} }
/// Divides this segment into two at the given parametric t value, which must range from 0.0 to
/// 1.0.
///
/// This uses de Casteljau subdivision.
#[inline] #[inline]
pub fn split(&self, t: f32) -> (Segment, Segment) { pub fn split(&self, t: f32) -> (Segment, Segment) {
// FIXME(pcwalton): Don't degree elevate! // FIXME(pcwalton): Don't degree elevate!
@ -207,6 +218,10 @@ impl Segment {
} }
} }
/// Returns the position of the point on this line or curve with the given parametric t value,
/// which must range from 0.0 to 1.0.
///
/// If called on an invalid segment (`None` type), the result is unspecified.
#[inline] #[inline]
pub fn sample(self, t: f32) -> Vector2F { pub fn sample(self, t: f32) -> Vector2F {
// FIXME(pcwalton): Don't degree elevate! // FIXME(pcwalton): Don't degree elevate!
@ -217,6 +232,7 @@ impl Segment {
} }
} }
/// Applies the given affine transform to this segment and returns it.
#[inline] #[inline]
pub fn transform(self, transform: &Transform2F) -> Segment { pub fn transform(self, transform: &Transform2F) -> Segment {
Segment { Segment {
@ -227,18 +243,18 @@ impl Segment {
} }
} }
pub fn arc_length(&self) -> f32 { pub(crate) fn arc_length(&self) -> f32 {
// FIXME(pcwalton) // FIXME(pcwalton)
self.baseline.vector().length() self.baseline.vector().length()
} }
pub fn time_for_distance(&self, distance: f32) -> f32 { pub(crate) fn time_for_distance(&self, distance: f32) -> f32 {
// FIXME(pcwalton) // FIXME(pcwalton)
distance / self.arc_length() distance / self.arc_length()
} }
} }
/// The type of line segment this is. /// The type of segment this is.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
#[repr(u8)] #[repr(u8)]
pub enum SegmentKind { pub enum SegmentKind {
@ -253,17 +269,25 @@ pub enum SegmentKind {
} }
bitflags! { bitflags! {
/// Various flags that specify the relation of this segment to other segments in a contour.
pub struct SegmentFlags: u8 { pub struct SegmentFlags: u8 {
/// This segment is the first one in the contour.
const FIRST_IN_SUBPATH = 0x01; const FIRST_IN_SUBPATH = 0x01;
/// This segment is the closing segment of the contour (i.e. it returns back to the
/// starting point).
const CLOSES_SUBPATH = 0x02; const CLOSES_SUBPATH = 0x02;
} }
} }
/// A wrapper for a `Segment` that contains method specific to cubic Bézier curves.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct CubicSegment<'s>(pub &'s Segment); pub struct CubicSegment<'s>(pub &'s Segment);
impl<'s> CubicSegment<'s> { impl<'s> CubicSegment<'s> {
// See Kaspar Fischer, "Piecewise Linear Approximation of Bézier Curves", 2000. /// Returns true if the maximum deviation of this curve from the straight line connecting its
/// endpoints is less than `tolerance`.
///
/// See Kaspar Fischer, "Piecewise Linear Approximation of Bézier Curves", 2000.
#[inline] #[inline]
pub fn is_flat(self, tolerance: f32) -> bool { pub fn is_flat(self, tolerance: f32) -> bool {
let mut uv = F32x4::splat(3.0) * self.0.ctrl.0 let mut uv = F32x4::splat(3.0) * self.0.ctrl.0
@ -275,6 +299,10 @@ impl<'s> CubicSegment<'s> {
uv[0] + uv[1] <= 16.0 * tolerance * tolerance uv[0] + uv[1] <= 16.0 * tolerance * tolerance
} }
/// Splits this cubic Bézier curve into two at the given parametric t value, which will be
/// clamped to the range 0.0 to 1.0.
///
/// This uses de Casteljau subdivision.
#[inline] #[inline]
pub fn split(self, t: f32) -> (Segment, Segment) { pub fn split(self, t: f32) -> (Segment, Segment) {
let (baseline0, ctrl0, baseline1, ctrl1); let (baseline0, ctrl0, baseline1, ctrl1);
@ -330,34 +358,43 @@ impl<'s> CubicSegment<'s> {
) )
} }
/// A convenience method equivalent to `segment.split(t).0`.
#[inline] #[inline]
pub fn split_before(self, t: f32) -> Segment { pub fn split_before(self, t: f32) -> Segment {
self.split(t).0 self.split(t).0
} }
/// A convenience method equivalent to `segment.split(t).1`.
#[inline] #[inline]
pub fn split_after(self, t: f32) -> Segment { pub fn split_after(self, t: f32) -> Segment {
self.split(t).1 self.split(t).1
} }
// FIXME(pcwalton): Use Horner's method! /// Returns the position of the point on this curve at parametric time `t`, which will be
/// clamped between 0.0 and 1.0.
///
/// FIXME(pcwalton): Use Horner's method!
#[inline] #[inline]
pub fn sample(self, t: f32) -> Vector2F { pub fn sample(self, t: f32) -> Vector2F {
self.split(t).0.baseline.to() self.split(t).0.baseline.to()
} }
/// Returns the left extent of this curve's axis-aligned bounding box.
#[inline] #[inline]
pub fn min_x(&self) -> f32 { pub fn min_x(&self) -> f32 {
f32::min(self.0.baseline.min_x(), self.0.ctrl.min_x()) f32::min(self.0.baseline.min_x(), self.0.ctrl.min_x())
} }
/// Returns the top extent of this curve's axis-aligned bounding box.
#[inline] #[inline]
pub fn min_y(&self) -> f32 { pub fn min_y(&self) -> f32 {
f32::min(self.0.baseline.min_y(), self.0.ctrl.min_y()) f32::min(self.0.baseline.min_y(), self.0.ctrl.min_y())
} }
/// Returns the right extent of this curve's axis-aligned bounding box.
#[inline] #[inline]
pub fn max_x(&self) -> f32 { pub fn max_x(&self) -> f32 {
f32::max(self.0.baseline.max_x(), self.0.ctrl.max_x()) f32::max(self.0.baseline.max_x(), self.0.ctrl.max_x())
} }
/// Returns the bottom extent of this curve's axis-aligned bounding box.
#[inline] #[inline]
pub fn max_y(&self) -> f32 { pub fn max_y(&self) -> f32 {
f32::max(self.0.baseline.max_y(), self.0.ctrl.max_y()) f32::max(self.0.baseline.max_y(), self.0.ctrl.max_y())

View File

@ -21,39 +21,69 @@ use std::f32;
const TOLERANCE: f32 = 0.01; const TOLERANCE: f32 = 0.01;
/// Strokes an outline with a stroke style to produce a new outline.
///
/// An example of use:
///
/// ```norun
/// let mut stroke_to_fill = OutlineStrokeToFill::new(&input_outline, StrokeStyle::default());
/// stroke_to_fill.offset();
/// let output_outline = stroke_to_fill.into_outline();
/// ```
pub struct OutlineStrokeToFill<'a> { pub struct OutlineStrokeToFill<'a> {
input: &'a Outline, input: &'a Outline,
output: Outline, output: Outline,
style: StrokeStyle, style: StrokeStyle,
} }
/// How an outline should be stroked.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct StrokeStyle { pub struct StrokeStyle {
/// The width of the stroke in scene units.
pub line_width: f32, pub line_width: f32,
/// The shape of the ends of the stroke.
pub line_cap: LineCap, pub line_cap: LineCap,
/// The shape used to join two line segments where they meet.
pub line_join: LineJoin, pub line_join: LineJoin,
} }
/// The shape of the ends of the stroke.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum LineCap { pub enum LineCap {
/// The ends of lines are squared off at the endpoints.
Butt, Butt,
/// The ends of lines are squared off by adding a box with an equal width and half the height
/// of the line's thickness.
Square, Square,
/// The ends of lines are rounded.
Round, Round,
} }
/// The shape used to join two line segments where they meet.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum LineJoin { pub enum LineJoin {
/// Connected segments are joined by extending their outside edges to connect at a single
/// point, with the effect of filling an additional lozenge-shaped area. The `f32` value
/// specifies the miter limit ratio.
Miter(f32), Miter(f32),
/// Fills an additional triangular area between the common endpoint of connected segments and
/// the separate outside rectangular corners of each segment.
Bevel, Bevel,
/// Rounds off the corners of a shape by filling an additional sector of disc centered at the
/// common endpoint of connected segments. The radius for these rounded corners is equal to the
/// line width.
Round, Round,
} }
impl<'a> OutlineStrokeToFill<'a> { impl<'a> OutlineStrokeToFill<'a> {
/// Creates a new `OutlineStrokeToFill` object that will stroke the given outline with the
/// given stroke style.
#[inline] #[inline]
pub fn new(input: &Outline, style: StrokeStyle) -> OutlineStrokeToFill { pub fn new(input: &Outline, style: StrokeStyle) -> OutlineStrokeToFill {
OutlineStrokeToFill { input, output: Outline::new(), style } OutlineStrokeToFill { input, output: Outline::new(), style }
} }
/// Performs the stroke operation.
pub fn offset(&mut self) { pub fn offset(&mut self) {
let mut new_contours = vec![]; let mut new_contours = vec![];
for input in &self.input.contours { for input in &self.input.contours {
@ -89,6 +119,7 @@ impl<'a> OutlineStrokeToFill<'a> {
self.output.bounds = new_bounds.unwrap_or_else(|| RectF::default()); self.output.bounds = new_bounds.unwrap_or_else(|| RectF::default());
} }
/// Returns the resulting stroked outline. This should be called after `offset()`.
#[inline] #[inline]
pub fn into_outline(self) -> Outline { pub fn into_outline(self) -> Outline {
self.output self.output

View File

@ -10,6 +10,8 @@
//! Utilities for transforming paths. //! Utilities for transforming paths.
#![allow(deprecated)]
use crate::segment::Segment; use crate::segment::Segment;
use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::transform3d::Perspective; use pathfinder_geometry::transform3d::Perspective;
@ -51,6 +53,7 @@ impl<I> Transform2FPathIter<I>
where where
I: Iterator<Item = Segment>, I: Iterator<Item = Segment>,
{ {
/// Creates a new `Transform2FPathIter` ready to transform the given path.
#[inline] #[inline]
pub fn new(iter: I, transform: &Transform2F) -> Transform2FPathIter<I> { pub fn new(iter: I, transform: &Transform2F) -> Transform2FPathIter<I> {
Transform2FPathIter { Transform2FPathIter {
@ -61,6 +64,7 @@ where
} }
/// Transforms a path with a perspective projection. /// Transforms a path with a perspective projection.
#[deprecated]
pub struct PerspectivePathIter<I> pub struct PerspectivePathIter<I>
where where
I: Iterator<Item = Segment>, I: Iterator<Item = Segment>,
@ -96,7 +100,9 @@ impl<I> PerspectivePathIter<I>
where where
I: Iterator<Item = Segment>, I: Iterator<Item = Segment>,
{ {
/// Creates a new `PerspectivePathIter` ready to apply perspective to the given path.
#[inline] #[inline]
#[deprecated]
pub fn new(iter: I, perspective: &Perspective) -> PerspectivePathIter<I> { pub fn new(iter: I, perspective: &Perspective) -> PerspectivePathIter<I> {
PerspectivePathIter { PerspectivePathIter {
iter, iter,