Auto merge of #427 - pcwalton:content-docs, r=pcwalton

Document the rest of `pathfinder_content`
This commit is contained in:
bors-servo 2020-07-29 15:17:53 -04:00 committed by GitHub
commit 2b5c6682d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 211 additions and 23 deletions

View File

@ -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,
}

View File

@ -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<ColorStop>,
/// 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<L>(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;
}

View File

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

View File

@ -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;

View File

@ -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,
}
}

View File

@ -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,
}

View File

@ -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 <LICENSE-APACHE or
// 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]
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())

View File

@ -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

View File

@ -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<I> Transform2FPathIter<I>
where
I: Iterator<Item = Segment>,
{
/// Creates a new `Transform2FPathIter` ready to transform the given path.
#[inline]
pub fn new(iter: I, transform: &Transform2F) -> Transform2FPathIter<I> {
Transform2FPathIter {
@ -61,6 +64,7 @@ where
}
/// Transforms a path with a perspective projection.
#[deprecated]
pub struct PerspectivePathIter<I>
where
I: Iterator<Item = Segment>,
@ -96,7 +100,9 @@ impl<I> PerspectivePathIter<I>
where
I: Iterator<Item = Segment>,
{
/// Creates a new `PerspectivePathIter` ready to apply perspective to the given path.
#[inline]
#[deprecated]
pub fn new(iter: I, perspective: &Perspective) -> PerspectivePathIter<I> {
PerspectivePathIter {
iter,