From 4060baa80c0686a5ad9b220f4fe5f079628e8706 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 29 Jul 2020 12:17:03 -0700 Subject: [PATCH] Document the rest of `pathfinder_content` --- content/src/effects.rs | 46 +++++++++++++++++++++++ content/src/gradient.rs | 58 ++++++++++++++++++++++++++-- content/src/lib.rs | 2 + content/src/orientation.rs | 7 ++++ content/src/pattern.rs | 6 +++ content/src/render_target.rs | 5 ++- content/src/segment.rs | 73 +++++++++++++++++++++++++++--------- content/src/stroke.rs | 31 +++++++++++++++ content/src/transform.rs | 6 +++ 9 files changed, 211 insertions(+), 23 deletions(-) diff --git a/content/src/effects.rs b/content/src/effects.rs index 932b4a98..c2933f85 100644 --- a/content/src/effects.rs +++ b/content/src/effects.rs @@ -55,6 +55,7 @@ pub enum Filter { uv_origin: Vector2F, }, + /// One of the `PatternFilter` filters. PatternFilter(PatternFilter), } @@ -80,7 +81,9 @@ pub enum PatternFilter { /// To produce a full Gaussian blur, perform two successive blur operations, one in each /// direction. Blur { + /// The axis of the blur: horizontal or vertical. direction: BlurDirection, + /// Half the blur radius. sigma: f32, }, @@ -95,43 +98,86 @@ pub enum PatternFilter { #[derive(Clone, Copy, PartialEq, Debug)] pub enum BlendMode { // Porter-Duff, supported by GPU blender + /// No regions are enabled. Clear, + /// Only the source will be present. Copy, + /// The source that overlaps the destination, replaces the destination. SrcIn, + /// Source is placed, where it falls outside of the destination. SrcOut, + /// Source is placed over the destination. SrcOver, + /// Source which overlaps the destination, replaces the destination. Destination is placed + /// elsewhere. SrcAtop, + /// Destination which overlaps the source, replaces the source. DestIn, + /// Destination is placed, where it falls outside of the source. DestOut, + /// Destination is placed over the source. DestOver, + /// Destination which overlaps the source replaces the source. Source is placed elsewhere. DestAtop, + /// The non-overlapping regions of source and destination are combined. Xor, + /// Display the sum of the source image and destination image. It is defined in the Porter-Duff + /// paper as the plus operator. Lighter, // Others, unsupported by GPU blender + /// Selects the darker of the backdrop and source colors. Darken, + /// Selects the lighter of the backdrop and source colors. Lighten, + /// The source color is multiplied by the destination color and replaces the destination. Multiply, + /// Multiplies the complements of the backdrop and source color values, then complements the + /// result. 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, + /// Multiplies or screens the colors, depending on the backdrop color value. Overlay, + /// Brightens the backdrop color to reflect the source color. ColorDodge, + /// Darkens the backdrop color to reflect the source color. 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, + /// Subtracts the darker of the two constituent colors from the lighter color. Difference, + /// Produces an effect similar to that of the Difference mode but lower in contrast. Exclusion, + /// Creates a color with the hue of the source color and the saturation and luminosity of the + /// backdrop color. Hue, + /// Creates a color with the saturation of the source color and the hue and luminosity of the + /// backdrop color. Saturation, + /// Creates a color with the hue and saturation of the source color and the luminosity of the + /// backdrop 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, } +/// 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)] pub struct DefringingKernel(pub [f32; 4]); +/// The axis a Gaussian blur is applied to. #[derive(Clone, Copy, PartialEq, Debug)] pub enum BlurDirection { + /// The horizontal axis. X, + /// The vertical axis. Y, } diff --git a/content/src/gradient.rs b/content/src/gradient.rs index 828f7b86..bab52c5d 100644 --- a/content/src/gradient.rs +++ b/content/src/gradient.rs @@ -22,27 +22,45 @@ use std::convert; use std::hash::{Hash, Hasher}; use std::mem; +/// A gradient, either linear or radial. #[derive(Clone, PartialEq, Debug)] pub struct Gradient { + /// Information specific to the type of gradient (linear or radial). pub geometry: GradientGeometry, stops: Vec, + /// What should be rendered upon reaching the end of the color stops. 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)] 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, + /// The color of the gradient stop. pub color: ColorU, } +/// The type of gradient: linear or radial. #[derive(Clone, PartialEq, Debug)] 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), + /// A radial gradient that radiates outward from a line connecting two circles (or from one + /// circle). Radial { - /// The line that connects the two circles. It may have zero length for simple radial - /// gradients. + /// The line that connects the centers of the two circles. For single-circle radial + /// 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, - /// 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, /// 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)] 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, + /// The gradient repeats indefinitely. Repeat, } @@ -97,6 +119,9 @@ impl Hash for ColorStop { } 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] pub fn linear(line: LineSegment2F) -> Gradient { Gradient { @@ -106,11 +131,19 @@ impl Gradient { } } + /// A convenience method equivalent to `Gradient::linear(LineSegment2F::new(from, to))`. #[inline] pub fn linear_from_points(from: Vector2F, to: Vector2F) -> Gradient { 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] pub fn radial(line: L, radii: F32x2) -> Gradient where L: RadialGradientLine { let transform = Transform2F::default(); @@ -121,6 +154,7 @@ impl Gradient { } } + /// Adds a new color stop to the radial gradient. #[inline] pub fn add(&mut self, stop: ColorStop) { let index = self.stops.binary_search_by(|other| { @@ -129,22 +163,28 @@ impl Gradient { 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] pub fn add_color_stop(&mut self, color: ColorU, offset: f32) { self.add(ColorStop::new(color, offset)) } + /// Returns the list of color stops in this gradient. #[inline] pub fn stops(&self) -> &[ColorStop] { &self.stops } + /// Returns a mutable version of the list of color stops in this gradient. #[inline] pub fn stops_mut(&mut self) -> &mut [ColorStop] { &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 { if self.stops.is_empty() { return ColorU::transparent_black(); @@ -170,16 +210,23 @@ impl Gradient { 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] pub fn is_opaque(&self) -> bool { 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] pub fn is_fully_transparent(&self) -> bool { 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) { if new_transform.is_identity() { return; @@ -195,13 +242,16 @@ impl Gradient { } impl ColorStop { + /// Creates a new color stop from a color and offset between 0.0 and 1.0 inclusive. #[inline] pub fn new(color: ColorU, offset: f32) -> ColorStop { ColorStop { color, offset } } } +/// Allows `Gradient::radial` to be called with either a `LineSegment2F` or a `Vector2F`. pub trait RadialGradientLine { + /// Represents this value as a line. fn to_line(self) -> LineSegment2F; } diff --git a/content/src/lib.rs b/content/src/lib.rs index 19ae5680..1302e2a8 100644 --- a/content/src/lib.rs +++ b/content/src/lib.rs @@ -10,6 +10,8 @@ //! Components of a vector scene, and various path utilities. +#![warn(missing_docs)] + #[macro_use] extern crate bitflags; #[macro_use] diff --git a/content/src/orientation.rs b/content/src/orientation.rs index 9e815ebb..1ef190d0 100644 --- a/content/src/orientation.rs +++ b/content/src/orientation.rs @@ -8,15 +8,22 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! Determining whether an outline is wound clockwise or counterclockwise. + use crate::outline::Outline; +/// Winding order: counterclockwise or clockwise, with Y down. #[derive(Clone, Copy, Debug, PartialEq)] pub enum Orientation { + /// Counterclockwise, with Y down. Ccw = -1, + /// Clockwise, with Y down. Cw = 1, } impl Orientation { + /// Determines whether the outermost winding of an outline is counterclockwise or clockwise. + /// /// This follows the FreeType algorithm. pub fn from_outline(outline: &Outline) -> Orientation { let mut area = 0.0; diff --git a/content/src/pattern.rs b/content/src/pattern.rs index 783a46fa..c69ade84 100644 --- a/content/src/pattern.rs +++ b/content/src/pattern.rs @@ -36,9 +36,15 @@ pub struct Pattern { /// Where a raster image pattern comes from. #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub enum PatternSource { + /// A image whose pixels are stored in CPU memory. Image(Image), + /// Previously-rendered vector content. + /// + /// This value allows you to render content and then later use that content as a pattern. RenderTarget { + /// The ID of the render target, including the ID of the scene it came from. id: RenderTargetId, + /// The device pixel size of the render target. size: Vector2I, } } diff --git a/content/src/render_target.rs b/content/src/render_target.rs index ffd1b88f..6bdd9f1c 100644 --- a/content/src/render_target.rs +++ b/content/src/render_target.rs @@ -8,10 +8,13 @@ // option. This file may not be copied, modified, or distributed // 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)] pub struct RenderTargetId { + /// The ID of the scene that this render target ID belongs to. pub scene: u32, + /// The ID of the render target within this scene. pub render_target: u32, } diff --git a/content/src/segment.rs b/content/src/segment.rs index a5023a05..ed2cbd9f 100644 --- a/content/src/segment.rs +++ b/content/src/segment.rs @@ -1,6 +1,6 @@ // 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 or the MIT license @@ -58,6 +58,7 @@ impl Segment { } } + /// Returns a segment representing a quadratic Bézier curve. #[inline] pub fn quadratic(baseline: LineSegment2F, ctrl: Vector2F) -> Segment { Segment { @@ -68,6 +69,7 @@ impl Segment { } } + /// Returns a segment representing a cubic Bézier curve. #[inline] pub fn cubic(baseline: LineSegment2F, ctrl: LineSegment2F) -> Segment { Segment { @@ -108,6 +110,8 @@ impl Segment { 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] pub fn quarter_circle_arc() -> Segment { let p0 = Vector2F::splat(SQRT_2 * 0.5); @@ -117,40 +121,52 @@ impl Segment { Segment::cubic(LineSegment2F::new(p3, p0), LineSegment2F::new(p2, p1)) } + /// If this segment is a line, returns it. In debug builds, panics otherwise. #[inline] pub fn as_line_segment(&self) -> LineSegment2F { debug_assert!(self.is_line()); self.baseline } + /// Returns true if this segment is invalid. #[inline] pub fn is_none(&self) -> bool { self.kind == SegmentKind::None } + /// Returns true if this segment represents a straight line. #[inline] pub fn is_line(&self) -> bool { self.kind == SegmentKind::Line } + /// Returns true if this segment represents a quadratic Bézier curve. #[inline] pub fn is_quadratic(&self) -> bool { self.kind == SegmentKind::Quadratic } + /// Returns true if this segment represents a cubic Bézier curve. #[inline] pub fn is_cubic(&self) -> bool { self.kind == SegmentKind::Cubic } + /// If this segment is a cubic Bézier curve, returns it. In debug builds, panics otherwise. #[inline] pub fn as_cubic_segment(&self) -> CubicSegment { debug_assert!(self.is_cubic()); 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): Handle lines! #[inline] pub fn to_cubic(&self) -> Segment { if self.is_cubic() { @@ -165,6 +181,7 @@ impl Segment { new_segment } + /// Returns this segment with endpoints and control points reversed. #[inline] pub fn reversed(&self) -> Segment { Segment { @@ -179,23 +196,17 @@ impl Segment { } } - // Reverses if necessary so that the from point is above the to point. Calling this method - // again will undo the transformation. - #[inline] - pub fn orient(&self, y_winding: i32) -> Segment { - if y_winding >= 0 { - *self - } else { - self.reversed() - } - } - + /// Returns true if this segment is smaller than an implementation-defined epsilon value. #[inline] pub fn is_tiny(&self) -> bool { const EPSILON: f32 = 0.0001; 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] pub fn split(&self, t: f32) -> (Segment, Segment) { // 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] pub fn sample(self, t: f32) -> Vector2F { // FIXME(pcwalton): Don't degree elevate! @@ -217,6 +232,7 @@ impl Segment { } } + /// Applies the given affine transform to this segment and returns it. #[inline] pub fn transform(self, transform: &Transform2F) -> Segment { Segment { @@ -227,18 +243,18 @@ impl Segment { } } - pub fn arc_length(&self) -> f32 { + pub(crate) fn arc_length(&self) -> f32 { // FIXME(pcwalton) 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) distance / self.arc_length() } } -/// The type of line segment this is. +/// The type of segment this is. #[derive(Clone, Copy, Debug, PartialEq)] #[repr(u8)] pub enum SegmentKind { @@ -253,17 +269,25 @@ pub enum SegmentKind { } bitflags! { + /// Various flags that specify the relation of this segment to other segments in a contour. pub struct SegmentFlags: u8 { + /// This segment is the first one in the contour. 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; } } +/// A wrapper for a `Segment` that contains method specific to cubic Bézier curves. #[derive(Clone, Copy, Debug)] pub struct CubicSegment<'s>(pub &'s Segment); 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] pub fn is_flat(self, tolerance: f32) -> bool { 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 } + /// 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] pub fn split(self, t: f32) -> (Segment, Segment) { let (baseline0, ctrl0, baseline1, ctrl1); @@ -330,34 +358,43 @@ impl<'s> CubicSegment<'s> { ) } + /// A convenience method equivalent to `segment.split(t).0`. #[inline] pub fn split_before(self, t: f32) -> Segment { self.split(t).0 } + /// A convenience method equivalent to `segment.split(t).1`. #[inline] pub fn split_after(self, t: f32) -> Segment { 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] pub fn sample(self, t: f32) -> Vector2F { self.split(t).0.baseline.to() } + /// Returns the left extent of this curve's axis-aligned bounding box. #[inline] pub fn min_x(&self) -> f32 { 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] pub fn min_y(&self) -> f32 { 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] pub fn max_x(&self) -> f32 { 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] pub fn max_y(&self) -> f32 { f32::max(self.0.baseline.max_y(), self.0.ctrl.max_y()) diff --git a/content/src/stroke.rs b/content/src/stroke.rs index 385fbecf..e629d202 100644 --- a/content/src/stroke.rs +++ b/content/src/stroke.rs @@ -21,39 +21,69 @@ use std::f32; 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> { input: &'a Outline, output: Outline, style: StrokeStyle, } +/// How an outline should be stroked. #[derive(Clone, Copy, Debug, PartialEq)] pub struct StrokeStyle { + /// The width of the stroke in scene units. pub line_width: f32, + /// The shape of the ends of the stroke. pub line_cap: LineCap, + /// The shape used to join two line segments where they meet. pub line_join: LineJoin, } +/// The shape of the ends of the stroke. #[derive(Clone, Copy, Debug, PartialEq)] pub enum LineCap { + /// The ends of lines are squared off at the endpoints. 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, + /// The ends of lines are rounded. Round, } +/// The shape used to join two line segments where they meet. #[derive(Clone, Copy, Debug, PartialEq)] 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), + /// Fills an additional triangular area between the common endpoint of connected segments and + /// the separate outside rectangular corners of each segment. 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, } impl<'a> OutlineStrokeToFill<'a> { + /// Creates a new `OutlineStrokeToFill` object that will stroke the given outline with the + /// given stroke style. #[inline] pub fn new(input: &Outline, style: StrokeStyle) -> OutlineStrokeToFill { OutlineStrokeToFill { input, output: Outline::new(), style } } + /// Performs the stroke operation. pub fn offset(&mut self) { let mut new_contours = vec![]; for input in &self.input.contours { @@ -89,6 +119,7 @@ impl<'a> OutlineStrokeToFill<'a> { self.output.bounds = new_bounds.unwrap_or_else(|| RectF::default()); } + /// Returns the resulting stroked outline. This should be called after `offset()`. #[inline] pub fn into_outline(self) -> Outline { self.output diff --git a/content/src/transform.rs b/content/src/transform.rs index 4f2cde2b..8cdcbdf0 100644 --- a/content/src/transform.rs +++ b/content/src/transform.rs @@ -10,6 +10,8 @@ //! Utilities for transforming paths. +#![allow(deprecated)] + use crate::segment::Segment; use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform3d::Perspective; @@ -51,6 +53,7 @@ impl Transform2FPathIter where I: Iterator, { + /// Creates a new `Transform2FPathIter` ready to transform the given path. #[inline] pub fn new(iter: I, transform: &Transform2F) -> Transform2FPathIter { Transform2FPathIter { @@ -61,6 +64,7 @@ where } /// Transforms a path with a perspective projection. +#[deprecated] pub struct PerspectivePathIter where I: Iterator, @@ -96,7 +100,9 @@ impl PerspectivePathIter where I: Iterator, { + /// Creates a new `PerspectivePathIter` ready to apply perspective to the given path. #[inline] + #[deprecated] pub fn new(iter: I, perspective: &Perspective) -> PerspectivePathIter { PerspectivePathIter { iter,